1/**
2 * \file
3 * \brief Get/Set client API implementation
4 *
5 * This file provides convenience functions to interface with the
6 * octopus.if RPC calls.
7 */
8
9/*
10 * Copyright (c) 2011, 2012, ETH Zurich.
11 * All rights reserved.
12 *
13 * This file is distributed under the terms in the attached LICENSE file.
14 * If you do not find this file, copies can be found by writing to:
15 * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
16 */
17
18#include <stdlib.h>
19#include <stdio.h>
20#include <stdarg.h>
21
22#include <barrelfish/barrelfish.h>
23
24#include <if/octopus_defs.h>
25#include <if/octopus_thc.h>
26
27#include <octopus/init.h>
28#include <octopus/getset.h>
29#include <octopus/parser/ast.h>
30#include <octopus/trigger.h>
31
32#include "common.h"
33
34/**
35 * \brief Retrieve all record names matching a given query.
36 *
37 * \param[out] names Names of all records matching the query.
38 * Needs to be freed by the client (use oct_free_names) in
39 * case of SYS_ERR_OK/LIB_ERR_MALLOC_FAIL.
40 * \param[out] size Number of records matching the query. 0 in case of error.
41 * \param[in] query Query sent to the server
42 * \param ... Parameters used to build query with help of vsprintf.
43 *
44 * \retval SYS_ERR_OK
45 * \retval OCT_ERR_NO_RECORD
46 * \retval OCT_ERR_PARSER_FAIL
47 * \retval OCT_ERR_ENGINE_FAIL
48 * \retval LIB_ERR_MALLOC_FAIL
49 */
50errval_t oct_get_names(char*** names, size_t* len, const char* query, ...)
51{
52    assert(query != NULL);
53
54    errval_t err = SYS_ERR_OK;
55    va_list args;
56
57    char* buf = NULL;
58    *len = 0;
59
60    FORMAT_QUERY(query, args, buf); // buf
61
62    struct octopus_thc_client_binding_t* cl = oct_get_thc_client();
63
64    struct octopus_get_names_response__rx_args reply;
65    err = cl->call_seq.get_names(cl, buf, NOP_TRIGGER, reply.output,
66            &reply.tid, &reply.error_code);
67    if (err_is_ok(err)) {
68        err = reply.error_code;
69    }
70
71    if (err_is_ok(err)) {
72        err = oct_parse_names(reply.output, names, len);
73        //qsort(*names, *len, sizeof(char*), cmpstringp);
74    }
75
76    free(buf);
77    return err;
78}
79
80/**
81 * \brief Gets one record matching the given query.
82 *
83 * \param[out] data Record returned by the server.
84 * \param[in] query The query sent to the server.
85 * \param ... Additional arguments to format the query using vsprintf.
86 *
87 * \retval SYS_ERR_OK
88 * \retval OCT_ERR_NO_RECORD
89 * \retval OCT_ERR_AMBIGOUS_QUERY TODO!
90 * \retval OCT_ERR_PARSER_FAIL
91 * \retval OCT_ERR_ENGINE_FAIL
92 */
93errval_t oct_get(char** data, const char* query, ...)
94{
95    assert(query != NULL);
96    errval_t err = SYS_ERR_OK;
97    va_list args;
98
99    char* buf = NULL;
100    FORMAT_QUERY(query, args, buf);
101
102    struct octopus_thc_client_binding_t* cl = oct_get_thc_client();
103    assert(cl != NULL);
104
105    struct octopus_get_response__rx_args reply;
106    err = cl->call_seq.get(cl, buf, NOP_TRIGGER, reply.output,
107            &reply.tid, &reply.error_code);
108
109    if (err_is_ok(err)) {
110        err = reply.error_code;
111    }
112
113    free(buf);
114
115    if (err_is_fail(err)) {
116        return err;
117    }
118
119    if (data) {
120        *data = strdup(reply.output);
121    }
122
123
124    return err;
125}
126
127/**
128 * \brief Sets a record.
129 *
130 * \param query The record to set.
131 * \param ... Additional arguments to format the query using vsprintf.
132 *
133 * \retval SYS_ERR_OK
134 * \retval OCT_ERR_NO_RECORD_NAME
135 * \retval OCT_ERR_PARSER_FAIL
136 * \retval OCT_ERR_ENGINE_FAIL
137 */
138errval_t oct_set(const char* query, ...)
139{
140    assert(query != NULL);
141    errval_t err = SYS_ERR_OK;
142    va_list args;
143
144    char* buf = NULL;
145    FORMAT_QUERY(query, args, buf);
146
147    // Send to Server
148    struct octopus_thc_client_binding_t* cl = oct_get_thc_client();
149
150    errval_t error_code;
151    err = cl->call_seq.set(cl, buf, SET_DEFAULT, NOP_TRIGGER, false, NULL, NULL,
152                           &error_code);
153
154    if (err_is_ok(err)) {
155        err = error_code;
156    }
157
158    free(buf);
159    return err;
160}
161
162/**
163 * \brief Sets a record.
164 *
165 * \param query The record to set.
166 * \param mode A combination of mode bits (see getset.h).
167 * \param ... Additional arguments to format the query using vsprintf.
168 *
169 * \retval SYS_ERR_OK
170 * \retval OCT_ERR_NO_RECORD_NAME
171 * \retval OCT_ERR_PARSER_FAIL
172 * \retval OCT_ERR_ENGINE_FAIL
173 */
174errval_t oct_mset(oct_mode_t mode, const char* query, ...)
175{
176    assert(query != NULL);
177    errval_t err = SYS_ERR_OK;
178    va_list args;
179
180    char* buf = NULL;
181    FORMAT_QUERY(query, args, buf);
182
183    // Send to Server
184    struct octopus_thc_client_binding_t* cl = oct_get_thc_client();
185
186    errval_t error_code;
187    err = cl->call_seq.set(cl, buf, mode, NOP_TRIGGER, false, NULL, NULL,
188                           &error_code);
189
190    if (err_is_ok(err)) {
191        err = error_code;
192    }
193
194    free(buf);
195    return err;
196}
197
198/**
199 * \brief Sets a record and returns and in case of no error
200 * returns it to the client.
201 *
202 * Additonally the mode how a record is set can be specified.
203 *
204 * \param mode A combination of mode bits (see getset.h).
205 * \param[out] record The new record.
206 * \param[in] query The record to set.
207 * \param ... Additional arguments to format the query using vsprintf.
208 *
209 * \retval SYS_ERR_OK
210 * \retval OCT_ERR_NO_RECORD_NAME
211 * \retval OCT_ERR_PARSER_FAIL
212 * \retval OCT_ERR_ENGINE_FAIL
213 *
214 * TODO maybe remove this function completely and let all use rpc call for set
215 * directly if they want to a non-trivial set?
216 */
217errval_t oct_set_get(oct_mode_t mode, char** record, const char* query, ...)
218{
219    assert(query != NULL);
220    errval_t err = SYS_ERR_OK;
221    va_list args;
222
223    char* buf = NULL;
224    FORMAT_QUERY(query, args, buf);
225
226    // Send to Server
227    struct octopus_thc_client_binding_t* cl = oct_get_thc_client();
228    struct octopus_set_response__rx_args reply;
229    err = cl->call_seq.set(cl, buf, mode, NOP_TRIGGER, true, reply.record,
230                           &reply.tid, &reply.error_code);
231    if (err_is_ok(err)) {
232        err = reply.error_code;
233    }
234
235    free(buf);
236
237    if (err_is_fail(err)) {
238        return err;
239    }
240
241    if (record) {
242        *record = strdup(reply.record);
243    }
244
245    return err;
246}
247
248/**
249 * \brief Gets one record using the ID capability as the key/name.
250 *
251 * \param[out] data Record returned by the server.
252 * \param[in] idcap ID capability used as the key/name of the record.
253 *
254 * \retval SYS_ERR_OK
255 * \retval OCT_ERR_NO_RECORD
256 * \retval OCT_ERR_PARSER_FAIL
257 * \retval OCT_ERR_ENGINE_FAIL
258 */
259errval_t oct_get_with_idcap(char **data, struct capref idcap)
260{
261    assert(!capref_is_null(idcap));
262    errval_t err = SYS_ERR_OK;
263
264    struct octopus_thc_client_binding_t *cl = oct_get_thc_client();
265    assert(cl != NULL);
266    struct octopus_get_with_idcap_response__rx_args reply;
267    err = cl->call_seq.get_with_idcap(cl, idcap, NOP_TRIGGER, reply.output,
268                                      &reply.tid, &reply.error_code);
269
270    if (err_is_ok(err)) {
271        err = reply.error_code;
272    }
273
274    if (err_is_fail(err)) {
275        return err;
276    }
277
278    if (data) {
279        *data = strdup(reply.output);
280    }
281
282    return err;
283}
284
285/**
286 * \brief Sets a record using the ID capability as the name/key of the record.
287 *
288 * \param idcap      ID capability used as the name/key of the record.
289 * \param attributes Attributes of the record.
290 * \param ...        Additional arguments to format the attributes using
291 *                   vsprintf.
292 *
293 * \retval SYS_ERR_OK
294 * \retval OCT_ERR_NO_RECORD_NAME
295 * \retval OCT_ERR_PARSER_FAIL
296 * \retval OCT_ERR_ENGINE_FAIL
297 */
298errval_t oct_set_with_idcap(struct capref idcap, const char *attributes, ...)
299{
300    assert(!capref_is_null(idcap));
301    assert(attributes != NULL);
302    errval_t err = SYS_ERR_OK;
303    va_list args;
304
305    char *buf = NULL;
306    FORMAT_QUERY(attributes, args, buf);
307
308    // Send to Server
309    struct octopus_thc_client_binding_t *cl = oct_get_thc_client();
310
311    errval_t error_code;
312    err = cl->call_seq.set_with_idcap(cl, idcap, buf, SET_DEFAULT, NOP_TRIGGER,
313                                      false, NULL, NULL, &error_code);
314
315    if (err_is_ok(err)) {
316        err = error_code;
317    }
318
319    free(buf);
320    return err;
321}
322
323/**
324 * \brief Deletes all records matching the given query.
325 *
326 * \param query Specifies the record(s) to be deleted.
327 * \param ... Additional arguments to format the query using vsprintf.
328 *
329 * \retval SYS_ERR_OK
330 * \retval OCT_ERR_NO_RECORD
331 * \retval OCT_ERR_NO_RECORD_NAME
332 * \retval OCT_ERR_ENGINE_FAIL
333 * \retval OCT_ERR_PARSER_FAIL
334 *
335 * TODO: Atm only name of record is included in del query on server.
336 */
337errval_t oct_del(const char* query, ...)
338{
339    assert(query != NULL);
340    errval_t err = SYS_ERR_OK;
341    va_list args;
342
343    char* buf = NULL;
344    FORMAT_QUERY(query, args, buf);
345
346    struct octopus_thc_client_binding_t* cl = oct_get_thc_client();
347    errval_t error_code;
348    err = cl->call_seq.del(cl, buf, NOP_TRIGGER, NULL, &error_code);
349    if (err_is_ok(err)) {
350        err = error_code;
351    }
352
353    free(buf);
354    return err;
355}
356
357/**
358 * \brief Checks if a result for a given query exists.
359 *
360 * \param query Results are searched based on the query.
361 * \param ... Additional arguments to format the query using vsprintf.
362 *
363 * \retval SYS_ERR_OK
364 * \retval OCT_ERR_NO_RECORD
365 * \retval OCT_ERR_PARSER_FAIL
366 * \retval OCT_ERR_ENGINE_FAIL
367 **/
368errval_t oct_exists(const char* query, ...)
369{
370    assert(query != NULL);
371    errval_t err = SYS_ERR_OK;
372    va_list args;
373
374    char* buf = NULL;
375    FORMAT_QUERY(query, args, buf);
376
377    struct octopus_thc_client_binding_t* cl = oct_get_thc_client();
378    errval_t error_code;
379    err = cl->call_seq.exists(cl, buf, NOP_TRIGGER, NULL, &error_code);
380    if (err_is_ok(err)) {
381        err = error_code;
382    }
383
384    free(buf);
385    return err;
386}
387
388/**
389 * \brief Waits until a given record exists.
390 *
391 * \param record  Record that matched the query, callee has to free this.
392 * \param query Format of record to wait for.
393 * \param ... Additional arguments to format query.
394 *
395 * \note This call blocks on the octopus RPC waitset if the record is not there yet.
396 *
397 * \retval SYS_ERR_OK
398 */
399errval_t oct_wait_for(char** record, const char *query, ...)
400{
401    assert(query != NULL);
402    errval_t err = SYS_ERR_OK;
403    va_list args;
404
405    char* buf = NULL;
406    FORMAT_QUERY(query, args, buf);
407
408    struct octopus_thc_client_binding_t* cl = oct_get_thc_client();
409
410    struct octopus_wait_for_response__rx_args reply;
411    err = cl->call_seq.wait_for(cl, buf, reply.record, &reply.error_code);
412    if (err_is_fail(err)) {
413        goto out;
414    }
415    err = reply.error_code;
416
417    if (err_is_fail(err)) {
418        goto out;
419    }
420
421    if (record) {
422        *record = strdup(reply.record);
423    }
424
425out:
426    free(buf);
427    return err;
428}
429