[BACK]Return to session.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / tmux

Annotation of src/usr.bin/tmux/session.c, Revision 1.14

1.14    ! nicm        1: /* $OpenBSD: session.c,v 1.13 2009/11/13 14:47:31 nicm Exp $ */
1.1       nicm        2:
                      3: /*
                      4:  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
                      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/time.h>
                     21:
1.6       nicm       22: #include <paths.h>
1.1       nicm       23: #include <string.h>
                     24: #include <stdlib.h>
                     25: #include <unistd.h>
1.10      nicm       26: #include <time.h>
1.1       nicm       27:
                     28: #include "tmux.h"
                     29:
                     30: /* Global session list. */
                     31: struct sessions        sessions;
1.7       nicm       32: struct sessions dead_sessions;
1.11      nicm       33: struct session_groups session_groups;
1.1       nicm       34:
                     35: struct winlink *session_next_activity(struct session *, struct winlink *);
                     36: struct winlink *session_previous_activity(struct session *, struct winlink *);
                     37:
                     38: void
                     39: session_alert_cancel(struct session *s, struct winlink *wl)
                     40: {
                     41:        struct session_alert    *sa, *sb;
                     42:
                     43:        sa = SLIST_FIRST(&s->alerts);
                     44:        while (sa != NULL) {
                     45:                sb = sa;
                     46:                sa = SLIST_NEXT(sa, entry);
                     47:
                     48:                if (wl == NULL || sb->wl == wl) {
                     49:                        SLIST_REMOVE(&s->alerts, sb, session_alert, entry);
                     50:                        xfree(sb);
                     51:                }
                     52:        }
                     53: }
                     54:
                     55: void
                     56: session_alert_add(struct session *s, struct window *w, int type)
                     57: {
                     58:        struct session_alert    *sa;
                     59:        struct winlink          *wl;
                     60:
                     61:        RB_FOREACH(wl, winlinks, &s->windows) {
                     62:                if (wl == s->curw)
                     63:                        continue;
                     64:
                     65:                if (wl->window == w &&
                     66:                    !session_alert_has(s, wl, type)) {
                     67:                        sa = xmalloc(sizeof *sa);
                     68:                        sa->wl = wl;
                     69:                        sa->type = type;
                     70:                        SLIST_INSERT_HEAD(&s->alerts, sa, entry);
                     71:                }
                     72:        }
                     73: }
                     74:
                     75: int
                     76: session_alert_has(struct session *s, struct winlink *wl, int type)
                     77: {
                     78:        struct session_alert    *sa;
                     79:
                     80:        SLIST_FOREACH(sa, &s->alerts, entry) {
                     81:                if (sa->wl == wl && sa->type == type)
                     82:                        return (1);
                     83:        }
                     84:
                     85:        return (0);
                     86: }
                     87:
                     88: int
                     89: session_alert_has_window(struct session *s, struct window *w, int type)
                     90: {
                     91:        struct session_alert    *sa;
                     92:
                     93:        SLIST_FOREACH(sa, &s->alerts, entry) {
                     94:                if (sa->wl->window == w && sa->type == type)
                     95:                        return (1);
                     96:        }
                     97:
                     98:        return (0);
                     99: }
                    100:
                    101: /* Find session by name. */
                    102: struct session *
                    103: session_find(const char *name)
                    104: {
                    105:        struct session  *s;
                    106:        u_int            i;
                    107:
                    108:        for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
                    109:                s = ARRAY_ITEM(&sessions, i);
                    110:                if (s != NULL && strcmp(s->name, name) == 0)
                    111:                        return (s);
                    112:        }
                    113:
                    114:        return (NULL);
                    115: }
                    116:
                    117: /* Create a new session. */
                    118: struct session *
1.3       nicm      119: session_create(const char *name, const char *cmd, const char *cwd,
1.5       nicm      120:     struct environ *env, struct termios *tio, int idx, u_int sx, u_int sy,
                    121:     char **cause)
