1/**
2 * \file
3 * \brief Stubs of Octopus implementation
4 *
5 * Warning: This implementation provides only the very basic functionality
6 * needed by the name service API in libbarrelfish!
7 */
8/*
9 * Copyright (c) 2011, ETH Zurich.
10 * All rights reserved.
11 *
12 * This file is distributed under the terms in the attached LICENSE file.
13 * If you do not find this file, copies can be found by writing to:
14 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
15 */
16
17#include <stdio.h>
18#include <string.h>
19
20#include <pcre.h>
21
22#include <barrelfish/barrelfish.h>
23#include <if/octopus_defs.h>
24
25#include <octopus_server/debug.h>
26#include <octopus_server/query.h>
27#include <octopus/parser/ast.h>
28#include <octopus/definitions.h>
29#include <octopus/getset.h> // for SET_SEQUENTIAL define
30
31#define MAX_RECORDS 1024
32#define RECORD_NAME(ast) ((ast)->u.on.name->u.in.str)
33#define RECORD_NAME_REGEX(ast) ((ast)->u.on.name->u.cnsn.value->u.sn.str)
34
35// cnsn
36
37struct wait_queue;
38struct wait_queue {
39    struct oct_reply_state* ors;
40    struct wait_queue* next;
41};
42
43struct record {
44    char* name;
45    struct ast_object* record;
46    struct wait_queue* waiting_parties;
47};
48
49static struct record record_storage[MAX_RECORDS] = { { NULL, NULL, NULL } };
50
51static void transform_to_string(struct ast_object* ast, char* str)
52{
53    assert(ast != NULL);
54    assert(str != NULL);
55    size_t idx = 0;
56
57    idx += sprintf(str + idx, "%s { ", RECORD_NAME(ast));
58
59    struct ast_object* attr = ast->u.on.attrs;
60    while (attr != NULL) {
61        assert(attr->type == nodeType_Attribute);
62
63        struct ast_object* pair = attr->u.an.attr;
64        assert(pair != NULL);
65        assert(pair->type == nodeType_Pair);
66
67        struct ast_object* left = pair->u.pn.left;
68        struct ast_object* right = pair->u.pn.right;
69        ;assert(left != NULL);
70        assert(right != NULL);
71
72        assert(left->type == nodeType_Ident);
73        idx += sprintf(str + idx, "%s: ", left->u.in.str);
74
75        switch(right->type) {
76            case nodeType_String:
77                idx += sprintf(str + idx, "%s", right->u.sn.str);
78                break;
79            case nodeType_Constant:
80                idx += sprintf(str + idx, "%"PRId64"", right->u.cn.value);
81                break;
82            case nodeType_Ident:
83                idx += sprintf(str + idx, "%s ", right->u.in.str);
84                break;
85            default:
86                USER_PANIC("Unsupported node type: %u\n", right->type);
87        }
88
89        attr = attr->u.an.next;
90        if (attr != NULL) {
91            idx += sprintf(str + idx, ", ");
92        }
93    }
94
95    idx += sprintf(str + idx, " }");
96    str[idx + 1] = '\0';
97
98    OCT_DEBUG("transform to string: %s\n", str);
99}
100
101static void copy_ast(struct ast_object** copy, struct ast_object* ast)
102{
103    if (ast == NULL) {
104        return;
105    }
106
107    *copy = malloc(sizeof(struct ast_object));
108    memcpy(*copy, ast, sizeof(struct ast_object));
109
110    switch (ast->type) {
111    case nodeType_Object:
112        copy_ast(&(*copy)->u.on.name, ast->u.on.name);
113        copy_ast(&(*copy)->u.on.attrs, ast->u.on.attrs);
114        break;
115
116    case nodeType_Attribute:
117        copy_ast(&(*copy)->u.an.attr, ast->u.an.attr);
118        copy_ast(&(*copy)->u.an.next, ast->u.an.next);
119        break;
120
121    case nodeType_Pair:
122        copy_ast(&(*copy)->u.pn.left, ast->u.pn.left);
123        copy_ast(&(*copy)->u.pn.right, ast->u.pn.right);
124        break;
125
126    case nodeType_Ident:
127        (*copy)->u.in.str = strdup(ast->u.in.str);
128        break;
129
130    case nodeType_String:
131        (*copy)->u.sn.str = strdup(ast->u.sn.str);
132        break;
133
134    case nodeType_Constant:
135        // Nothing to copy
136        break;
137
138    default:
139        OCT_DEBUG("node is: %d\n", ast->type);
140        assert(!"Unsupported Node!");
141        break;
142    }
143}
144
145errval_t get_record(struct ast_object* ast, struct oct_query_state* sqs)
146{
147    OCT_DEBUG("get record %s\n", RECORD_NAME(ast));
148    assert(ast != NULL);
149    assert(sqs != NULL);
150
151    for (size_t i = 0; i < MAX_RECORDS; i++) {
152        struct record* entry = &record_storage[i];
153        if (entry->name == NULL) {
154            continue;
155        }
156
157        if (strcmp(RECORD_NAME(ast), entry->name) == 0 && entry->record != NULL) {
158            transform_to_string(entry->record, sqs->std_out.buffer);
159            return SYS_ERR_OK;
160        }
161    }
162
163    return OCT_ERR_NO_RECORD;
164}
165
166static void wakeup_clients(struct record* entry)
167{
168    struct wait_queue* cur = entry->waiting_parties;
169
170    // Walk through list, wake up all clients
171    while (cur != NULL) {
172        assert(cur->ors != NULL);
173        assert(cur->ors->reply != NULL);
174        assert(cur->ors->binding != NULL);
175        transform_to_string(entry->record,
176                cur->ors->query_state.std_out.buffer);
177        OCT_DEBUG(
178                "wakeup %p for %s\n", cur->ors->binding, cur->ors->query_state.std_out.buffer);
179        cur->ors->reply(cur->ors->binding, cur->ors);
180
181        struct wait_queue* next = cur->next;
182        free(cur);
183        cur = next;
184    }
185
186    entry->waiting_parties = NULL;
187}
188
189errval_t set_record(struct ast_object* ast, uint64_t mode,
190        struct oct_query_state* sqs)
191{
192    assert(ast != NULL);
193    assert(sqs != NULL);
194    assert(mode == 0);
195
196    for (size_t i = 0; i < MAX_RECORDS; i++) {
197        struct record* entry = &record_storage[i];
198        if (entry->name == NULL) {
199            continue;
200        }
201
202        OCT_DEBUG("found record: %s\n", entry->name);
203        if (strcmp(RECORD_NAME(ast), entry->name) == 0) {
204            assert(entry->record == NULL);
205            copy_ast(&entry->record, ast);
206
207            wakeup_clients(entry);
208            return SYS_ERR_OK;
209        }
210    }
211
212    for (size_t i = 0; i < MAX_RECORDS; i++) {
213        struct record* entry = &record_storage[i];
214        if (entry->name == NULL) {
215            entry->name = strdup(RECORD_NAME(ast));
216            copy_ast(&entry->record, ast);
217
218            assert(entry->waiting_parties == NULL);
219            return SYS_ERR_OK;
220        }
221    }
222
223    assert(!"No more storage space!");
224    return OCT_ERR_NO_RECORD;
225}
226
227errval_t del_record(struct ast_object* ast, struct oct_query_state* sqs)
228{
229    assert(ast != NULL);
230    assert(sqs != NULL);
231
232    for (size_t i = 0; i < MAX_RECORDS; i++) {
233        struct record* entry = &record_storage[i];
234        if (entry->name == NULL) {
235            continue;
236        }
237
238        if (strcmp(RECORD_NAME(ast), entry->name) == 0) {
239            assert(entry->record != NULL);
240
241            free(entry->name);
242            entry->name = NULL;
243            free_ast(entry->record);
244            entry->record = NULL;
245
246            // Free the waiting list, this should be NULL anyways I guess
247            struct wait_queue* cur = entry->waiting_parties;
248            while (cur != NULL) {
249                struct wait_queue* next = cur->next;
250                free(cur);
251                cur = next;
252            }
253            entry->waiting_parties = NULL;
254
255            return SYS_ERR_OK;
256        }
257    }
258
259    return OCT_ERR_NO_RECORD;
260}
261
262/**
263 * TODO: this can only regex match on record name at the moment.
264 */
265errval_t get_record_names(struct ast_object* ast, struct oct_query_state* sqs)
266{
267    const char* errptr = NULL;
268    int erroffset = 0;
269    unsigned char tableptr;
270
271    char** names = calloc(sizeof(char*), MAX_RECORDS);
272    size_t names_cur = 0;
273
274    OCT_DEBUG("%s:%s:%d: About to pcre_compile: %s\n",
275           __FILE__, __FUNCTION__, __LINE__, RECORD_NAME_REGEX(ast));
276    pcre* reg = pcre_compile(RECORD_NAME_REGEX(ast), 0, &errptr, &erroffset, &tableptr);
277    if (reg == NULL) {
278        OCT_DEBUG("Failed to compile the regex errptr = %s erroroffset = %d",
279                  errptr, erroffset);
280        return OCT_ERR_PARSER_FAIL;
281    }
282
283    for (size_t i = 0; i < MAX_RECORDS; i++) {
284        struct record* entry = &record_storage[i];
285        if (entry->name == NULL) {
286            continue;
287        }
288
289        int results[1];
290        int rc = pcre_exec(reg, NULL, entry->name, strlen(entry->name),
291                           0, 0, results, 1);
292        if (rc == 0) {
293            OCT_DEBUG("%s:%s:%d: We have a match\n",
294                      __FILE__, __FUNCTION__, __LINE__);
295            names[names_cur++] = entry->name;
296        }
297        else if (rc < 0) {
298            OCT_DEBUG("%s:%s:%d: pcre_exec failed matching %s with rc = %d.\n",
299                      __FILE__, __FUNCTION__, __LINE__, entry->name, rc);
300        }
301    }
302    if (names_cur == 0) {
303        return OCT_ERR_NO_RECORD;
304    }
305
306    // Form a comma separated string to send back over flounder
307    names_cur = 0;
308    int max_bytes = MAX_QUERY_LENGTH;
309    sqs->std_out.buffer[0] = '\0';
310
311    while(names[names_cur] != NULL) {
312        strncat(sqs->std_out.buffer, names[names_cur], max_bytes);
313        max_bytes -= strlen(names[names_cur]);
314        names_cur++;
315        if (names[names_cur] != NULL) {
316            strncat(sqs->std_out.buffer, ",", max_bytes--);
317        }
318        assert(max_bytes > 0);
319    }
320
321    free(names);
322    return SYS_ERR_OK;
323}
324
325errval_t set_watch(struct octopus_binding* b, struct ast_object* ast,
326        uint64_t mode, struct oct_reply_state* drs, uint64_t* wid)
327{
328    OCT_DEBUG("set_watch %s\n", RECORD_NAME(ast));
329
330    assert(ast != NULL);
331    assert(mode == OCT_ON_SET);
332
333    struct record* entry = NULL;
334    for (size_t i = 0; i < MAX_RECORDS; i++) {
335        entry = &record_storage[i];
336        if (entry->name == NULL) {
337            continue;
338        }
339
340        if (strcmp(RECORD_NAME(ast), entry->name) == 0) {
341            goto insert;
342        }
343    }
344    for (size_t i = 0; i < MAX_RECORDS; i++) {
345        entry = &record_storage[i];
346        if (entry->name == NULL) {
347            entry->name = strdup(RECORD_NAME(ast));
348            entry->record = NULL;
349            entry->waiting_parties = NULL;
350            goto insert;
351        }
352    }assert(!"Out of record space.");
353
354    insert: if (strcmp(RECORD_NAME(ast), entry->name) == 0) {
355        assert(entry->record == NULL);
356        struct wait_queue* wq = malloc(sizeof(struct wait_queue));
357        wq->next = NULL;
358        wq->ors = drs;
359
360        // Insert wq into waiting_parties of entry
361        struct wait_queue** cur = &entry->waiting_parties;
362        for (; *cur != NULL; cur = &(*cur)->next) {
363            // Walk to the end of the list
364        }
365        *cur = wq;
366    }
367
368    *wid = 1;
369    return SYS_ERR_OK;
370}
371
372errval_t del_watch(struct octopus_binding* b, octopus_trigger_id_t id,
373        struct oct_query_state* dqs)
374{
375    assert(!"NYI");
376    return OCT_ERR_INVALID_ID;
377}
378
379struct octopus_binding* get_event_binding(struct octopus_binding* b)
380{
381    assert(!"NYI");
382    return b;
383}
384
385errval_t add_subscription(struct octopus_binding* b, struct ast_object* ast,
386        uint64_t trigger_fn, uint64_t state, struct oct_reply_state* drs)
387{
388    assert(!"NYI");
389    return OCT_ERR_NO_SUBSCRIPTION;
390}
391
392errval_t del_subscription(struct octopus_binding* b, uint64_t id,
393        struct oct_query_state* sqs)
394{
395    assert(!"NYI");
396    return OCT_ERR_NO_SUBSCRIPTION;
397}
398
399errval_t find_subscribers(struct ast_object* ast, struct oct_query_state* sqs)
400{
401    assert(!"NYI");
402    return OCT_ERR_NO_SUBSCRIBERS;
403}
404
405errval_t set_binding(octopus_binding_type_t type, uint64_t id, void* binding)
406{
407    return SYS_ERR_OK;
408}
409