1/** \file
2 *  \brief IDC system test code
3 */
4
5/*
6 * Copyright (c) 2010, ETH Zurich.
7 * All rights reserved.
8 *
9 * This file is distributed under the terms in the attached LICENSE file.
10 * If you do not find this file, copies can be found by writing to:
11 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
12 */
13
14#define _USE_XOPEN /* for strdup() */
15#include <string.h>
16#include <stdio.h>
17#include <barrelfish/barrelfish.h>
18#include <barrelfish/nameservice_client.h>
19#include <barrelfish/debug.h>
20#include <barrelfish/deferred.h>
21#include <if/test_defs.h>
22
23static const char *my_service_name = "idctest";
24
25static const char *longstr = ""
26  "Far out in the uncharted backwaters of the unfashionable end of the\n"
27  "western spiral arm of the Galaxy lies a small unregarded yellow sun.\n"
28  "\n"
29  "Orbiting this at a distance of roughly ninety-two million miles is an\n"
30  "utterly insignificant little blue green planet whose ape- descended life\n"
31  "forms are so amazingly primitive that they still think digital watches\n"
32  "are a pretty neat idea.\n"
33  "\n"
34  "This planet has - or rather had - a problem, which was this: most of the\n"
35  "people on it were unhappy for pretty much of the time. Many solutions\n"
36  "were suggested for this problem, but most of these were largely concerned\n"
37  "with the movements of small green pieces of paper, which is odd because\n"
38  "on the whole it wasn't the small green pieces of paper that were unhappy.\n"
39  "\n"
40  "And so the problem remained; lots of the people were mean, and most\n"
41  "of them were miserable, even the ones with digital watches.\n"
42  "\n"
43  "Many were increasingly of the opinion that they'd all made a big mistake\n"
44  "in coming down from the trees in the first place. And some said that\n"
45  "even the trees had been a bad move, and that no one should ever have\n"
46  "left the oceans.\n"
47  "\n"
48  "And then, one Thursday, nearly two thousand years after one man had\n"
49  "been nailed to a tree for saying how great it would be to be nice to people\n"
50  "for a change, one girl sitting on her own in a small cafe in Rickmansworth\n"
51  "suddenly realized what it was that had been going wrong all this time,\n"
52  "and she finally knew how the world could be made a good and happy\n"
53  "place. This time it was right, it would work, and no one would have to\n"
54  "get nailed to anything.\n"
55  "\n"
56  "Sadly, however, before she could get to a phone to tell anyone- about it,\n"
57  "a terribly stupid catastrophe occurred, and the idea was lost forever.\n"
58  "\n"
59  "This is her story.";
60
61static const char *shortstr = "Hello, world!";
62
63/* ------------------------ COMMON MESSAGE HANDLERS ------------------------ */
64
65static void rx_basic(struct test_binding *b, uint32_t arg)
66{
67    debug_printf("rx_basic %"PRIu32"\n", arg);
68    assert(arg == 7);
69}
70
71static void rx_str(struct test_binding *b, uint32_t arg, const char *s)
72{
73    debug_printf("rx_str %"PRIu32" Str[:5]:'%.5s'\n", arg, s);
74    switch(arg){
75        case 9:
76        case 37:
77            assert(strcmp(shortstr, s) == 0);
78            break;
79        case 42:
80            assert(strcmp(longstr, s) == 0);
81            break;
82        default:
83            assert(!"wrong argument received!");
84    }
85}
86
87static void rx_one_cap(struct test_binding *b, uint32_t arg,
88                    struct capref cap1)
89{
90    char buf1[256];
91    debug_print_cap_at_capref(buf1, sizeof(buf1), cap1);
92    buf1[sizeof(buf1) - 1] = '\0';
93    debug_printf("rx_one_cap %"PRIu32" [%s]\n", arg, buf1);
94}
95
96static void rx_caps(struct test_binding *b, uint32_t arg,
97                    struct capref cap1, struct capref cap2)
98{
99    char buf1[256], buf2[256];
100    debug_print_cap_at_capref(buf1, sizeof(buf1), cap1);
101    debug_print_cap_at_capref(buf2, sizeof(buf2), cap2);
102    buf1[sizeof(buf1) - 1] = '\0';
103    buf2[sizeof(buf2) - 1] = '\0';
104    debug_printf("rx_caps %"PRIu32" [%s] [%s]\n", arg, buf1, buf2);
105}
106
107static void rx_buf(struct test_binding *b, const uint8_t *buf, size_t buflen)
108{
109    debug_printf("rx_buf (%zu bytes)\n", buflen);
110}
111
112static struct test_rx_vtbl rx_vtbl = {
113    .basic = rx_basic,
114    .str = rx_str,
115    .one_cap = rx_one_cap,
116    .caps = rx_caps,
117    .buf = rx_buf,
118};
119
120/* ------------------------------ CLIENT ------------------------------ */
121
122struct client_state {
123    struct test_binding *binding;
124    int nextmsg;
125    char *str;
126    struct capref cap1, cap2, cap3;
127};
128
129// send the next message in our sequence
130static void send_cont(void *arg)
131{
132    struct client_state *myst = arg;
133    struct test_binding *b = myst->binding;
134    struct event_closure txcont = MKCONT(send_cont, myst);
135    errval_t err = SYS_ERR_OK;
136
137    debug_printf("client sending msg %d\n", myst->nextmsg);
138
139    switch(myst->nextmsg) {
140    case 0:
141        err = test_basic__tx(b, txcont, 7);
142        break;
143
144    case 1:
145        // send a static string
146        err = test_str__tx(b, txcont, 9, shortstr);
147        break;
148
149    case 2:
150        // send a "dynamically allocated" string
151        myst->str = strdup(shortstr);
152        err = test_str__tx(b, txcont, 37, myst->str);
153        break;
154
155    case 3:
156        // deallocate the string we sent in the previous message
157        free(myst->str);
158
159        // send a long string
160        err = test_str__tx(b, txcont, 42, longstr);
161        break;
162
163    case 4:
164        err = frame_alloc(&myst->cap1, BASE_PAGE_SIZE, NULL);
165        assert(err_is_ok(err));
166        err = test_one_cap__tx(b, txcont, 77, myst->cap1);
167        break;
168
169    case 5:
170        // create some caps to send (assume it all works)
171        err = frame_alloc(&myst->cap2, BASE_PAGE_SIZE, NULL);
172        assert(err_is_ok(err));
173
174        err = slot_alloc(&myst->cap3);
175        assert(err_is_ok(err));
176
177        err = vnode_create(myst->cap3, ObjType_VNode_x86_64_ptable);
178        assert(err_is_ok(err));
179
180        err = test_caps__tx(b, txcont, 69, myst->cap2, myst->cap3);
181        break;
182
183    case 6:
184        // delete the caps, now they've been sent
185        //err = cap_destroy(myst->cap1);
186        //assert(err_is_ok(err));
187
188        //err = cap_destroy(myst->cap2);
189        //assert(err_is_ok(err));
190
191        // send a "buffer"
192        err = test_buf__tx(b, txcont, (uint8_t *)longstr, strlen(longstr));
193        break;
194
195    case 7:
196        // here is where we would deallocate the buffer, if it wasn't static
197        // client all done is the message determined to terminate the test,
198        // wait a bit to give the server time to print the message.
199        barrelfish_usleep(1000000);
200        printf("client all done!\n");
201        return;
202
203    default:
204        err = LIB_ERR_NOT_IMPLEMENTED; // TODO: Make meaningful
205        assert(!"shouldn't happen");
206    }
207
208    if (err_is_ok(err)) {
209        myst->nextmsg++;
210    } else {
211        DEBUG_ERR(err, "error sending message %d", myst->nextmsg);
212
213        if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
214            assert(!"binding is busy for tx?!");
215
216            // this error should never happen to us, because we serialise all
217            // sends with the continuation, however it may happen in another
218            // situation if the binding is already busy sending. in this case,
219            // the user can use something like:
220
221            struct waitset *ws = get_default_waitset();
222            err = b->register_send(b, ws, txcont);
223            if (err_is_fail(err)) {
224                // note that only one continuation may be registered at a time
225                DEBUG_ERR(err, "register_send on binding failed!");
226            }
227        }
228
229        abort();
230    }
231}
232
233static void bind_cb(void *st, errval_t err, struct test_binding *b)
234{
235    if (err_is_fail(err)) {
236        USER_PANIC_ERR(err, "bind failed");
237    }
238
239    debug_printf("client bound!\n");
240
241    // copy my message receive handler vtable to the binding
242    b->rx_vtbl = rx_vtbl;
243
244    // construct local per-binding state
245    struct client_state *myst = malloc(sizeof(struct client_state));
246    assert(myst != NULL);
247    myst->nextmsg = 0;
248    myst->binding = b;
249    b->st = myst;
250
251    // start sending stuff to the service
252    send_cont(myst);
253}
254
255static void start_client(void)
256{
257    iref_t iref;
258    errval_t err;
259
260    debug_printf("client looking up '%s' in name service...\n", my_service_name);
261    err = nameservice_blocking_lookup(my_service_name, &iref);
262    if (err_is_fail(err)) {
263        USER_PANIC_ERR(err, "nameservice_blocking_lookup failed");
264    }
265
266    debug_printf("client binding to %"PRIuIREF"...\n", iref);
267    err = test_bind(iref, bind_cb, NULL /* state pointer for bind_cb */,
268                    get_default_waitset(), IDC_BIND_FLAGS_DEFAULT);
269    if (err_is_fail(err)) {
270        USER_PANIC_ERR(err, "bind failed");
271    }
272}
273
274/* ------------------------------ SERVER ------------------------------ */
275
276static void export_cb(void *st, errval_t err, iref_t iref)
277{
278    if (err_is_fail(err)) {
279        USER_PANIC_ERR(err, "export failed");
280    }
281
282    debug_printf("service exported at iref %"PRIuIREF"\n", iref);
283
284    // register this iref with the name service
285    err = nameservice_register(my_service_name, iref);
286    if (err_is_fail(err)) {
287        USER_PANIC_ERR(err, "nameservice_register failed");
288    }
289}
290
291static errval_t connect_cb(void *st, struct test_binding *b)
292{
293    debug_printf("service got a connection!\n");
294
295    // copy my message receive handler vtable to the binding
296    b->rx_vtbl = rx_vtbl;
297
298    // accept the connection (we could return an error to refuse it)
299    return SYS_ERR_OK;
300}
301
302static void start_server(void)
303{
304    errval_t err;
305
306    err = test_export(NULL /* state pointer for connect/export callbacks */,
307                      export_cb, connect_cb, get_default_waitset(),
308                      IDC_EXPORT_FLAGS_DEFAULT);
309    if (err_is_fail(err)) {
310        USER_PANIC_ERR(err, "export failed");
311    }
312}
313
314/* ------------------------------ MAIN ------------------------------ */
315
316int main(int argc, char *argv[])
317{
318    errval_t err;
319
320    if (argc == 2 && strcmp(argv[1], "client") == 0) {
321        start_client();
322    } else if (argc == 2 && strcmp(argv[1], "server") == 0) {
323        start_server();
324    } else {
325        debug_printf("Usage: %s client|server\n", argv[0]);
326        return EXIT_FAILURE;
327    }
328
329    struct waitset *ws = get_default_waitset();
330    while (1) {
331        err = event_dispatch(ws);
332        if (err_is_fail(err)) {
333            DEBUG_ERR(err, "in event_dispatch");
334            break;
335        }
336    }
337
338    return EXIT_FAILURE;
339}
340