1 /* $Id: tmux.c,v 1.113 2009-04-20 19:25:58 nicm Exp $ */
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>
37 /* DragonFly uses an OpenBSD-like malloc() since 1.6 */
38 #if defined(__OpenBSD__) || defined(__DragonFly__)
39 const char *malloc_options
= "AFGJPX";
42 const char *_malloc_options
= "AJX";
46 volatile sig_atomic_t sigwinch
;
47 volatile sig_atomic_t sigterm
;
48 volatile sig_atomic_t sigcont
;
49 volatile sig_atomic_t sigchld
;
52 struct options global_options
;
53 struct options global_window_options
;
56 char *server_password
;
57 time_t server_activity
;
64 __dead
void usage(void);
65 char *makesockpath(const char *);
68 const char *__progname
= "tmux";
74 fprintf(stderr
, "usage: %s [-28dqUuVv] [-f file] "
75 "[-L socket-name] [-S socket-path] [command [flags]]\n",
81 logfile(const char *name
)
86 if (debug_level
> 0) {
88 &path
, "%s-%s-%ld.log", __progname
, name
, (long) getpid());
89 log_open_file(debug_level
, path
);
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");
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");
179 makesockpath(const char *label
)
181 char base
[MAXPATHLEN
], *path
;
186 xsnprintf(base
, MAXPATHLEN
, "%s/%s-%d", _PATH_TMP
, __progname
, uid
);
188 if (mkdir(base
, S_IRWXU
) != 0 && errno
!= EEXIST
)
191 if (lstat(base
, &sb
) != 0)
193 if (!S_ISDIR(sb
.st_mode
)) {
197 if (sb
.st_uid
!= uid
|| (sb
.st_mode
& (S_IRWXG
|S_IRWXO
)) != 0) {
202 xasprintf(&path
, "%s/%s", base
, label
);
207 main(int argc
, char **argv
)
209 struct client_ctx cctx
;
210 struct msg_command_data cmddata
;
212 struct cmd_list
*cmdlist
;
218 char *path
, *label
, *cause
, *home
, *pass
= NULL
;
219 char cwd
[MAXPATHLEN
];
220 int retcode
, opt
, flags
, unlock
, start_server
;
224 while ((opt
= getopt(argc
, argv
, "28df:L:qS:uUVv")) != -1) {
227 flags
|= IDENTIFY_256COLOURS
;
228 flags
&= ~IDENTIFY_88COLOURS
;
231 flags
|= IDENTIFY_88COLOURS
;
232 flags
&= ~IDENTIFY_256COLOURS
;
235 cfg_file
= xstrdup(optarg
);
239 log_warnx("-L and -S cannot be used together");
244 label
= xstrdup(optarg
);
248 log_warnx("-L and -S cannot be used together");
253 path
= xstrdup(optarg
);
259 flags
|= IDENTIFY_UTF8
;
265 flags
|= IDENTIFY_HASDEFAULTS
;
271 printf("%s " BUILD
"\n", __progname
);
280 log_open_tty(debug_level
);
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]");
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);
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());
335 xasprintf(&cfg_file
, "%s/%s", home
, DEFAULT_CFG
);
336 if (access(cfg_file
, R_OK
) != 0) {
341 if (access(cfg_file
, R_OK
) != 0) {
342 log_warn("%s", cfg_file
);
348 label
= xstrdup("default");
349 if (path
== NULL
&& (path
= makesockpath(label
)) == NULL
) {
350 log_warn("can't create socket");
355 shell
= getenv("SHELL");
356 if (shell
== NULL
|| *shell
== '\0') {
357 pw
= getpwuid(getuid());
359 shell
= pw
->pw_shell
;
361 if (shell
== NULL
|| *shell
== '\0')
362 shell
= _PATH_BSHELL
;
365 &global_options
, "default-command", "exec %s", shell
);
368 if (getcwd(cwd
, sizeof cwd
) == NULL
) {
372 options_set_string(&global_options
, "default-path", "%s", cwd
);
376 log_warnx("can't specify a command when unlocking");
380 if ((pass
= getpass("Password: ")) == NULL
)
385 cmd
= xmalloc(sizeof *cmd
);
386 cmd
->entry
= &cmd_new_session_entry
;
387 cmd
->entry
->init(cmd
, 0);
389 cmdlist
= xmalloc(sizeof *cmdlist
);
391 TAILQ_INSERT_HEAD(cmdlist
, cmd
, qentry
);
393 cmdlist
= cmd_list_parse(argc
, argv
, &cause
);
394 if (cmdlist
== NULL
) {
395 log_warnx("%s", cause
);
400 TAILQ_FOREACH(cmd
, cmdlist
, qentry
) {
401 if (cmd
->entry
->flags
& CMD_STARTSERVER
) {
408 memset(&cctx
, 0, sizeof cctx
);
409 if (client_init(path
, &cctx
, start_server
, flags
) != 0)
413 b
= buffer_create(BUFSIZ
);
415 cmd_send_string(b
, pass
);
417 &cctx
, MSG_UNLOCK
, BUFFER_OUT(b
), BUFFER_USED(b
));
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
));
429 pfd
.fd
= cctx
.srv_fd
;
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
)
437 fatal("poll failed");
440 if (buffer_poll(&pfd
, cctx
.srv_in
, cctx
.srv_out
) != 0)
444 if (BUFFER_USED(cctx
.srv_in
) < sizeof hdr
)
446 memcpy(&hdr
, BUFFER_OUT(cctx
.srv_in
), sizeof hdr
);
447 if (BUFFER_USED(cctx
.srv_in
) < (sizeof hdr
) + hdr
.size
)
449 buffer_remove(cctx
.srv_in
, sizeof hdr
);
459 if (hdr
.size
> INT_MAX
- 1)
460 fatalx("bad MSG_PRINT size");
462 (int) hdr
.size
, BUFFER_OUT(cctx
.srv_in
));
464 buffer_remove(cctx
.srv_in
, hdr
.size
);
467 retcode
= client_main(&cctx
);
470 fatalx("unexpected command");
475 options_free(&global_options
);
476 options_free(&global_window_options
);
479 buffer_destroy(cctx
.srv_in
);
480 buffer_destroy(cctx
.srv_out
);
483 xmalloc_report(getpid(), "client");