Coverage Report

Created: 2023-06-07 06:21

/src/h2o/deps/libyrmcds/send.c
Line
Count
Source (jump to first uncovered line)
1
// (C) 2013-2016 Cybozu et al.
2
3
#include "yrmcds.h"
4
#include "yrmcds_portability.h"
5
#include "yrmcds_text.h"
6
7
#ifdef LIBYRMCDS_USE_LZ4
8
#  include "lz4/lib/lz4.h"
9
#endif
10
11
#include <errno.h>
12
#include <stdlib.h>
13
#include <string.h>
14
#include <sys/socket.h>
15
#include <sys/types.h>
16
#include <sys/uio.h>
17
18
static const size_t BINARY_HEADER_SIZE = 24;
19
static const size_t MAX_DATA_SIZE = ((size_t)1) << 30;
20
21
0
static inline void hton64(uint64_t i, char* p) {
22
0
    uint64_t n = htobe64(i);
23
0
    memcpy(p, &n, sizeof(n));
24
0
}
25
26
0
static inline void hton32(uint32_t i, char* p) {
27
0
    uint32_t n = htobe32(i);
28
0
    memcpy(p, &n, sizeof(n));
29
0
}
30
31
0
static inline void hton16(uint16_t i, char* p) {
32
0
    uint16_t n = htobe16(i);
33
0
    memcpy(p, &n, sizeof(n));
34
0
}
35
36
static yrmcds_error send_command(
37
    yrmcds* c, yrmcds_command cmd, uint64_t cas, uint32_t* serial,
38
    size_t key_len, const char* key,
39
    size_t extras_len, const char* extras,
40
0
    size_t data_len, const char* data) {
41
0
    if( cmd >= YRMCDS_CMD_BOTTOM ||
42
0
        key_len > 65535 || extras_len > 127 || data_len > MAX_DATA_SIZE ||
43
0
        (key_len != 0 && key == NULL) ||
44
0
        (extras_len != 0 && extras == NULL) ||
45
0
        (data_len != 0 && data == NULL) )
46
0
        return YRMCDS_BAD_ARGUMENT;
47
48
0
    char h[BINARY_HEADER_SIZE];
49
0
    memset(h, 0, sizeof(h));
50
0
    h[0] = '\x80';
51
0
    h[1] = (char)cmd;
52
0
    hton16((uint16_t)key_len, &h[2]);
53
0
    h[4] = (char)extras_len;
54
0
    size_t total_len = (key_len + extras_len + data_len);
55
0
    hton32((uint32_t)total_len, &h[8]);
56
0
    hton64(cas, &h[16]);
57
58
0
#ifndef LIBYRMCDS_NO_INTERNAL_LOCK
59
0
    int e = pthread_mutex_lock(&c->lock);
60
0
    if( e != 0 ) {
61
0
        errno = e;
62
0
        return YRMCDS_SYSTEM_ERROR;
63
0
    }
64
0
#endif // ! LIBYRMCDS_NO_INTERNAL_LOCK
65
66
0
    yrmcds_error ret = YRMCDS_OK;
67
0
    c->serial = c->serial + 1;
68
0
    memcpy(&h[12], &c->serial, 4);
69
0
    if( serial != NULL )
70
0
        *serial = c->serial;
71
72
0
    struct iovec iov[4];
73
0
    int iovcnt = 1;
74
0
    iov[0].iov_base = h;
75
0
    iov[0].iov_len = sizeof(h);
76
77
0
    if( extras_len > 0 ) {
78
0
        iov[iovcnt].iov_base = (void*)extras;
79
0
        iov[iovcnt].iov_len = extras_len;
80
0
        iovcnt++;
81
0
    }
82
0
    if( key_len > 0 ) {
83
0
        iov[iovcnt].iov_base = (void*)key;
84
0
        iov[iovcnt].iov_len = key_len;
85
0
        iovcnt++;
86
0
    }
87
0
    if( data_len > 0 ) {
88
0
        iov[iovcnt].iov_base = (void*)data;
89
0
        iov[iovcnt].iov_len = data_len;
90
0
        iovcnt++;
91
0
    }
92
93
0
    while( iovcnt > 0 ) {
94
0
        ssize_t n = writev(c->sock, iov, iovcnt);
95
0
        size_t n2 = (size_t)n;
96
0
        if( n == -1 ) {
97
0
            if( errno == EINTR ) continue;
98
0
            ret = YRMCDS_SYSTEM_ERROR;
99
0
            goto OUT;
100
0
        }
101
0
        while( n2 > 0 ) {
102
0
            if( n2 < iov[0].iov_len ) {
103
0
                iov[0].iov_base = (char*)iov[0].iov_base + n2;
104
0
                iov[0].iov_len -= n2;
105
0
                break;
106
0
            }
107
0
            n2 -= iov[0].iov_len;
108
0
            iovcnt --;
109
0
            if( iovcnt == 0 )
110
0
                break;
111
112
0
            int i;
113
0
            for( i = 0; i < iovcnt; ++i )
114
0
                iov[i] = iov[i+1];
115
0
        }
116
0
    }
117
118
0
  OUT:
119
0
#ifndef LIBYRMCDS_NO_INTERNAL_LOCK
120
0
    pthread_mutex_unlock(&c->lock);
121
0
#endif
122
0
    return ret;
123
0
}
124
125
static yrmcds_error send_data(
126
    yrmcds* c, yrmcds_command cmd, const char* key, size_t key_len,
127
    const char* data, size_t data_len, uint32_t flags, uint32_t expire,
128
0
    uint64_t cas, uint32_t* serial) {
129
0
    if( c == NULL || key == NULL || key_len == 0 ||
130
0
        data == NULL || data_len == 0 )
131
0
        return YRMCDS_BAD_ARGUMENT;
132
133
0
    int compressed = 0;
134
#ifdef LIBYRMCDS_USE_LZ4
135
    if( (c->compress_size > 0) && (data_len > c->compress_size) ) {
136
        if( flags & YRMCDS_FLAG_COMPRESS )
137
            return YRMCDS_BAD_ARGUMENT;
138
139
        size_t bound = (size_t)LZ4_compressBound((int)data_len);
140
        char* new_data = (char*)malloc(bound + sizeof(uint32_t));
141
        if( new_data == NULL )
142
            return YRMCDS_OUT_OF_MEMORY;
143
        uint32_t new_size =
144
            (uint32_t)LZ4_compress(data, new_data + sizeof(uint32_t),
145
                                   (int)data_len);
146
        if( new_size == 0 ) {
147
            free(new_data);
148
            return YRMCDS_COMPRESS_FAILED;
149
        }
150
        hton32((uint32_t)data_len, new_data);
151
        flags |= YRMCDS_FLAG_COMPRESS;
152
        data_len = sizeof(uint32_t) + new_size;
153
        data = new_data;
154
        compressed = 1;
155
    }
156
#endif // LIBYRMCDS_USE_LZ4
157
158
0
    char extras[8];
159
0
    hton32(flags, extras);
160
0
    hton32(expire, &extras[4]);
161
0
    yrmcds_error e = send_command(c, cmd, cas, serial, key_len, key,
162
0
                                  sizeof(extras), extras, data_len, data);
163
0
    if( compressed )
164
0
        free((void*)data);
165
0
    return e;
166
0
}
167
168
0
yrmcds_error yrmcds_noop(yrmcds* c, uint32_t* serial) {
169
0
    if( c == NULL )
170
0
        return YRMCDS_BAD_ARGUMENT;
171
172
0
    if( c->text_mode )
173
0
        return YRMCDS_NOT_IMPLEMENTED;
174
175
0
    return send_command(c, YRMCDS_CMD_NOOP, 0, serial,
176
0
                        0, NULL, 0, NULL, 0, NULL);
177
0
}
178
179
yrmcds_error yrmcds_get(yrmcds* c, const char* key, size_t key_len,
180
0
                        int quiet, uint32_t* serial) {
181
0
    if( c == NULL || key == NULL || key_len == 0 )
182
0
        return YRMCDS_BAD_ARGUMENT;
183
184
0
    if( c->text_mode )
185
0
        return yrmcds_text_get(c, key, key_len, quiet, serial);
186
187
0
    return send_command(c, quiet ? YRMCDS_CMD_GETQ : YRMCDS_CMD_GET,
188
0
                        0, serial, key_len, key, 0, NULL, 0, NULL);
189
0
}
190
191
yrmcds_error yrmcds_getk(yrmcds* c, const char* key, size_t key_len,
192
0
                         int quiet, uint32_t* serial) {
193
0
    if( c == NULL || key == NULL || key_len == 0 )
194
0
        return YRMCDS_BAD_ARGUMENT;
195
196
0
    if( c->text_mode )
197
0
        return yrmcds_text_get(c, key, key_len, quiet, serial);
198
199
0
    return send_command(c, quiet ? YRMCDS_CMD_GETKQ : YRMCDS_CMD_GETK,
200
0
                        0, serial, key_len, key, 0, NULL, 0, NULL);
201
0
}
202
203
yrmcds_error yrmcds_get_touch(yrmcds* c, const char* key, size_t key_len,
204
0
                              uint32_t expire, int quiet, uint32_t* serial) {
205
0
    if( c == NULL || key == NULL || key_len == 0 )
206
0
        return YRMCDS_BAD_ARGUMENT;
207
208
0
    if( c->text_mode )
209
0
        return YRMCDS_NOT_IMPLEMENTED;
210
211
0
    char extras[4];
212
0
    hton32(expire, extras);
213
0
    return send_command(c, quiet ? YRMCDS_CMD_GATQ : YRMCDS_CMD_GAT,
214
0
                        0, serial, key_len, key,
215
0
                        sizeof(extras), extras, 0, NULL);
216
0
}
217
218
yrmcds_error yrmcds_getk_touch(yrmcds* c, const char* key, size_t key_len,
219
0
                               uint32_t expire, int quiet, uint32_t* serial) {
220
0
    if( c == NULL || key == NULL || key_len == 0 )
221
0
        return YRMCDS_BAD_ARGUMENT;
222
223
0
    if( c->text_mode )
224
0
        return YRMCDS_NOT_IMPLEMENTED;
225
226
0
    char extras[4];
227
0
    hton32(expire, extras);
228
0
    return send_command(c, quiet ? YRMCDS_CMD_GATKQ : YRMCDS_CMD_GATK,
229
0
                        0, serial, key_len, key,
230
0
                        sizeof(extras), extras, 0, NULL);
231
0
}
232
233
yrmcds_error yrmcds_lock_get(yrmcds* c, const char* key, size_t key_len,
234
0
                             int quiet, uint32_t* serial) {
235
0
    if( c == NULL || key == NULL || key_len == 0 )
236
0
        return YRMCDS_BAD_ARGUMENT;
237
238
0
    if( c->text_mode )
239
0
        return YRMCDS_NOT_IMPLEMENTED;
240
241
0
    return send_command(c, quiet ? YRMCDS_CMD_LAGQ : YRMCDS_CMD_LAG,
242
0
                        0, serial, key_len, key, 0, NULL, 0, NULL);
243
0
}
244
245
yrmcds_error yrmcds_lock_getk(yrmcds* c, const char* key, size_t key_len,
246
0
                              int quiet, uint32_t* serial) {
247
0
    if( c == NULL || key == NULL || key_len == 0 )
248
0
        return YRMCDS_BAD_ARGUMENT;
249
250
0
    if( c->text_mode )
251
0
        return YRMCDS_NOT_IMPLEMENTED;
252
253
0
    return send_command(c, quiet ? YRMCDS_CMD_LAGKQ : YRMCDS_CMD_LAGK,
254
0
                        0, serial, key_len, key, 0, NULL, 0, NULL);
255
0
}
256
257
yrmcds_error yrmcds_touch(yrmcds* c, const char* key, size_t key_len,
258
0
                          uint32_t expire, int quiet, uint32_t* serial) {
259
0
    if( c == NULL || key == NULL || key_len == 0 )
260
0
        return YRMCDS_BAD_ARGUMENT;
261
262
0
    if( c->text_mode )
263
0
        return yrmcds_text_touch(c, key, key_len, expire, quiet, serial);
264
265
0
    char extras[4];
266
0
    hton32(expire, extras);
267
0
    return send_command(c, YRMCDS_CMD_TOUCH, 0, serial, key_len, key,
268
0
                        sizeof(extras), extras, 0, NULL);
269
0
}
270
271
yrmcds_error yrmcds_set(yrmcds* c, const char* key, size_t key_len,
272
                        const char* data, size_t data_len,
273
                        uint32_t flags, uint32_t expire, uint64_t cas,
274
0
                        int quiet, uint32_t* serial) {
275
0
    if( c && c->text_mode )
276
0
        return yrmcds_text_set(c, key, key_len, data, data_len,
277
0
                               flags, expire, cas, quiet, serial);
278
279
0
    return send_data(c, quiet ? YRMCDS_CMD_SETQ : YRMCDS_CMD_SET,
280
0
                     key, key_len, data, data_len, flags, expire, cas, serial);
281
0
}
282
283
yrmcds_error yrmcds_replace(yrmcds* c, const char* key, size_t key_len,
284
                            const char* data, size_t data_len,
285
                            uint32_t flags, uint32_t expire, uint64_t cas,
286
0
                            int quiet, uint32_t* serial) {
287
0
    if( c && c->text_mode )
288
0
        return yrmcds_text_replace(c, key, key_len, data, data_len,
289
0
                                   flags, expire, cas, quiet, serial);
290
291
0
    return send_data(c, quiet ? YRMCDS_CMD_REPLACEQ : YRMCDS_CMD_REPLACE,
292
0
                     key, key_len, data, data_len, flags, expire, cas, serial);
293
0
}
294
295
yrmcds_error yrmcds_add(yrmcds* c, const char* key, size_t key_len,
296
                        const char* data, size_t data_len,
297
                        uint32_t flags, uint32_t expire, uint64_t cas,
298
0
                        int quiet, uint32_t* serial) {
299
0
    if( c && c->text_mode )
300
0
        return yrmcds_text_add(c, key, key_len, data, data_len,
301
0
                               flags, expire, cas, quiet, serial);
302
303
0
    return send_data(c, quiet ? YRMCDS_CMD_ADDQ : YRMCDS_CMD_ADD,
304
0
                     key, key_len, data, data_len, flags, expire, cas, serial);
305
0
}
306
307
yrmcds_error yrmcds_replace_unlock(yrmcds* c, const char* key, size_t key_len,
308
                                   const char* data, size_t data_len,
309
                                   uint32_t flags, uint32_t expire,
310
0
                                   int quiet, uint32_t* serial) {
311
0
    if( c && c->text_mode )
312
0
        return YRMCDS_NOT_IMPLEMENTED;
313
314
0
    return send_data(c, quiet ? YRMCDS_CMD_RAUQ : YRMCDS_CMD_RAU,
315
0
                     key, key_len, data, data_len, flags, expire, 0, serial);
316
0
}
317
318
yrmcds_error yrmcds_incr(yrmcds* c, const char* key, size_t key_len,
319
0
                         uint64_t value, int quiet, uint32_t* serial) {
320
0
    if( c == NULL || key == NULL || key_len == 0 )
321
0
        return YRMCDS_BAD_ARGUMENT;
322
323
0
    if( c->text_mode )
324
0
        return yrmcds_text_incr(c, key, key_len, value, quiet, serial);
325
326
0
    char extras[20];
327
0
    hton64(value, extras);
328
0
    hton64((uint64_t)0, &extras[8]);
329
0
    hton32(~(uint32_t)0, &extras[16]);
330
0
    return send_command(c, quiet ? YRMCDS_CMD_INCREMENTQ : YRMCDS_CMD_INCREMENT,
331
0
                        0, serial, key_len, key,
332
0
                        sizeof(extras), extras, 0, NULL);
333
0
}
334
335
yrmcds_error yrmcds_incr2(yrmcds* c, const char* key, size_t key_len,
336
                          uint64_t value, uint64_t initial, uint32_t expire,
337
0
                          int quiet, uint32_t* serial) {
338
0
    if( c == NULL || key == NULL || key_len == 0 )
339
0
        return YRMCDS_BAD_ARGUMENT;
340
341
0
    if( c->text_mode )
342
0
        return YRMCDS_NOT_IMPLEMENTED;
343
344
0
    char extras[20];
345
0
    hton64(value, extras);
346
0
    hton64(initial, &extras[8]);
347
0
    hton32(expire, &extras[16]);
348
0
    return send_command(c, quiet ? YRMCDS_CMD_INCREMENTQ : YRMCDS_CMD_INCREMENT,
349
0
                        0, serial, key_len, key,
350
0
                        sizeof(extras), extras, 0, NULL);
351
0
}
352
353
yrmcds_error yrmcds_decr(yrmcds* c, const char* key, size_t key_len,
354
0
                         uint64_t value, int quiet, uint32_t* serial) {
355
0
    if( c == NULL || key == NULL || key_len == 0 )
356
0
        return YRMCDS_BAD_ARGUMENT;
357
358
0
    if( c->text_mode )
359
0
        return yrmcds_text_decr(c, key, key_len, value, quiet, serial);
360
361
0
    char extras[20];
362
0
    hton64(value, extras);
363
0
    hton64((uint64_t)0, &extras[8]);
364
0
    hton32(~(uint32_t)0, &extras[16]);
365
0
    return send_command(c, quiet ? YRMCDS_CMD_DECREMENTQ : YRMCDS_CMD_DECREMENT,
366
0
                        0, serial, key_len, key,
367
0
                        sizeof(extras), extras, 0, NULL);
368
0
}
369
370
yrmcds_error yrmcds_decr2(yrmcds* c, const char* key, size_t key_len,
371
                          uint64_t value, uint64_t initial, uint32_t expire,
372
0
                          int quiet, uint32_t* serial) {
373
0
    if( c == NULL || key == NULL || key_len == 0 )
374
0
        return YRMCDS_BAD_ARGUMENT;
375
376
0
    if( c->text_mode )
377
0
        return YRMCDS_NOT_IMPLEMENTED;
378
379
0
    char extras[20];
380
0
    hton64(value, extras);
381
0
    hton64(initial, &extras[8]);
382
0
    hton32(expire, &extras[16]);
383
0
    return send_command(c, quiet ? YRMCDS_CMD_DECREMENTQ : YRMCDS_CMD_DECREMENT,
384
0
                        0, serial, key_len, key,
385
0
                        sizeof(extras), extras, 0, NULL);
386
0
}
387
388
yrmcds_error yrmcds_append(yrmcds* c, const char* key, size_t key_len,
389
                           const char* data, size_t data_len,
390
0
                           int quiet, uint32_t* serial) {
391
0
    if( c == NULL || key == NULL || key_len == 0 ||
392
0
        data == NULL || data_len == 0 )
393
0
        return YRMCDS_BAD_ARGUMENT;
394
395
0
    if( c->text_mode )
396
0
        return yrmcds_text_append(c, key, key_len, data, data_len,
397
0
                                  quiet, serial);
398
399
0
    return send_command(c, quiet ? YRMCDS_CMD_APPENDQ : YRMCDS_CMD_APPEND,
400
0
                        0, serial, key_len, key, 0, NULL, data_len, data);
401
0
}
402
403
yrmcds_error yrmcds_prepend(yrmcds* c, const char* key, size_t key_len,
404
                            const char* data, size_t data_len,
405
0
                            int quiet, uint32_t* serial) {
406
0
    if( c == NULL || key == NULL || key_len == 0 ||
407
0
        data == NULL || data_len == 0 )
408
0
        return YRMCDS_BAD_ARGUMENT;
409
410
0
    if( c->text_mode )
411
0
        return yrmcds_text_prepend(c, key, key_len, data, data_len,
412
0
                                   quiet, serial);
413
414
0
    return send_command(c, quiet ? YRMCDS_CMD_PREPENDQ : YRMCDS_CMD_PREPEND,
415
0
                        0, serial, key_len, key, 0, NULL, data_len, data);
416
0
}
417
418
yrmcds_error yrmcds_remove(yrmcds* c, const char* key, size_t key_len,
419
0
                           int quiet, uint32_t* serial) {
420
0
    if( c == NULL || key == NULL || key_len == 0 )
421
0
        return YRMCDS_BAD_ARGUMENT;
422
423
0
    if( c->text_mode )
424
0
        return yrmcds_text_remove(c, key, key_len, quiet, serial);
425
426
0
    return send_command(c, quiet ? YRMCDS_CMD_DELETEQ : YRMCDS_CMD_DELETE,
427
0
                        0, serial, key_len, key, 0, NULL, 0, NULL);
428
0
}
429
430
yrmcds_error yrmcds_lock(yrmcds* c, const char* key, size_t key_len,
431
0
                         int quiet, uint32_t* serial) {
432
0
    if( c == NULL || key == NULL || key_len == 0 )
433
0
        return YRMCDS_BAD_ARGUMENT;
434
435
0
    if( c->text_mode )
436
0
        return YRMCDS_NOT_IMPLEMENTED;
437
438
0
    return send_command(c, quiet ? YRMCDS_CMD_LOCKQ : YRMCDS_CMD_LOCK,
439
0
                        0, serial, key_len, key, 0, NULL, 0, NULL);
440
0
}
441
442
yrmcds_error yrmcds_unlock(yrmcds* c, const char* key, size_t key_len,
443
0
                           int quiet, uint32_t* serial) {
444
0
    if( c == NULL || key == NULL || key_len == 0 )
445
0
        return YRMCDS_BAD_ARGUMENT;
446
447
0
    if( c->text_mode )
448
0
        return YRMCDS_NOT_IMPLEMENTED;
449
450
0
    return send_command(c, quiet ? YRMCDS_CMD_UNLOCKQ : YRMCDS_CMD_UNLOCK,
451
0
                        0, serial, key_len, key, 0, NULL, 0, NULL);
452
0
}
453
454
0
yrmcds_error yrmcds_unlockall(yrmcds* c, int quiet, uint32_t* serial) {
455
0
    if( c == NULL )
456
0
        return YRMCDS_BAD_ARGUMENT;
457
458
0
    if( c->text_mode )
459
0
        return YRMCDS_NOT_IMPLEMENTED;
460
461
0
    return send_command(c, quiet ? YRMCDS_CMD_UNLOCKALLQ : YRMCDS_CMD_UNLOCKALL,
462
0
                        0, serial, 0, NULL, 0, NULL, 0, NULL);
463
0
}
464
465
yrmcds_error yrmcds_flush(yrmcds* c, uint32_t delay,
466
0
                          int quiet, uint32_t* serial) {
467
0
    if( c == NULL )
468
0
        return YRMCDS_BAD_ARGUMENT;
469
470
0
    if( c->text_mode )
471
0
        return yrmcds_text_flush(c, delay, quiet, serial);
472
473
0
    if( delay == 0 )
474
0
        return send_command(c, quiet ? YRMCDS_CMD_FLUSHQ : YRMCDS_CMD_FLUSH,
475
0
                            0, serial, 0, NULL, 0, NULL, 0, NULL);
476
477
0
    char extra[4];
478
0
    hton32(delay, extra);
479
0
    return send_command(c, quiet ? YRMCDS_CMD_FLUSHQ : YRMCDS_CMD_FLUSH,
480
0
                        0, serial, 0, NULL, sizeof(extra), extra, 0, NULL);
481
0
}
482
483
0
yrmcds_error yrmcds_stat_general(yrmcds* c, uint32_t* serial) {
484
0
    if( c == NULL )
485
0
        return YRMCDS_BAD_ARGUMENT;
486
487
0
    if( c->text_mode )
488
0
        return YRMCDS_NOT_IMPLEMENTED;
489
490
0
    return send_command(c, YRMCDS_CMD_STAT,
491
0
                        0, serial, 0, NULL, 0, NULL, 0, NULL);
492
0
}
493
494
0
yrmcds_error yrmcds_stat_settings(yrmcds* c, uint32_t* serial) {
495
0
    if( c == NULL )
496
0
        return YRMCDS_BAD_ARGUMENT;
497
498
0
    if( c->text_mode )
499
0
        return YRMCDS_NOT_IMPLEMENTED;
500
501
0
    const char key[] = "settings";
502
0
    return send_command(c, YRMCDS_CMD_STAT,
503
0
                        0, serial, sizeof(key) - 1, key, 0, NULL, 0, NULL);
504
0
}
505
506
0
yrmcds_error yrmcds_stat_items(yrmcds* c, uint32_t* serial) {
507
0
    if( c == NULL )
508
0
        return YRMCDS_BAD_ARGUMENT;
509
510
0
    if( c->text_mode )
511
0
        return YRMCDS_NOT_IMPLEMENTED;
512
513
0
    const char key[] = "items";
514
0
    return send_command(c, YRMCDS_CMD_STAT,
515
0
                        0, serial, sizeof(key) - 1, key, 0, NULL, 0, NULL);
516
0
}
517
518
0
yrmcds_error yrmcds_stat_sizes(yrmcds* c, uint32_t* serial) {
519
0
    if( c == NULL )
520
0
        return YRMCDS_BAD_ARGUMENT;
521
522
0
    if( c->text_mode )
523
0
        return YRMCDS_NOT_IMPLEMENTED;
524
525
0
    const char key[] = "sizes";
526
0
    return send_command(c, YRMCDS_CMD_STAT,
527
0
                        0, serial, sizeof(key) - 1, key, 0, NULL, 0, NULL);
528
0
}
529
530
yrmcds_error yrmcds_keys(yrmcds* c, const char* prefix, size_t prefix_len,
531
0
                         uint32_t* serial) {
532
0
    if( c == NULL ||
533
0
        (prefix == NULL && prefix_len != 0) ||
534
0
        (prefix != NULL && prefix_len == 0) )
535
0
        return YRMCDS_BAD_ARGUMENT;
536
537
0
    if( c->text_mode )
538
0
        return YRMCDS_NOT_IMPLEMENTED;
539
540
0
    return send_command(c, YRMCDS_CMD_KEYS,
541
0
                        0, serial, prefix_len, prefix, 0, NULL, 0, NULL);
542
0
}
543
544
0
yrmcds_error yrmcds_version(yrmcds* c, uint32_t* serial) {
545
0
    if( c == NULL )
546
0
        return YRMCDS_BAD_ARGUMENT;
547
548
0
    if( c->text_mode )
549
0
        return yrmcds_text_version(c, serial);
550
551
0
    return send_command(c, YRMCDS_CMD_VERSION,
552
0
                        0, serial, 0, NULL, 0, NULL, 0, NULL);
553
0
}
554
555
0
yrmcds_error yrmcds_quit(yrmcds* c, int quiet, uint32_t* serial) {
556
0
    if( c == NULL )
557
0
        return YRMCDS_BAD_ARGUMENT;
558
559
0
    if( c->text_mode )
560
0
        return yrmcds_text_quit(c, serial);
561
562
0
    return send_command(c, quiet ? YRMCDS_CMD_QUITQ : YRMCDS_CMD_QUIT,
563
0
                        0, serial, 0, NULL, 0, NULL, 0, NULL);
564
0
}