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