1: /* $OpenBSD$ */
2:
3: /*
4: * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
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/ioctl.h>
21: #include <sys/socket.h>
22: #include <sys/stat.h>
23: #include <sys/un.h>
24: #include <sys/wait.h>
25:
26: #include <errno.h>
27: #include <event.h>
28: #include <fcntl.h>
29: #include <signal.h>
30: #include <stdio.h>
31: #include <stdlib.h>
32: #include <string.h>
33: #include <termios.h>
34: #include <time.h>
35: #include <unistd.h>
36:
37: #include "tmux.h"
38:
39: /*
40: * Main server functions.
41: */
42:
43: struct clients clients;
44:
45: struct tmuxproc *server_proc;
46: static int server_fd;
47: static int server_exit;
48: static struct event server_ev_accept;
49:
50: struct cmd_find_state marked_pane;
51:
52: static int server_create_socket(void);
53: static int server_loop(void);
54: static void server_send_exit(void);
55: static void server_accept(int, short, void *);
56: static void server_signal(int);
57: static void server_child_signal(void);
58: static void server_child_exited(pid_t, int);
59: static void server_child_stopped(pid_t, int);
60:
61: /* Set marked pane. */
62: void
63: server_set_marked(struct session *s, struct winlink *wl, struct window_pane *wp)
64: {
65: cmd_find_clear_state(&marked_pane, NULL, 0);
66: marked_pane.s = s;
67: marked_pane.wl = wl;
68: marked_pane.w = wl->window;
69: marked_pane.wp = wp;
70: }
71:
72: /* Clear marked pane. */
73: void
74: server_clear_marked(void)
75: {
76: cmd_find_clear_state(&marked_pane, NULL, 0);
77: }
78:
79: /* Is this the marked pane? */
80: int
81: server_is_marked(struct session *s, struct winlink *wl, struct window_pane *wp)
82: {
83: if (s == NULL || wl == NULL || wp == NULL)
84: return (0);
85: if (marked_pane.s != s || marked_pane.wl != wl)
86: return (0);
87: if (marked_pane.wp != wp)
88: return (0);
89: return (server_check_marked());
90: }
91:
92: /* Check if the marked pane is still valid. */
93: int
94: server_check_marked(void)
95: {
96: return (cmd_find_valid_state(&marked_pane));
97: }
98:
99: /* Create server socket. */
100: static int
101: server_create_socket(void)
102: {
103: struct sockaddr_un sa;
104: size_t size;
105: mode_t mask;
106: int fd;
107:
108: memset(&sa, 0, sizeof sa);
109: sa.sun_family = AF_UNIX;
110: size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path);
111: if (size >= sizeof sa.sun_path) {
112: errno = ENAMETOOLONG;
113: return (-1);
114: }
115: unlink(sa.sun_path);
116:
117: if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
118: return (-1);
119:
120: mask = umask(S_IXUSR|S_IXGRP|S_IRWXO);
121: if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) == -1)
122: return (-1);
123: umask(mask);
124:
125: if (listen(fd, 128) == -1)
126: return (-1);
127: setblocking(fd, 0);
128:
129: return (fd);
130: }
131:
132: /* Fork new server. */
133: int
134: server_start(struct event_base *base, int lockfd, char *lockfile)
135: {
136: int pair[2];
137:
138: if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
139: fatal("socketpair failed");
140:
141: server_proc = proc_start("server", base, 1, server_signal);
142: if (server_proc == NULL) {
143: close(pair[1]);
144: return (pair[0]);
145: }
146: close(pair[0]);
147:
148: if (log_get_level() > 3)
149: tty_create_log();
150: if (pledge("stdio rpath wpath cpath fattr unix getpw recvfd proc exec "
151: "tty ps", NULL) != 0)
152: fatal("pledge failed");
153:
154: RB_INIT(&windows);
155: RB_INIT(&all_window_panes);
156: TAILQ_INIT(&clients);
157: RB_INIT(&sessions);
158: RB_INIT(&session_groups);
159: key_bindings_init();
160:
161: gettimeofday(&start_time, NULL);
162:
163: server_fd = server_create_socket();
164: if (server_fd == -1)
165: fatal("couldn't create socket");
166: server_update_socket();
167: server_client_create(pair[1]);
168:
169: if (lockfd >= 0) {
170: unlink(lockfile);
171: free(lockfile);
172: close(lockfd);
173: }
174:
175: start_cfg();
176:
177: status_prompt_load_history();
178:
179: server_add_accept(0);
180:
181: proc_loop(server_proc, server_loop);
182: status_prompt_save_history();
183: exit(0);
184: }
185:
186: /* Server loop callback. */
187: static int
188: server_loop(void)
189: {
190: struct client *c;
191: u_int items;
192:
193: do {
194: items = cmdq_next(NULL);
195: TAILQ_FOREACH(c, &clients, entry) {
196: if (c->flags & CLIENT_IDENTIFIED)
197: items += cmdq_next(c);
198: }
199: } while (items != 0);
200:
201: server_client_loop();
202:
203: if (!options_get_number(global_options, "exit-unattached")) {
204: if (!RB_EMPTY(&sessions))
205: return (0);
206: }
207:
208: TAILQ_FOREACH(c, &clients, entry) {
209: if (c->session != NULL)
210: return (0);
211: }
212:
213: /*
214: * No attached clients therefore want to exit - flush any waiting
215: * clients but don't actually exit until they've gone.
216: */
217: cmd_wait_for_flush();
218: if (!TAILQ_EMPTY(&clients))
219: return (0);
220:
221: return (1);
222: }
223:
224: /* Exit the server by killing all clients and windows. */
225: static void
226: server_send_exit(void)
227: {
228: struct client *c, *c1;
229: struct session *s, *s1;
230:
231: cmd_wait_for_flush();
232:
233: TAILQ_FOREACH_SAFE(c, &clients, entry, c1) {
234: if (c->flags & CLIENT_SUSPENDED)
235: server_client_lost(c);
236: else
237: proc_send(c->peer, MSG_SHUTDOWN, -1, NULL, 0);
238: c->session = NULL;
239: }
240:
241: RB_FOREACH_SAFE(s, sessions, &sessions, s1)
242: session_destroy(s);
243: }
244:
245: /* Update socket execute permissions based on whether sessions are attached. */
246: void
247: server_update_socket(void)
248: {
249: struct session *s;
250: static int last = -1;
251: int n, mode;
252: struct stat sb;
253:
254: n = 0;
255: RB_FOREACH(s, sessions, &sessions) {
256: if (!(s->flags & SESSION_UNATTACHED)) {
257: n++;
258: break;
259: }
260: }
261:
262: if (n != last) {
263: last = n;
264:
265: if (stat(socket_path, &sb) != 0)
266: return;
267: mode = sb.st_mode & ACCESSPERMS;
268: if (n != 0) {
269: if (mode & S_IRUSR)
270: mode |= S_IXUSR;
271: if (mode & S_IRGRP)
272: mode |= S_IXGRP;
273: if (mode & S_IROTH)
274: mode |= S_IXOTH;
275: } else
276: mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
277: chmod(socket_path, mode);
278: }
279: }
280:
281: /* Callback for server socket. */
282: static void
283: server_accept(int fd, short events, __unused void *data)
284: {
285: struct sockaddr_storage sa;
286: socklen_t slen = sizeof sa;
287: int newfd;
288:
289: server_add_accept(0);
290: if (!(events & EV_READ))
291: return;
292:
293: newfd = accept(fd, (struct sockaddr *) &sa, &slen);
294: if (newfd == -1) {
295: if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED)
296: return;
297: if (errno == ENFILE || errno == EMFILE) {
298: /* Delete and don't try again for 1 second. */
299: server_add_accept(1);
300: return;
301: }
302: fatal("accept failed");
303: }
304: if (server_exit) {
305: close(newfd);
306: return;
307: }
308: server_client_create(newfd);
309: }
310:
311: /*
312: * Add accept event. If timeout is nonzero, add as a timeout instead of a read
313: * event - used to backoff when running out of file descriptors.
314: */
315: void
316: server_add_accept(int timeout)
317: {
318: struct timeval tv = { timeout, 0 };
319:
320: if (event_initialized(&server_ev_accept))
321: event_del(&server_ev_accept);
322:
323: if (timeout == 0) {
324: event_set(&server_ev_accept, server_fd, EV_READ, server_accept,
325: NULL);
326: event_add(&server_ev_accept, NULL);
327: } else {
328: event_set(&server_ev_accept, server_fd, EV_TIMEOUT,
329: server_accept, NULL);
330: event_add(&server_ev_accept, &tv);
331: }
332: }
333:
334: /* Signal handler. */
335: static void
336: server_signal(int sig)
337: {
338: int fd;
339:
340: switch (sig) {
341: case SIGTERM:
342: server_exit = 1;
343: server_send_exit();
344: break;
345: case SIGCHLD:
346: server_child_signal();
347: break;
348: case SIGUSR1:
349: event_del(&server_ev_accept);
350: fd = server_create_socket();
351: if (fd != -1) {
352: close(server_fd);
353: server_fd = fd;
354: server_update_socket();
355: }
356: server_add_accept(0);
357: break;
358: }
359: }
360:
361: /* Handle SIGCHLD. */
362: static void
363: server_child_signal(void)
364: {
365: int status;
366: pid_t pid;
367:
368: for (;;) {
369: switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) {
370: case -1:
371: if (errno == ECHILD)
372: return;
373: fatal("waitpid failed");
374: case 0:
375: return;
376: }
377: if (WIFSTOPPED(status))
378: server_child_stopped(pid, status);
379: else if (WIFEXITED(status) || WIFSIGNALED(status))
380: server_child_exited(pid, status);
381: }
382: }
383:
384: /* Handle exited children. */
385: static void
386: server_child_exited(pid_t pid, int status)
387: {
388: struct window *w, *w1;
389: struct window_pane *wp;
390: struct job *job;
391:
392: RB_FOREACH_SAFE(w, windows, &windows, w1) {
393: TAILQ_FOREACH(wp, &w->panes, entry) {
394: if (wp->pid == pid) {
395: wp->status = status;
396: server_destroy_pane(wp, 1);
397: break;
398: }
399: }
400: }
401:
402: LIST_FOREACH(job, &all_jobs, lentry) {
403: if (pid == job->pid) {
404: job_died(job, status); /* might free job */
405: break;
406: }
407: }
408: }
409:
410: /* Handle stopped children. */
411: static void
412: server_child_stopped(pid_t pid, int status)
413: {
414: struct window *w;
415: struct window_pane *wp;
416:
417: if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU)
418: return;
419:
420: RB_FOREACH(w, windows, &windows) {
421: TAILQ_FOREACH(wp, &w->panes, entry) {
422: if (wp->pid == pid) {
423: if (killpg(pid, SIGCONT) != 0)
424: kill(pid, SIGCONT);
425: }
426: }
427: }
428: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>