1/*
2 * Copyright (c) 2012, ETH Zurich.
3 * All rights reserved.
4 *
5 * This file is distributed under the terms in the attached LICENSE file.
6 * If you do not find this file, copies can be found by writing to:
7 * ETH Zurich D-INFK, CAB F.78, Universitaetstr. 6, CH-8092 Zurich,
8 * Attn: Systems Group.
9 */
10
11#include <assert.h>
12#include <stdbool.h>
13#include <stdio.h>
14
15#include <angler/angler.h>
16#include <barrelfish/barrelfish.h>
17#include <barrelfish/domain.h>
18#include <barrelfish/nameservice_client.h>
19#include <barrelfish/waitset.h>
20#include <if/monitor_defs.h>
21#include <if/terminal_session_defs.h>
22#include <if/terminal_session_defs.h>
23#include <if/octopus_defs.h>
24#include <octopus/getset.h> /* For SET_DEFAULT */
25#include <octopus/trigger.h> /* For NOP_TRIGGER */
26
27/**
28 * Format of the session record stored at octopus.
29 */
30#define SESSION_OCT_RECORD "{session_iref: %" PRIuIREF ", in_iref: %" PRIuIREF \
31                           ", out_iref: %" PRIuIREF ", conf_iref: %" PRIuIREF  \
32                           "}"
33
34/* internal state */
35struct _angler_state {
36    errval_t err;
37    struct terminal_session_binding *binding;
38};
39
40/* internal functions */
41static void bind_cb(void *st, errval_t err, struct terminal_session_binding *b);
42static errval_t store_session_state(struct capref *session_id,
43                                    iref_t session_iref, iref_t in_iref,
44                                    iref_t out_iref, iref_t conf_iref);
45
46/**
47 * \brief Start a new session.
48 *
49 * \param terminal   Terminal used for the session.
50 * \param session_id ID capability representing the session. Filled-in by
51 *                   function.
52 */
53errval_t angler_new_session(char *terminal, struct capref *session_id)
54{
55    errval_t err;
56    iref_t iref;
57
58    assert(terminal != NULL);
59    assert(session_id != NULL);
60
61    /* Lookup terminal session iref at nameservice. */
62    err = nameservice_blocking_lookup(terminal, &iref);
63    if (err_is_fail(err)) {
64        return err_push(err, ANGLER_ERR_LOOKUP_TERMINAL);
65    }
66
67    return angler_new_session_with_iref(iref, session_id);
68}
69
70/**
71 * \brief Start a new session.
72 *
73 * \param session_iref Interface reference of session interface of terminal that
74 *                     is used for the session.
75 * \param session_id   ID capability representing the session. Filled-in by
76 *                     function.
77 */
78errval_t angler_new_session_with_iref(iref_t session_iref,
79                                      struct capref *session_id)
80{
81    errval_t err = SYS_ERR_OK;
82    errval_t error = SYS_ERR_OK;
83    struct _angler_state *state = NULL;
84    struct waitset ws;
85    iref_t in_iref;
86    iref_t out_iref;
87    iref_t conf_iref;
88
89    assert(session_id != NULL);
90
91    /* Create ID capability used to represent the session. */
92    err = idcap_alloc(session_id);
93    if (err_is_fail(err)) {
94        return err_push(err, ANGLER_ERR_CREATE_SESSIONID);
95    }
96
97    /* Initialize internal state */
98    state = malloc(sizeof(struct _angler_state));
99    assert(state != NULL);
100    state->err = SYS_ERR_OK;
101    state->binding = NULL;
102
103    /* Bind to terminal session interface */
104    waitset_init(&ws);
105    err = terminal_session_bind(session_iref, bind_cb, state, &ws,
106                                IDC_BIND_FLAG_RPC_CAP_TRANSFER);
107    if (err_is_fail(err)) {
108        err = err_push(err, ANGLER_ERR_BIND_TERMINAL);
109        goto out;
110    }
111
112    /* Wait on the monitor until the bind completes. */
113    struct monitor_binding *monitor_b = get_monitor_binding();
114    struct waitset *monitor_ws = monitor_b->waitset;
115    while (state->binding == NULL) {
116        err = event_dispatch(monitor_ws);
117        if (err_is_fail(err) || err_is_fail(state->err)) {
118            err = err_push(err, ANGLER_ERR_BIND_TERMINAL);
119            goto out;
120        }
121    }
122
123    /* Initialize rpc client. */
124    terminal_session_rpc_client_init(state->binding);
125
126    /* Associate session with terminal. */
127    err = state->binding->rpc_tx_vtbl.session_associate_with_terminal
128            (state->binding, *session_id, &in_iref, &out_iref, &conf_iref, &error);
129    if (err_is_fail(err)) {
130        err = err_push(err, ANGLER_ERR_ASSOCIATE_WITH_TERMINAL);
131        goto out;
132    }
133    if (err_is_fail(error)) {
134        err = error;
135        err = err_push(err, ANGLER_ERR_ASSOCIATE_WITH_TERMINAL);
136        goto out;
137    }
138
139    /* Store session state at octopus using session id as access control. */
140    err = store_session_state(session_id, session_iref, in_iref, out_iref,
141                              conf_iref);
142    if (err_is_fail(err)) {
143        err = err_push(err, ANGLER_ERR_STORE_SESSION_STATE);
144    }
145
146out:
147    free(state);
148    return err;
149}
150
151/**
152 * \privatesection
153 * Internal functions and bind code follow.
154 */
155static void bind_cb(void *st, errval_t err, struct terminal_session_binding *b)
156{
157    struct _angler_state *state = st;
158
159    if (err_is_fail(err)) {
160        state->err = err;
161    } else {
162        state->binding = b;
163    }
164}
165
166static errval_t store_session_state(struct capref *session_id,
167                                    iref_t session_iref, iref_t in_iref,
168                                    iref_t out_iref, iref_t conf_iref)
169{
170    errval_t err = SYS_ERR_OK;
171    errval_t error;
172    struct octopus_binding *rpc_client = NULL;
173    char *attributes = NULL;
174    size_t attributes_len = 0;
175    octopus_trigger_id_t tid;
176
177    rpc_client = get_octopus_binding();
178    assert(rpc_client != NULL);
179
180    /* Build attributes. */
181    attributes_len = snprintf(NULL, 0, SESSION_OCT_RECORD, session_iref,
182                              in_iref, out_iref, conf_iref);
183    attributes = malloc(attributes_len + 1);
184    assert(attributes != NULL);
185    snprintf(attributes, attributes_len + 1, SESSION_OCT_RECORD, session_iref,
186             in_iref, out_iref, conf_iref);
187
188
189    /* Store record at octopus. */
190    err = rpc_client->rpc_tx_vtbl.set_with_idcap(rpc_client, *session_id, attributes,
191                                           SET_DEFAULT, NOP_TRIGGER, false,
192                                           NULL, &tid, &error);
193    if (err_is_fail(err)) {
194        goto out;
195    }
196    if (err_is_fail(error)) {
197        err = error;
198        goto out;
199    }
200
201out:
202    free(attributes);
203    return err;
204}
205