xref: /openbsd-src/usr.bin/tmux/tmux.c (revision 1d75677ebf71bb80421fa88338e24051ffbfd108)
1 /* $OpenBSD: tmux.c,v 1.143 2015/09/03 14:30:23 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		*shell_cmd;
45 int		 debug_level;
46 time_t		 start_time;
47 char		 socket_path[PATH_MAX];
48 
49 __dead void	 usage(void);
50 char 		*makesocketpath(const char *);
51 
52 __dead void
53 usage(void)
54 {
55 	fprintf(stderr,
56 	    "usage: %s [-2Cluv] [-c shell-command] [-f file] [-L socket-name]\n"
57 	    "            [-S socket-path] [command [flags]]\n",
58 	    __progname);
59 	exit(1);
60 }
61 
62 void
63 logfile(const char *name)
64 {
65 	char	*path;
66 
67 	if (debug_level > 0) {
68 		xasprintf(&path, "tmux-%s-%ld.log", name, (long) getpid());
69 		log_open(path);
70 		free(path);
71 	}
72 }
73 
74 const char *
75 getshell(void)
76 {
77 	struct passwd	*pw;
78 	const char	*shell;
79 
80 	shell = getenv("SHELL");
81 	if (checkshell(shell))
82 		return (shell);
83 
84 	pw = getpwuid(getuid());
85 	if (pw != NULL && checkshell(pw->pw_shell))
86 		return (pw->pw_shell);
87 
88 	return (_PATH_BSHELL);
89 }
90 
91 int
92 checkshell(const char *shell)
93 {
94 	if (shell == NULL || *shell == '\0' || *shell != '/')
95 		return (0);
96 	if (areshell(shell))
97 		return (0);
98 	if (access(shell, X_OK) != 0)
99 		return (0);
100 	return (1);
101 }
102 
103 int
104 areshell(const char *shell)
105 {
106 	const char	*progname, *ptr;
107 
108 	if ((ptr = strrchr(shell, '/')) != NULL)
109 		ptr++;
110 	else
111 		ptr = shell;
112 	progname = __progname;
113 	if (*progname == '-')
114 		progname++;
115 	if (strcmp(ptr, progname) == 0)
116 		return (1);
117 	return (0);
118 }
119 
120 char *
121 makesocketpath(const char *label)
122 {
123 	char		base[PATH_MAX], realbase[PATH_MAX], *path, *s;
124 	struct stat	sb;
125 	u_int		uid;
126 
127 	uid = getuid();
128 	if ((s = getenv("TMUX_TMPDIR")) != NULL && *s != '\0')
129 		xsnprintf(base, sizeof base, "%s/tmux-%u", s, uid);
130 	else if ((s = getenv("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 
203 	if (**argv == '-')
204 		flags = CLIENT_LOGIN;
205 	else
206 		flags = 0;
207 
208 	label = path = NULL;
209 	while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUv")) != -1) {
210 		switch (opt) {
211 		case '2':
212 			flags |= CLIENT_256COLOURS;
213 			break;
214 		case 'c':
215 			free(shell_cmd);
216 			shell_cmd = xstrdup(optarg);
217 			break;
218 		case 'C':
219 			if (flags & CLIENT_CONTROL)
220 				flags |= CLIENT_CONTROLCONTROL;
221 			else
222 				flags |= CLIENT_CONTROL;
223 			break;
224 		case 'f':
225 			set_cfg_file(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 	/*
310 	 * Figure out the socket path. If specified on the command-line with -S
311 	 * or -L, use it, otherwise try $TMUX or assume -L default.
312 	 */
313 	if (path == NULL) {
314 		/* If no -L, use the environment. */
315 		if (label == NULL) {
316 			s = getenv("TMUX");
317 			if (s != NULL) {
318 				path = xstrdup(s);
319 				path[strcspn (path, ",")] = '\0';
320 				if (*path == '\0') {
321 					free(path);
322 					label = xstrdup("default");
323 				}
324 			} else
325 				label = xstrdup("default");
326 		}
327 
328 		/* -L or default set. */
329 		if (label != NULL) {
330 			if ((path = makesocketpath(label)) == NULL) {
331 				fprintf(stderr, "can't create socket: %s\n",
332 				    strerror(errno));
333 				exit(1);
334 			}
335 		}
336 	}
337 	free(label);
338 
339 	if (strlcpy(socket_path, path, sizeof socket_path) >=
340 	    sizeof socket_path) {
341 		fprintf(stderr, "socket path too long: %s\n", path);
342 		exit(1);
343 	}
344 	free(path);
345 
346 	/* Set process title. */
347 	setproctitle("%s (%s)", __progname, socket_path);
348 
349 	/* Pass control to the client. */
350 	exit(client_main(event_init(), argc, argv, flags));
351 }
352