xref: /openbsd-src/usr.bin/tmux/tmux.c (revision 4e7a7ade8c378113fb9de224a8f3c8aa1de4fdcd)
1 /* $OpenBSD: tmux.c,v 1.145 2015/10/23 16:07:29 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 <unistd.h>
33 
34 #include "tmux.h"
35 
36 #ifdef DEBUG
37 extern char	*malloc_options;
38 #endif
39 
40 struct options	 global_options;	/* server options */
41 struct options	 global_s_options;	/* session options */
42 struct options	 global_w_options;	/* window options */
43 struct environ	 global_environ;
44 
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 	static const char	*home;
176 
177 	if (home != NULL)
178 		return (home);
179 
180 	home = getenv("HOME");
181 	if (home == NULL || *home == '\0') {
182 		pw = getpwuid(getuid());
183 		if (pw != NULL)
184 			home = pw->pw_dir;
185 		else
186 			home = NULL;
187 	}
188 
189 	return (home);
190 }
191 
192 int
193 main(int argc, char **argv)
194 {
195 	char	*s, *path, *label, **var, tmp[PATH_MAX];
196 	int	 opt, flags, keys;
197 
198 #ifdef DEBUG
199 	malloc_options = (char *) "AFGJPX";
200 #endif
201 
202 	setlocale(LC_TIME, "");
203 	tzset();
204 
205 	if (**argv == '-')
206 		flags = CLIENT_LOGIN;
207 	else
208 		flags = 0;
209 
210 	label = path = NULL;
211 	while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUv")) != -1) {
212 		switch (opt) {
213 		case '2':
214 			flags |= CLIENT_256COLOURS;
215 			break;
216 		case 'c':
217 			free(shell_cmd);
218 			shell_cmd = xstrdup(optarg);
219 			break;
220 		case 'C':
221 			if (flags & CLIENT_CONTROL)
222 				flags |= CLIENT_CONTROLCONTROL;
223 			else
224 				flags |= CLIENT_CONTROL;
225 			break;
226 		case 'f':
227 			set_cfg_file(optarg);
228 			break;
229 		case 'l':
230 			flags |= CLIENT_LOGIN;
231 			break;
232 		case 'L':
233 			free(label);
234 			label = xstrdup(optarg);
235 			break;
236 		case 'q':
237 			break;
238 		case 'S':
239 			free(path);
240 			path = xstrdup(optarg);
241 			break;
242 		case 'u':
243 			flags |= CLIENT_UTF8;
244 			break;
245 		case 'v':
246 			debug_level++;
247 			break;
248 		default:
249 			usage();
250 		}
251 	}
252 	argc -= optind;
253 	argv += optind;
254 
255 	if (shell_cmd != NULL && argc != 0)
256 		usage();
257 
258 	if (pledge("stdio rpath wpath cpath flock fattr unix sendfd recvfd "
259 	    "proc exec tty ps", NULL) != 0)
260 		err(1, "pledge");
261 
262 	if (!(flags & CLIENT_UTF8)) {
263 		/*
264 		 * If the user has set whichever of LC_ALL, LC_CTYPE or LANG
265 		 * exist (in that order) to contain UTF-8, it is a safe
266 		 * assumption that either they are using a UTF-8 terminal, or
267 		 * if not they know that output from UTF-8-capable programs may
268 		 * be wrong.
269 		 */
270 		if ((s = getenv("LC_ALL")) == NULL || *s == '\0') {
271 			if ((s = getenv("LC_CTYPE")) == NULL || *s == '\0')
272 				s = getenv("LANG");
273 		}
274 		if (s != NULL && (strcasestr(s, "UTF-8") != NULL ||
275 		    strcasestr(s, "UTF8") != NULL))
276 			flags |= CLIENT_UTF8;
277 	}
278 
279 	environ_init(&global_environ);
280 	for (var = environ; *var != NULL; var++)
281 		environ_put(&global_environ, *var);
282 	if (getcwd(tmp, sizeof tmp) != NULL)
283 		environ_set(&global_environ, "PWD", tmp);
284 
285 	options_init(&global_options, NULL);
286 	options_table_populate_tree(server_options_table, &global_options);
287 
288 	options_init(&global_s_options, NULL);
289 	options_table_populate_tree(session_options_table, &global_s_options);
290 	options_set_string(&global_s_options, "default-shell", "%s",
291 	    getshell());
292 
293 	options_init(&global_w_options, NULL);
294 	options_table_populate_tree(window_options_table, &global_w_options);
295 
296 	/* Enable UTF-8 if the first client is on UTF-8 terminal. */
297 	if (flags & CLIENT_UTF8) {
298 		options_set_number(&global_s_options, "status-utf8", 1);
299 		options_set_number(&global_s_options, "mouse-utf8", 1);
300 		options_set_number(&global_w_options, "utf8", 1);
301 	}
302 
303 	/* Override keys to vi if VISUAL or EDITOR are set. */
304 	if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) {
305 		if (strrchr(s, '/') != NULL)
306 			s = strrchr(s, '/') + 1;
307 		if (strstr(s, "vi") != NULL)
308 			keys = MODEKEY_VI;
309 		else
310 			keys = MODEKEY_EMACS;
311 		options_set_number(&global_s_options, "status-keys", keys);
312 		options_set_number(&global_w_options, "mode-keys", keys);
313 	}
314 
315 	/*
316 	 * Figure out the socket path. If specified on the command-line with -S
317 	 * or -L, use it, otherwise try $TMUX or assume -L default.
318 	 */
319 	if (path == NULL) {
320 		/* If no -L, use the environment. */
321 		if (label == NULL) {
322 			s = getenv("TMUX");
323 			if (s != NULL) {
324 				path = xstrdup(s);
325 				path[strcspn (path, ",")] = '\0';
326 				if (*path == '\0') {
327 					free(path);
328 					label = xstrdup("default");
329 				}
330 			} else
331 				label = xstrdup("default");
332 		}
333 
334 		/* -L or default set. */
335 		if (label != NULL) {
336 			if ((path = makesocketpath(label)) == NULL) {
337 				fprintf(stderr, "can't create socket: %s\n",
338 				    strerror(errno));
339 				exit(1);
340 			}
341 		}
342 	}
343 	free(label);
344 
345 	if (strlcpy(socket_path, path, sizeof socket_path) >=
346 	    sizeof socket_path) {
347 		fprintf(stderr, "socket path too long: %s\n", path);
348 		exit(1);
349 	}
350 	free(path);
351 
352 	/* Set process title. */
353 	setproctitle("%s (%s)", __progname, socket_path);
354 
355 	/* Pass control to the client. */
356 	exit(client_main(event_init(), argc, argv, flags));
357 }
358