Annotation of embedaddon/tmux/server-fn.c, revision 1.1.1.1

1.1       misho       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/uio.h>
                     21: 
                     22: #include <stdlib.h>
                     23: #include <string.h>
                     24: #include <time.h>
                     25: #include <unistd.h>
                     26: 
                     27: #include "tmux.h"
                     28: 
                     29: static struct session  *server_next_session(struct session *);
                     30: static void             server_destroy_session_group(struct session *);
                     31: 
                     32: void
                     33: server_redraw_client(struct client *c)
                     34: {
                     35:        c->flags |= CLIENT_REDRAW;
                     36: }
                     37: 
                     38: void
                     39: server_status_client(struct client *c)
                     40: {
                     41:        c->flags |= CLIENT_STATUS;
                     42: }
                     43: 
                     44: void
                     45: server_redraw_session(struct session *s)
                     46: {
                     47:        struct client   *c;
                     48: 
                     49:        TAILQ_FOREACH(c, &clients, entry) {
                     50:                if (c->session == s)
                     51:                        server_redraw_client(c);
                     52:        }
                     53: }
                     54: 
                     55: void
                     56: server_redraw_session_group(struct session *s)
                     57: {
                     58:        struct session_group    *sg;
                     59: 
                     60:        if ((sg = session_group_contains(s)) == NULL)
                     61:                server_redraw_session(s);
                     62:        else {
                     63:                TAILQ_FOREACH(s, &sg->sessions, gentry)
                     64:                        server_redraw_session(s);
                     65:        }
                     66: }
                     67: 
                     68: void
                     69: server_status_session(struct session *s)
                     70: {
                     71:        struct client   *c;
                     72: 
                     73:        TAILQ_FOREACH(c, &clients, entry) {
                     74:                if (c->session == s)
                     75:                        server_status_client(c);
                     76:        }
                     77: }
                     78: 
                     79: void
                     80: server_status_session_group(struct session *s)
                     81: {
                     82:        struct session_group    *sg;
                     83: 
                     84:        if ((sg = session_group_contains(s)) == NULL)
                     85:                server_status_session(s);
                     86:        else {
                     87:                TAILQ_FOREACH(s, &sg->sessions, gentry)
                     88:                        server_status_session(s);
                     89:        }
                     90: }
                     91: 
                     92: void
                     93: server_redraw_window(struct window *w)
                     94: {
                     95:        struct client   *c;
                     96: 
                     97:        TAILQ_FOREACH(c, &clients, entry) {
                     98:                if (c->session != NULL && c->session->curw->window == w)
                     99:                        server_redraw_client(c);
                    100:        }
                    101: }
                    102: 
                    103: void
                    104: server_redraw_window_borders(struct window *w)
                    105: {
                    106:        struct client   *c;
                    107: 
                    108:        TAILQ_FOREACH(c, &clients, entry) {
                    109:                if (c->session != NULL && c->session->curw->window == w)
                    110:                        c->flags |= CLIENT_BORDERS;
                    111:        }
                    112: }
                    113: 
                    114: void
                    115: server_status_window(struct window *w)
                    116: {
                    117:        struct session  *s;
                    118: 
                    119:        /*
                    120:         * This is slightly different. We want to redraw the status line of any
                    121:         * clients containing this window rather than anywhere it is the
                    122:         * current window.
                    123:         */
                    124: 
                    125:        RB_FOREACH(s, sessions, &sessions) {
                    126:                if (session_has(s, w))
                    127:                        server_status_session(s);
                    128:        }
                    129: }
                    130: 
                    131: void
                    132: server_lock(void)
                    133: {
                    134:        struct client   *c;
                    135: 
                    136:        TAILQ_FOREACH(c, &clients, entry) {
                    137:                if (c->session != NULL)
                    138:                        server_lock_client(c);
                    139:        }
                    140: }
                    141: 
                    142: void
                    143: server_lock_session(struct session *s)
                    144: {
                    145:        struct client   *c;
                    146: 
                    147:        TAILQ_FOREACH(c, &clients, entry) {
                    148:                if (c->session == s)
                    149:                        server_lock_client(c);
                    150:        }
                    151: }
                    152: 
                    153: void
                    154: server_lock_client(struct client *c)
                    155: {
                    156:        const char      *cmd;
                    157: 
                    158:        if (c->flags & CLIENT_CONTROL)
                    159:                return;
                    160: 
                    161:        if (c->flags & CLIENT_SUSPENDED)
                    162:                return;
                    163: 
                    164:        cmd = options_get_string(c->session->options, "lock-command");
                    165:        if (strlen(cmd) + 1 > MAX_IMSGSIZE - IMSG_HEADER_SIZE)
                    166:                return;
                    167: 
                    168:        tty_stop_tty(&c->tty);
                    169:        tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_SMCUP));
                    170:        tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_CLEAR));
                    171:        tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_E3));
                    172: 
                    173:        c->flags |= CLIENT_SUSPENDED;
                    174:        proc_send_s(c->peer, MSG_LOCK, cmd);
                    175: }
                    176: 
                    177: void
                    178: server_kill_window(struct window *w)
                    179: {
                    180:        struct session          *s, *next_s, *target_s;
                    181:        struct session_group    *sg;
                    182:        struct winlink          *wl;
                    183: 
                    184:        next_s = RB_MIN(sessions, &sessions);
                    185:        while (next_s != NULL) {
                    186:                s = next_s;
                    187:                next_s = RB_NEXT(sessions, &sessions, s);
                    188: 
                    189:                if (!session_has(s, w))
                    190:                        continue;
                    191:                server_unzoom_window(w);
                    192:                while ((wl = winlink_find_by_window(&s->windows, w)) != NULL) {
                    193:                        if (session_detach(s, wl)) {
                    194:                                server_destroy_session_group(s);
                    195:                                break;
                    196:                        } else
                    197:                                server_redraw_session_group(s);
                    198:                }
                    199: 
                    200:                if (options_get_number(s->options, "renumber-windows")) {
                    201:                        if ((sg = session_group_contains(s)) != NULL) {
                    202:                                TAILQ_FOREACH(target_s, &sg->sessions, gentry)
                    203:                                        session_renumber_windows(target_s);
                    204:                        } else
                    205:                                session_renumber_windows(s);
                    206:                }
                    207:        }
                    208:        recalculate_sizes();
                    209: }
                    210: 
                    211: int
                    212: server_link_window(struct session *src, struct winlink *srcwl,
                    213:     struct session *dst, int dstidx, int killflag, int selectflag,
                    214:     char **cause)
                    215: {
                    216:        struct winlink          *dstwl;
                    217:        struct session_group    *srcsg, *dstsg;
                    218: 
                    219:        srcsg = session_group_contains(src);
                    220:        dstsg = session_group_contains(dst);
                    221:        if (src != dst && srcsg != NULL && dstsg != NULL && srcsg == dstsg) {
                    222:                xasprintf(cause, "sessions are grouped");
                    223:                return (-1);
                    224:        }
                    225: 
                    226:        dstwl = NULL;
                    227:        if (dstidx != -1)
                    228:                dstwl = winlink_find_by_index(&dst->windows, dstidx);
                    229:        if (dstwl != NULL) {
                    230:                if (dstwl->window == srcwl->window) {
                    231:                        xasprintf(cause, "same index: %d", dstidx);
                    232:                        return (-1);
                    233:                }
                    234:                if (killflag) {
                    235:                        /*
                    236:                         * Can't use session_detach as it will destroy session
                    237:                         * if this makes it empty.
                    238:                         */
                    239:                        notify_session_window("window-unlinked", dst,
                    240:                            dstwl->window);
                    241:                        dstwl->flags &= ~WINLINK_ALERTFLAGS;
                    242:                        winlink_stack_remove(&dst->lastw, dstwl);
                    243:                        winlink_remove(&dst->windows, dstwl);
                    244: 
                    245:                        /* Force select/redraw if current. */
                    246:                        if (dstwl == dst->curw) {
                    247:                                selectflag = 1;
                    248:                                dst->curw = NULL;
                    249:                        }
                    250:                }
                    251:        }
                    252: 
                    253:        if (dstidx == -1)
                    254:                dstidx = -1 - options_get_number(dst->options, "base-index");
                    255:        dstwl = session_attach(dst, srcwl->window, dstidx, cause);
                    256:        if (dstwl == NULL)
                    257:                return (-1);
                    258: 
                    259:        if (selectflag)
                    260:                session_select(dst, dstwl->idx);
                    261:        server_redraw_session_group(dst);
                    262: 
                    263:        return (0);
                    264: }
                    265: 
                    266: void
                    267: server_unlink_window(struct session *s, struct winlink *wl)
                    268: {
                    269:        if (session_detach(s, wl))
                    270:                server_destroy_session_group(s);
                    271:        else
                    272:                server_redraw_session_group(s);
                    273: }
                    274: 
                    275: void
                    276: server_destroy_pane(struct window_pane *wp, int notify)
                    277: {
                    278:        struct window           *w = wp->window;
                    279:        int                      old_fd;
                    280:        struct screen_write_ctx  ctx;
                    281:        struct grid_cell         gc;
                    282: 
                    283:        old_fd = wp->fd;
                    284:        if (wp->fd != -1) {
                    285: #ifdef HAVE_UTEMPTER
                    286:                utempter_remove_record(wp->fd);
                    287: #endif
                    288:                bufferevent_free(wp->event);
                    289:                close(wp->fd);
                    290:                wp->fd = -1;
                    291:        }
                    292: 
                    293:        if (options_get_number(w->options, "remain-on-exit")) {
                    294:                if (old_fd == -1)
                    295:                        return;
                    296: 
                    297:                if (notify)
                    298:                        notify_pane("pane-died", wp);
                    299: 
                    300:                screen_write_start(&ctx, wp, &wp->base);
                    301:                screen_write_scrollregion(&ctx, 0, screen_size_y(ctx.s) - 1);
                    302:                screen_write_cursormove(&ctx, 0, screen_size_y(ctx.s) - 1);
                    303:                screen_write_linefeed(&ctx, 1);
                    304:                memcpy(&gc, &grid_default_cell, sizeof gc);
                    305:                gc.attr |= GRID_ATTR_BRIGHT;
                    306:                screen_write_puts(&ctx, &gc, "Pane is dead");
                    307:                screen_write_stop(&ctx);
                    308:                wp->flags |= PANE_REDRAW;
                    309: 
                    310:                return;
                    311:        }
                    312: 
                    313:        if (notify)
                    314:                notify_pane("pane-exited", wp);
                    315: 
                    316:        server_unzoom_window(w);
                    317:        layout_close_pane(wp);
                    318:        window_remove_pane(w, wp);
                    319: 
                    320:        if (TAILQ_EMPTY(&w->panes))
                    321:                server_kill_window(w);
                    322:        else
                    323:                server_redraw_window(w);
                    324: }
                    325: 
                    326: static void
                    327: server_destroy_session_group(struct session *s)
                    328: {
                    329:        struct session_group    *sg;
                    330:        struct session          *s1;
                    331: 
                    332:        if ((sg = session_group_contains(s)) == NULL)
                    333:                server_destroy_session(s);
                    334:        else {
                    335:                TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) {
                    336:                        server_destroy_session(s);
                    337:                        session_destroy(s);
                    338:                }
                    339:        }
                    340: }
                    341: 
                    342: static struct session *
                    343: server_next_session(struct session *s)
                    344: {
                    345:        struct session *s_loop, *s_out;
                    346: 
                    347:        s_out = NULL;
                    348:        RB_FOREACH(s_loop, sessions, &sessions) {
                    349:                if (s_loop == s)
                    350:                        continue;
                    351:                if (s_out == NULL ||
                    352:                    timercmp(&s_loop->activity_time, &s_out->activity_time, <))
                    353:                        s_out = s_loop;
                    354:        }
                    355:        return (s_out);
                    356: }
                    357: 
                    358: void
                    359: server_destroy_session(struct session *s)
                    360: {
                    361:        struct client   *c;
                    362:        struct session  *s_new;
                    363: 
                    364:        if (!options_get_number(s->options, "detach-on-destroy"))
                    365:                s_new = server_next_session(s);
                    366:        else
                    367:                s_new = NULL;
                    368: 
                    369:        TAILQ_FOREACH(c, &clients, entry) {
                    370:                if (c->session != s)
                    371:                        continue;
                    372:                if (s_new == NULL) {
                    373:                        c->session = NULL;
                    374:                        c->flags |= CLIENT_EXIT;
                    375:                } else {
                    376:                        c->last_session = NULL;
                    377:                        c->session = s_new;
                    378:                        server_client_set_key_table(c, NULL);
                    379:                        status_timer_start(c);
                    380:                        notify_client("client-session-changed", c);
                    381:                        session_update_activity(s_new, NULL);
                    382:                        gettimeofday(&s_new->last_attached_time, NULL);
                    383:                        server_redraw_client(c);
                    384:                        alerts_check_session(s_new);
                    385:                }
                    386:        }
                    387:        recalculate_sizes();
                    388: }
                    389: 
                    390: void
                    391: server_check_unattached(void)
                    392: {
                    393:        struct session  *s;
                    394: 
                    395:        /*
                    396:         * If any sessions are no longer attached and have destroy-unattached
                    397:         * set, collect them.
                    398:         */
                    399:        RB_FOREACH(s, sessions, &sessions) {
                    400:                if (!(s->flags & SESSION_UNATTACHED))
                    401:                        continue;
                    402:                if (options_get_number (s->options, "destroy-unattached"))
                    403:                        session_destroy(s);
                    404:        }
                    405: }
                    406: 
                    407: /* Set stdin callback. */
                    408: int
                    409: server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int,
                    410:     void *), void *cb_data, char **cause)
                    411: {
                    412:        if (c == NULL || c->session != NULL) {
                    413:                *cause = xstrdup("no client with stdin");
                    414:                return (-1);
                    415:        }
                    416:        if (c->flags & CLIENT_TERMINAL) {
                    417:                *cause = xstrdup("stdin is a tty");
                    418:                return (-1);
                    419:        }
                    420:        if (c->stdin_callback != NULL) {
                    421:                *cause = xstrdup("stdin in use");
                    422:                return (-1);
                    423:        }
                    424: 
                    425:        c->stdin_callback_data = cb_data;
                    426:        c->stdin_callback = cb;
                    427: 
                    428:        c->references++;
                    429: 
                    430:        if (c->stdin_closed)
                    431:                c->stdin_callback(c, 1, c->stdin_callback_data);
                    432: 
                    433:        proc_send(c->peer, MSG_STDIN, -1, NULL, 0);
                    434: 
                    435:        return (0);
                    436: }
                    437: 
                    438: void
                    439: server_unzoom_window(struct window *w)
                    440: {
                    441:        if (window_unzoom(w) == 0) {
                    442:                server_redraw_window(w);
                    443:                server_status_window(w);
                    444:        }
                    445: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>