This commit was manufactured by cvs2svn to create tag 'TMUX_0_8'.
[tmux.git] / tmux.c
blobe7a52ac43b792c557fbe097c8042e88624a82849
1 /* $Id: tmux.c,v 1.113 2009-04-20 19:25:58 nicm Exp $ */
3 /*
4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
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.
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.
19 #include <sys/types.h>
20 #include <sys/stat.h>
22 #include <errno.h>
23 #include <pwd.h>
24 #include <signal.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <syslog.h>
28 #include <unistd.h>
30 #ifndef NO_PATHS_H
31 #include <paths.h>
32 #endif
34 #include "tmux.h"
36 #ifdef DEBUG
37 /* DragonFly uses an OpenBSD-like malloc() since 1.6 */
38 #if defined(__OpenBSD__) || defined(__DragonFly__)
39 const char *malloc_options = "AFGJPX";
40 #endif
41 #ifdef __FreeBSD__
42 const char *_malloc_options = "AJX";
43 #endif
44 #endif
46 volatile sig_atomic_t sigwinch;
47 volatile sig_atomic_t sigterm;
48 volatile sig_atomic_t sigcont;
49 volatile sig_atomic_t sigchld;
51 char *cfg_file;
52 struct options global_options;
53 struct options global_window_options;
55 int server_locked;
56 char *server_password;
57 time_t server_activity;
59 int debug_level;
60 int be_quiet;
61 time_t start_time;
62 char *socket_path;
64 __dead void usage(void);
65 char *makesockpath(const char *);
67 #ifdef NO_PROGNAME
68 const char *__progname = "tmux";
69 #endif
71 __dead void
72 usage(void)
74 fprintf(stderr, "usage: %s [-28dqUuVv] [-f file] "
75 "[-L socket-name] [-S socket-path] [command [flags]]\n",
76 __progname);
77 exit(1);
80 void
81 logfile(const char *name)
83 char *path;
85 log_close();
86 if (debug_level > 0) {
87 xasprintf(
88 &path, "%s-%s-%ld.log", __progname, name, (long) getpid());
89 log_open_file(debug_level, path);
90 xfree(path);
94 void
95 sighandler(int sig)
97 int saved_errno;
99 saved_errno = errno;
100 switch (sig) {
101 case SIGWINCH:
102 sigwinch = 1;
103 break;
104 case SIGTERM:
105 sigterm = 1;
106 break;
107 case SIGCHLD:
108 sigchld = 1;
109 break;
110 case SIGCONT:
111 sigcont = 1;
112 break;
114 errno = saved_errno;
117 void
118 siginit(void)
120 struct sigaction act;
122 memset(&act, 0, sizeof act);
123 sigemptyset(&act.sa_mask);
124 act.sa_flags = SA_RESTART;
126 act.sa_handler = SIG_IGN;
127 if (sigaction(SIGPIPE, &act, NULL) != 0)
128 fatal("sigaction failed");
129 if (sigaction(SIGUSR1, &act, NULL) != 0)
130 fatal("sigaction failed");
131 if (sigaction(SIGUSR2, &act, NULL) != 0)
132 fatal("sigaction failed");
133 if (sigaction(SIGINT, &act, NULL) != 0)
134 fatal("sigaction failed");
135 if (sigaction(SIGTSTP, &act, NULL) != 0)
136 fatal("sigaction failed");
137 if (sigaction(SIGQUIT, &act, NULL) != 0)
138 fatal("sigaction failed");
140 act.sa_handler = sighandler;
141 if (sigaction(SIGWINCH, &act, NULL) != 0)
142 fatal("sigaction failed");
143 if (sigaction(SIGTERM, &act, NULL) != 0)
144 fatal("sigaction failed");
145 if (sigaction(SIGCHLD, &act, NULL) != 0)
146 fatal("sigaction failed");
149 void
150 sigreset(void)
152 struct sigaction act;
154 memset(&act, 0, sizeof act);
155 sigemptyset(&act.sa_mask);
157 act.sa_handler = SIG_DFL;
158 if (sigaction(SIGPIPE, &act, NULL) != 0)
159 fatal("sigaction failed");
160 if (sigaction(SIGUSR1, &act, NULL) != 0)
161 fatal("sigaction failed");
162 if (sigaction(SIGUSR2, &act, NULL) != 0)
163 fatal("sigaction failed");
164 if (sigaction(SIGINT, &act, NULL) != 0)
165 fatal("sigaction failed");
166 if (sigaction(SIGTSTP, &act, NULL) != 0)
167 fatal("sigaction failed");
168 if (sigaction(SIGQUIT, &act, NULL) != 0)
169 fatal("sigaction failed");
170 if (sigaction(SIGWINCH, &act, NULL) != 0)
171 fatal("sigaction failed");
172 if (sigaction(SIGTERM, &act, NULL) != 0)
173 fatal("sigaction failed");
174 if (sigaction(SIGCHLD, &act, NULL) != 0)
175 fatal("sigaction failed");
178 char *
179 makesockpath(const char *label)
181 char base[MAXPATHLEN], *path;
182 struct stat sb;
183 u_int uid;
185 uid = getuid();
186 xsnprintf(base, MAXPATHLEN, "%s/%s-%d", _PATH_TMP, __progname, uid);
188 if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST)
189 return (NULL);
191 if (lstat(base, &sb) != 0)
192 return (NULL);
193 if (!S_ISDIR(sb.st_mode)) {
194 errno = ENOTDIR;
195 return (NULL);
197 if (sb.st_uid != uid || (sb.st_mode & (S_IRWXG|S_IRWXO)) != 0) {
198 errno = EACCES;
199 return (NULL);
202 xasprintf(&path, "%s/%s", base, label);
203 return (path);
207 main(int argc, char **argv)
209 struct client_ctx cctx;
210 struct msg_command_data cmddata;
211 struct buffer *b;
212 struct cmd_list *cmdlist;
213 struct cmd *cmd;
214 struct pollfd pfd;
215 struct hdr hdr;
216 const char *shell;
217 struct passwd *pw;
218 char *path, *label, *cause, *home, *pass = NULL;
219 char cwd[MAXPATHLEN];
220 int retcode, opt, flags, unlock, start_server;
222 unlock = flags = 0;
223 label = path = NULL;
224 while ((opt = getopt(argc, argv, "28df:L:qS:uUVv")) != -1) {
225 switch (opt) {
226 case '2':
227 flags |= IDENTIFY_256COLOURS;
228 flags &= ~IDENTIFY_88COLOURS;
229 break;
230 case '8':
231 flags |= IDENTIFY_88COLOURS;
232 flags &= ~IDENTIFY_256COLOURS;
233 break;
234 case 'f':
235 cfg_file = xstrdup(optarg);
236 break;
237 case 'L':
238 if (path != NULL) {
239 log_warnx("-L and -S cannot be used together");
240 exit(1);
242 if (label != NULL)
243 xfree(label);
244 label = xstrdup(optarg);
245 break;
246 case 'S':
247 if (label != NULL) {
248 log_warnx("-L and -S cannot be used together");
249 exit(1);
251 if (path != NULL)
252 xfree(path);
253 path = xstrdup(optarg);
254 break;
255 case 'q':
256 be_quiet = 1;
257 break;
258 case 'u':
259 flags |= IDENTIFY_UTF8;
260 break;
261 case 'U':
262 unlock = 1;
263 break;
264 case 'd':
265 flags |= IDENTIFY_HASDEFAULTS;
266 break;
267 case 'v':
268 debug_level++;
269 break;
270 case 'V':
271 printf("%s " BUILD "\n", __progname);
272 exit(0);
273 default:
274 usage();
277 argc -= optind;
278 argv += optind;
280 log_open_tty(debug_level);
281 siginit();
283 options_init(&global_options, NULL);
284 options_set_number(&global_options, "bell-action", BELL_ANY);
285 options_set_number(&global_options, "buffer-limit", 9);
286 options_set_number(&global_options, "display-time", 750);
287 options_set_number(&global_options, "history-limit", 2000);
288 options_set_number(&global_options, "message-bg", 3);
289 options_set_number(&global_options, "message-fg", 0);
290 options_set_number(&global_options, "message-attr", GRID_ATTR_REVERSE);
291 options_set_number(&global_options, "prefix", META);
292 options_set_number(&global_options, "repeat-time", 500);
293 options_set_number(&global_options, "set-titles", 1);
294 options_set_number(&global_options, "lock-after-time", 0);
295 options_set_number(&global_options, "set-remain-on-exit", 0);
296 options_set_number(&global_options, "status", 1);
297 options_set_number(&global_options, "status-bg", 2);
298 options_set_number(&global_options, "status-fg", 0);
299 options_set_number(&global_options, "status-attr", GRID_ATTR_REVERSE);
300 options_set_number(&global_options, "status-interval", 15);
301 options_set_number(&global_options, "status-left-length", 10);
302 options_set_number(&global_options, "status-right-length", 40);
303 options_set_string(&global_options, "status-left", "[#S]");
304 options_set_string(
305 &global_options, "status-right", "\"#24T\" %%H:%%M %%d-%%b-%%y");
306 options_set_number(&global_options, "status-keys", MODEKEY_EMACS);
307 options_init(&global_window_options, NULL);
308 options_set_number(&global_window_options, "aggressive-resize", 0);
309 options_set_number(&global_window_options, "clock-mode-colour", 4);
310 options_set_number(&global_window_options, "clock-mode-style", 1);
311 options_set_number(&global_window_options, "force-height", 0);
312 options_set_number(&global_window_options, "force-width", 0);
313 options_set_number(&global_window_options, "automatic-rename", 1);
314 options_set_number(&global_window_options, "mode-bg", 3);
315 options_set_number(&global_window_options, "mode-fg", 0);
316 options_set_number(
317 &global_window_options, "mode-attr", GRID_ATTR_REVERSE);
318 options_set_number(&global_window_options, "mode-keys", MODEKEY_EMACS);
319 options_set_number(&global_window_options, "monitor-activity", 0);
320 options_set_number(&global_window_options, "utf8", 0);
321 options_set_number(&global_window_options, "xterm-keys", 0);
322 options_set_number(&global_window_options, "remain-on-exit", 0);
323 options_set_number(&global_window_options, "window-status-bg", 8);
324 options_set_number(&global_window_options, "window-status-fg", 8);
325 options_set_number(&global_window_options, "window-status-attr", 0);
327 if (cfg_file == NULL) {
328 home = getenv("HOME");
329 if (home == NULL || *home == '\0') {
330 pw = getpwuid(getuid());
331 if (pw != NULL)
332 home = pw->pw_dir;
333 endpwent();
335 xasprintf(&cfg_file, "%s/%s", home, DEFAULT_CFG);
336 if (access(cfg_file, R_OK) != 0) {
337 xfree(cfg_file);
338 cfg_file = NULL;
340 } else {
341 if (access(cfg_file, R_OK) != 0) {
342 log_warn("%s", cfg_file);
343 exit(1);
347 if (label == NULL)
348 label = xstrdup("default");
349 if (path == NULL && (path = makesockpath(label)) == NULL) {
350 log_warn("can't create socket");
351 exit(1);
353 xfree(label);
355 shell = getenv("SHELL");
356 if (shell == NULL || *shell == '\0') {
357 pw = getpwuid(getuid());
358 if (pw != NULL)
359 shell = pw->pw_shell;
360 endpwent();
361 if (shell == NULL || *shell == '\0')
362 shell = _PATH_BSHELL;
364 options_set_string(
365 &global_options, "default-command", "exec %s", shell);
368 if (getcwd(cwd, sizeof cwd) == NULL) {
369 log_warn("getcwd");
370 exit(1);
372 options_set_string(&global_options, "default-path", "%s", cwd);
374 if (unlock) {
375 if (argc != 0) {
376 log_warnx("can't specify a command when unlocking");
377 exit(1);
379 cmdlist = NULL;
380 if ((pass = getpass("Password: ")) == NULL)
381 exit(1);
382 start_server = 0;
383 } else {
384 if (argc == 0) {
385 cmd = xmalloc(sizeof *cmd);
386 cmd->entry = &cmd_new_session_entry;
387 cmd->entry->init(cmd, 0);
389 cmdlist = xmalloc(sizeof *cmdlist);
390 TAILQ_INIT(cmdlist);
391 TAILQ_INSERT_HEAD(cmdlist, cmd, qentry);
392 } else {
393 cmdlist = cmd_list_parse(argc, argv, &cause);
394 if (cmdlist == NULL) {
395 log_warnx("%s", cause);
396 exit(1);
399 start_server = 0;
400 TAILQ_FOREACH(cmd, cmdlist, qentry) {
401 if (cmd->entry->flags & CMD_STARTSERVER) {
402 start_server = 1;
403 break;
408 memset(&cctx, 0, sizeof cctx);
409 if (client_init(path, &cctx, start_server, flags) != 0)
410 exit(1);
411 xfree(path);
413 b = buffer_create(BUFSIZ);
414 if (unlock) {
415 cmd_send_string(b, pass);
416 client_write_server(
417 &cctx, MSG_UNLOCK, BUFFER_OUT(b), BUFFER_USED(b));
418 } else {
419 cmd_list_send(cmdlist, b);
420 cmd_list_free(cmdlist);
421 client_fill_session(&cmddata);
422 client_write_server2(&cctx, MSG_COMMAND,
423 &cmddata, sizeof cmddata, BUFFER_OUT(b), BUFFER_USED(b));
425 buffer_destroy(b);
427 retcode = 0;
428 for (;;) {
429 pfd.fd = cctx.srv_fd;
430 pfd.events = POLLIN;
431 if (BUFFER_USED(cctx.srv_out) > 0)
432 pfd.events |= POLLOUT;
434 if (poll(&pfd, 1, INFTIM) == -1) {
435 if (errno == EAGAIN || errno == EINTR)
436 continue;
437 fatal("poll failed");
440 if (buffer_poll(&pfd, cctx.srv_in, cctx.srv_out) != 0)
441 goto out;
443 restart:
444 if (BUFFER_USED(cctx.srv_in) < sizeof hdr)
445 continue;
446 memcpy(&hdr, BUFFER_OUT(cctx.srv_in), sizeof hdr);
447 if (BUFFER_USED(cctx.srv_in) < (sizeof hdr) + hdr.size)
448 continue;
449 buffer_remove(cctx.srv_in, sizeof hdr);
451 switch (hdr.type) {
452 case MSG_EXIT:
453 case MSG_SHUTDOWN:
454 goto out;
455 case MSG_ERROR:
456 retcode = 1;
457 /* FALLTHROUGH */
458 case MSG_PRINT:
459 if (hdr.size > INT_MAX - 1)
460 fatalx("bad MSG_PRINT size");
461 log_info("%.*s",
462 (int) hdr.size, BUFFER_OUT(cctx.srv_in));
463 if (hdr.size != 0)
464 buffer_remove(cctx.srv_in, hdr.size);
465 goto restart;
466 case MSG_READY:
467 retcode = client_main(&cctx);
468 goto out;
469 default:
470 fatalx("unexpected command");
474 out:
475 options_free(&global_options);
476 options_free(&global_window_options);
478 close(cctx.srv_fd);
479 buffer_destroy(cctx.srv_in);
480 buffer_destroy(cctx.srv_out);
482 #ifdef DEBUG
483 xmalloc_report(getpid(), "client");
484 #endif
485 return (retcode);