1.1       nicm      122: {
                    123:        struct session  *s;
                    124:        u_int            i;
                    125:
                    126:        s = xmalloc(sizeof *s);
1.7       nicm      127:        s->references = 0;
1.1       nicm      128:        s->flags = 0;
1.7       nicm      129:
1.12      nicm      130:        if (gettimeofday(&s->creation_time, NULL) != 0)
1.9       nicm      131:                fatal("gettimeofday failed");
1.12      nicm      132:        memcpy(&s->activity_time, &s->creation_time, sizeof s->activity_time);
1.7       nicm      133:
1.1       nicm      134:        s->curw = NULL;
1.11      nicm      135:        TAILQ_INIT(&s->lastw);
1.1       nicm      136:        RB_INIT(&s->windows);
                    137:        SLIST_INIT(&s->alerts);
1.7       nicm      138:
1.1       nicm      139:        paste_init_stack(&s->buffers);
1.7       nicm      140:
1.2       nicm      141:        options_init(&s->options, &global_s_options);
1.3       nicm      142:        environ_init(&s->environ);
                    143:        if (env != NULL)
                    144:                environ_copy(env, &s->environ);
1.8       nicm      145:
                    146:        s->tio = NULL;
                    147:        if (tio != NULL) {
                    148:                s->tio = xmalloc(sizeof *s->tio);
                    149:                memcpy(s->tio, tio, sizeof *s->tio);
                    150:        }
1.1       nicm      151:
                    152:        s->sx = sx;
                    153:        s->sy = sy;
                    154:
                    155:        for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
                    156:                if (ARRAY_ITEM(&sessions, i) == NULL) {
                    157:                        ARRAY_SET(&sessions, i, s);
                    158:                        break;
                    159:                }
                    160:        }
                    161:        if (i == ARRAY_LENGTH(&sessions))
                    162:                ARRAY_ADD(&sessions, s);
                    163:
                    164:        if (name != NULL)
                    165:                s->name = xstrdup(name);
                    166:        else
                    167:                xasprintf(&s->name, "%u", i);
1.11      nicm      168:
                    169:        if (cmd != NULL) {
                    170:                if (session_new(s, NULL, cmd, cwd, idx, cause) == NULL) {
                    171:                        session_destroy(s);
                    172:                        return (NULL);
                    173:                }
                    174:                session_select(s, RB_ROOT(&s->windows)->idx);
1.1       nicm      175:        }
                    176:
                    177:        log_debug("session %s created", s->name);
                    178:
                    179:        return (s);
                    180: }
                    181:
                    182: /* Destroy a session. */
                    183: void
                    184: session_destroy(struct session *s)
                    185: {
                    186:        u_int   i;
                    187:
                    188:        log_debug("session %s destroyed", s->name);
                    189:
                    190:        if (session_index(s, &i) != 0)
                    191:                fatalx("session not found");
                    192:        ARRAY_SET(&sessions, i, NULL);
                    193:        while (!ARRAY_EMPTY(&sessions) && ARRAY_LAST(&sessions) == NULL)
                    194:                ARRAY_TRUNC(&sessions, 1);
                    195:
1.8       nicm      196:        if (s->tio != NULL)
                    197:                xfree(s->tio);
                    198:
1.11      nicm      199:        session_group_remove(s);
1.1       nicm      200:        session_alert_cancel(s, NULL);
1.3       nicm      201:        environ_free(&s->environ);
1.1       nicm      202:        options_free(&s->options);
                    203:        paste_free_stack(&s->buffers);
                    204:
1.11      nicm      205:        while (!TAILQ_EMPTY(&s->lastw))
                    206:                winlink_stack_remove(&s->lastw, TAILQ_FIRST(&s->lastw));
1.1       nicm      207:        while (!RB_EMPTY(&s->windows))
                    208:                winlink_remove(&s->windows, RB_ROOT(&s->windows));
                    209:
                    210:        xfree(s->name);
1.7       nicm      211:
                    212:        for (i = 0; i < ARRAY_LENGTH(&dead_sessions); i++) {
                    213:                if (ARRAY_ITEM(&dead_sessions, i) == NULL) {
                    214:                        ARRAY_SET(&dead_sessions, i, s);
                    215:                        break;
                    216:                }
                    217:        }
                    218:        if (i == ARRAY_LENGTH(&dead_sessions))
                    219:                ARRAY_ADD(&dead_sessions, s);
                    220:        s->flags |= SESSION_DEAD;
1.1       nicm      221: }
                    222:
                    223: /* Find session index. */
                    224: int
                    225: session_index(struct session *s, u_int *i)
                    226: {
                    227:        for (*i = 0; *i < ARRAY_LENGTH(&sessions); (*i)++) {
                    228:                if (s == ARRAY_ITEM(&sessions, *i))
                    229:                        return (0);
                    230:        }
                    231:        return (-1);
                    232: }
                    233:
                    234: /* Create a new window on a session. */
                    235: struct winlink *
1.4       nicm      236: session_new(struct session *s,
1.1       nicm      237:     const char *name, const char *cmd, const char *cwd, int idx, char **cause)
                    238: {
                    239:        struct window   *w;
1.3       nicm      240:        struct environ   env;
1.6       nicm      241:        const char      *shell;
1.1       nicm      242:        u_int            hlimit;
                    243:
1.3       nicm      244:        environ_init(&env);
                    245:        environ_copy(&global_environ, &env);
                    246:        environ_copy(&s->environ, &env);
                    247:        server_fill_environ(s, &env);
1.1       nicm      248:
1.6       nicm      249:        shell = options_get_string(&s->options, "default-shell");
                    250:        if (*shell == '\0' || areshell(shell))
                    251:                shell = _PATH_BSHELL;
                    252:
1.1       nicm      253:        hlimit = options_get_number(&s->options, "history-limit");
1.4       nicm      254:        w = window_create(
1.8       nicm      255:            name, cmd, shell, cwd, &env, s->tio, s->sx, s->sy, hlimit, cause);
1.3       nicm      256:        if (w == NULL) {
                    257:                environ_free(&env);
1.1       nicm      258:                return (NULL);
1.3       nicm      259:        }
                    260:        environ_free(&env);
1.1       nicm      261:
                    262:        if (options_get_number(&s->options, "set-remain-on-exit"))
                    263:                options_set_number(&w->options, "remain-on-exit", 1);
                    264:
                    265:        return (session_attach(s, w, idx, cause));
                    266: }
                    267:
                    268: /* Attach a window to a session. */
                    269: struct winlink *
                    270: session_attach(struct session *s, struct window *w, int idx, char **cause)
                    271: {
                    272:        struct winlink  *wl;
                    273:
                    274:        if ((wl = winlink_add(&s->windows, w, idx)) == NULL)
                    275:                xasprintf(cause, "index in use: %d", idx);
1.11      nicm      276:        session_group_synchronize_from(s);
1.1       nicm      277:        return (wl);
                    278: }
                    279:
                    280: /* Detach a window from a session. */
                    281: int
                    282: session_detach(struct session *s, struct winlink *wl)
                    283: {
                    284:        if (s->curw == wl &&
                    285:            session_last(s) != 0 && session_previous(s, 0) != 0)
                    286:                session_next(s, 0);
                    287:
                    288:        session_alert_cancel(s, wl);
                    289:        winlink_stack_remove(&s->lastw, wl);
                    290:        winlink_remove(&s->windows, wl);
1.11      nicm      291:        session_group_synchronize_from(s);
1.1       nicm      292:        if (RB_EMPTY(&s->windows)) {
                    293:                session_destroy(s);
                    294:                return (1);
                    295:        }
                    296:        return (0);
                    297: }
                    298:
                    299: /* Return if session has window. */
                    300: int
                    301: session_has(struct session *s, struct window *w)
                    302: {
                    303:        struct winlink  *wl;
                    304:
                    305:        RB_FOREACH(wl, winlinks, &s->windows) {
                    306:                if (wl->window == w)
                    307:                        return (1);
                    308:        }
                    309:        return (0);
                    310: }
                    311:
                    312: struct winlink *
                    313: session_next_activity(struct session *s, struct winlink *wl)
                    314: {
                    315:        while (wl != NULL) {
                    316:                if (session_alert_has(s, wl, WINDOW_BELL))
                    317:                        break;
                    318:                if (session_alert_has(s, wl, WINDOW_ACTIVITY))
                    319:                        break;
                    320:                if (session_alert_has(s, wl, WINDOW_CONTENT))
                    321:                        break;
1.14    ! nicm      322:                wl = winlink_next(wl);
1.1       nicm      323:        }
                    324:        return (wl);
                    325: }
                    326:
                    327: /* Move session to next window. */
                    328: int
                    329: session_next(struct session *s, int activity)
                    330: {
                    331:        struct winlink  *wl;
                    332:
                    333:        if (s->curw == NULL)
                    334:                return (-1);
                    335:
1.14    ! nicm      336:        wl = winlink_next(s->curw);
1.1       nicm      337:        if (activity)
                    338:                wl = session_next_activity(s, wl);
                    339:        if (wl == NULL) {
                    340:                wl = RB_MIN(winlinks, &s->windows);
                    341:                if (activity && ((wl = session_next_activity(s, wl)) == NULL))
                    342:                        return (-1);
                    343:        }
                    344:        if (wl == s->curw)
                    345:                return (1);
                    346:        winlink_stack_remove(&s->lastw, wl);
                    347:        winlink_stack_push(&s->lastw, s->curw);
                    348:        s->curw = wl;
                    349:        session_alert_cancel(s, wl);
                    350:        return (0);
                    351: }
                    352:
                    353: struct winlink *
                    354: session_previous_activity(struct session *s, struct winlink *wl)
                    355: {
                    356:        while (wl != NULL) {
                    357:                if (session_alert_has(s, wl, WINDOW_BELL))
                    358:                        break;
                    359:                if (session_alert_has(s, wl, WINDOW_ACTIVITY))
                    360:                        break;
                    361:                if (session_alert_has(s, wl, WINDOW_CONTENT))
                    362:                        break;
1.14    ! nicm      363:                wl = winlink_previous(wl);
1.1       nicm      364:        }
                    365:        return (wl);
                    366: }
                    367:
                    368: /* Move session to previous window. */
                    369: int
                    370: session_previous(struct session *s, int activity)
                    371: {
                    372:        struct winlink  *wl;
                    373:
                    374:        if (s->curw == NULL)
                    375:                return (-1);
                    376:
1.14    ! nicm      377:        wl = winlink_previous(s->curw);
1.1       nicm      378:        if (activity)
                    379:                wl = session_previous_activity(s, wl);
                    380:        if (wl == NULL) {
                    381:                wl = RB_MAX(winlinks, &s->windows);
                    382:                if (activity && (wl = session_previous_activity(s, wl)) == NULL)
                    383:                        return (-1);
                    384:        }
                    385:        if (wl == s->curw)
                    386:                return (1);
                    387:        winlink_stack_remove(&s->lastw, wl);
                    388:        winlink_stack_push(&s->lastw, s->curw);
                    389:        s->curw = wl;
                    390:        session_alert_cancel(s, wl);
                    391:        return (0);
                    392: }
                    393:
                    394: /* Move session to specific window. */
                    395: int
                    396: session_select(struct session *s, int idx)
                    397: {
                    398:        struct winlink  *wl;
                    399:
                    400:        wl = winlink_find_by_index(&s->windows, idx);
                    401:        if (wl == NULL)
                    402:                return (-1);
                    403:        if (wl == s->curw)
                    404:                return (1);
                    405:        winlink_stack_remove(&s->lastw, wl);
                    406:        winlink_stack_push(&s->lastw, s->curw);
                    407:        s->curw = wl;
                    408:        session_alert_cancel(s, wl);
                    409:        return (0);
                    410: }
                    411:
                    412: /* Move session to last used window. */
                    413: int
                    414: session_last(struct session *s)
                    415: {
                    416:        struct winlink  *wl;
                    417:
1.11      nicm      418:        wl = TAILQ_FIRST(&s->lastw);
1.1       nicm      419:        if (wl == NULL)
                    420:                return (-1);
                    421:        if (wl == s->curw)
                    422:                return (1);
                    423:
                    424:        winlink_stack_remove(&s->lastw, wl);
                    425:        winlink_stack_push(&s->lastw, s->curw);
                    426:        s->curw = wl;
                    427:        session_alert_cancel(s, wl);
                    428:        return (0);
1.11      nicm      429: }
                    430:
                    431: /* Find the session group containing a session. */
                    432: struct session_group *
                    433: session_group_find(struct session *target)
                    434: {
                    435:        struct session_group    *sg;
                    436:        struct session          *s;
                    437:
                    438:        TAILQ_FOREACH(sg, &session_groups, entry) {
                    439:                TAILQ_FOREACH(s, &sg->sessions, gentry) {
                    440:                        if (s == target)
                    441:                                return (sg);
                    442:                }
                    443:        }
                    444:        return (NULL);
                    445: }
                    446:
                    447: /* Find session group index. */
                    448: u_int
                    449: session_group_index(struct session_group *sg)
                    450: {
                    451:        struct session_group   *sg2;
                    452:        u_int                   i;
                    453:
                    454:        i = 0;
                    455:        TAILQ_FOREACH(sg2, &session_groups, entry) {
                    456:                if (sg == sg2)
                    457:                        return (i);
                    458:                i++;
                    459:        }
                    460:
                    461:        fatalx("session group not found");
                    462: }
                    463:
                    464: /*
                    465:  * Add a session to the session group containing target, creating it if
                    466:  * necessary.
                    467:  */
                    468: void
                    469: session_group_add(struct session *target, struct session *s)
                    470: {
                    471:        struct session_group    *sg;
                    472:
                    473:        if ((sg = session_group_find(target)) == NULL) {
                    474:                sg = xmalloc(sizeof *sg);
                    475:                TAILQ_INSERT_TAIL(&session_groups, sg, entry);
                    476:                TAILQ_INIT(&sg->sessions);
                    477:                TAILQ_INSERT_TAIL(&sg->sessions, target, gentry);
                    478:        }
                    479:        TAILQ_INSERT_TAIL(&sg->sessions, s, gentry);
                    480: }
                    481:
                    482: /* Remove a session from its group and destroy the group if empty. */
                    483: void
                    484: session_group_remove(struct session *s)
                    485: {
                    486:        struct session_group    *sg;
                    487:
                    488:        if ((sg = session_group_find(s)) == NULL)
                    489:                return;
                    490:        TAILQ_REMOVE(&sg->sessions, s, gentry);
                    491:        if (TAILQ_NEXT(TAILQ_FIRST(&sg->sessions), gentry) == NULL)
                    492:                TAILQ_REMOVE(&sg->sessions, TAILQ_FIRST(&sg->sessions), gentry);
                    493:        if (TAILQ_EMPTY(&sg->sessions)) {
                    494:                TAILQ_REMOVE(&session_groups, sg, entry);
                    495:                xfree(sg);
                    496:        }
                    497: }
                    498:
                    499: /* Synchronize a session to its session group. */
                    500: void
                    501: session_group_synchronize_to(struct session *s)
                    502: {
                    503:        struct session_group    *sg;
                    504:        struct session          *target;
                    505:
                    506:        if ((sg = session_group_find(s)) == NULL)
                    507:                return;
                    508:
                    509:        target = NULL;
                    510:        TAILQ_FOREACH(target, &sg->sessions, gentry) {
                    511:                if (target != s)
                    512:                        break;
                    513:        }
                    514:        session_group_synchronize1(target, s);
                    515: }
                    516:
                    517: /* Synchronize a session group to a session. */
                    518: void
                    519: session_group_synchronize_from(struct session *target)
                    520: {
                    521:        struct session_group    *sg;
                    522:        struct session          *s;
                    523:
                    524:        if ((sg = session_group_find(target)) == NULL)
                    525:                return;
                    526:
                    527:        TAILQ_FOREACH(s, &sg->sessions, gentry) {
                    528:                if (s != target)
                    529:                        session_group_synchronize1(target, s);
                    530:        }
                    531: }
                    532:
                    533: /*
                    534:  * Synchronize a session with a target session. This means destroying all
                    535:  * winlinks then recreating them, then updating the current window, last window
                    536:  * stack and alerts.
                    537:  */
                    538: void
                    539: session_group_synchronize1(struct session *target, struct session *s)
                    540: {
                    541:        struct winlinks          old_windows, *ww;
                    542:        struct winlink_stack     old_lastw;
                    543:        struct winlink          *wl, *wl2;
                    544:        struct session_alert    *sa;
                    545:
                    546:        /* Don't do anything if the session is empty (it'll be destroyed). */
                    547:        ww = &target->windows;
                    548:        if (RB_EMPTY(ww))
                    549:                return;
                    550:
                    551:        /* If the current window has vanished, move to the next now. */
                    552:        if (s->curw != NULL) {
                    553:                while (winlink_find_by_index(ww, s->curw->idx) == NULL)
                    554:                        session_next(s, 0);
                    555:        }
                    556:
                    557:        /* Save the old pointer and reset it. */
                    558:        memcpy(&old_windows, &s->windows, sizeof old_windows);
                    559:        RB_INIT(&s->windows);
                    560:
                    561:        /* Link all the windows from the target. */
                    562:        RB_FOREACH(wl, winlinks, ww)
                    563:                winlink_add(&s->windows, wl->window, wl->idx);
                    564:
                    565:        /* Fix up the current window. */
                    566:        if (s->curw != NULL)
                    567:                s->curw = winlink_find_by_index(&s->windows, s->curw->idx);
                    568:        else
                    569:                s->curw = winlink_find_by_index(&s->windows, target->curw->idx);
                    570:
                    571:        /* Fix up the last window stack. */
                    572:        memcpy(&old_lastw, &s->lastw, sizeof old_lastw);
                    573:        TAILQ_INIT(&s->lastw);
                    574:        TAILQ_FOREACH(wl, &old_lastw, sentry) {
                    575:                wl2 = winlink_find_by_index(&s->windows, wl->idx);
                    576:                if (wl2 != NULL)
                    577:                        TAILQ_INSERT_TAIL(&s->lastw, wl2, sentry);
                    578:        }
                    579:
                    580:        /* And update the alerts list. */
                    581:        SLIST_FOREACH(sa, &s->alerts, entry) {
                    582:                wl = winlink_find_by_index(&s->windows, sa->wl->idx);
                    583:                if (wl == NULL)
                    584:                        session_alert_cancel(s, sa->wl);
                    585:                else
                    586:                        sa->wl = wl;
                    587:        }
                    588:
                    589:        /* Then free the old winlinks list. */
                    590:        while (!RB_EMPTY(&old_windows)) {
                    591:                wl = RB_ROOT(&old_windows);
1.13      nicm      592:                winlink_remove(&old_windows, wl);
1.11      nicm      593:        }
1.1       nicm      594: }