http_send.c: Kill check_r member from struct http_client.
[paraslash.git] / blob.c
1 /*
2  * Copyright (C) 2007-2008 Andre Noll <maan@systemlinux.org>
3  *
4  * Licensed under the GPL v2. For licencing details see COPYING.
5  */
6
7 /** \file blob.c Macros and functions for blob handling. */
8
9 #include <fnmatch.h>
10 #include "para.h"
11 #include "error.h"
12 #include "string.h"
13 #include "afh.h"
14 #include "afs.h"
15 #include "net.h"
16
17 static struct osl_column_description blob_cols[] = {
18         [BLOBCOL_ID] = {
19                 .storage_type = OSL_MAPPED_STORAGE,
20                 .storage_flags = OSL_RBTREE | OSL_UNIQUE | OSL_FIXED_SIZE,
21                 .name = "id",
22                 .data_size = 4,
23                 .compare_function = uint32_compare
24         },
25         [BLOBCOL_NAME] = {
26                 .storage_type = OSL_MAPPED_STORAGE,
27                 .storage_flags = OSL_RBTREE | OSL_UNIQUE,
28                 .name = "name",
29                 .compare_function = string_compare
30         },
31         [BLOBCOL_DEF] = {
32                 .storage_type = OSL_DISK_STORAGE,
33                 .storage_flags = 0,
34                 .name = "definition"
35         }
36 };
37
38 /** \cond doxygen isn't smart enough to recognize these */
39 INIT_BLOB_TABLE(lyrics);
40 INIT_BLOB_TABLE(images);
41 INIT_BLOB_TABLE(moods);
42 INIT_BLOB_TABLE(playlists);
43 /** \endcond */
44
45 /** Flags that may be passed to the \p ls functions of each blob  type. */
46 enum blob_ls_flags {
47         /** List both id and name. */
48         BLOB_LS_FLAG_LONG = 1,
49         /** Reverse sort order. */
50         BLOB_LS_FLAG_REVERSE = 2,
51         /** Sort by id instead of name. */
52         BLOB_LS_FLAG_SORT_BY_ID = 4,
53 };
54
55 /** Structure passed to the \p print_blob function. */
56 struct lsblob_action_data {
57         /* The flags given at the command line. */
58         uint32_t flags;
59         /** Message buffer. */
60         struct para_buffer pb;
61 };
62
63 static int print_blob(struct osl_table *table, struct osl_row *row,
64                 const char *name, void *data)
65 {
66         struct lsblob_action_data *lbad = data;
67         struct osl_object obj;
68         uint32_t id;
69         int ret;
70
71         if (!(lbad->flags & BLOB_LS_FLAG_LONG)) {
72                 para_printf(&lbad->pb, "%s\n", name);
73                 return 1;
74         }
75         ret = osl_get_object(table, row, BLOBCOL_ID, &obj);
76         if (ret < 0) {
77                 para_printf(&lbad->pb, "%s: %s\n", name, para_strerror(-ret));
78                 return ret;
79         }
80         id = *(uint32_t *)obj.data;
81         para_printf(&lbad->pb, "%u\t%s\n", id, name);
82         return 1;
83 }
84
85 static int com_lsblob_callback(struct osl_table *table,
86                 const struct osl_object *query, struct osl_object *result)
87 {
88         struct lsblob_action_data lbad = {.flags = *(uint32_t *)query->data};
89         struct pattern_match_data pmd = {
90                 .table = table,
91                 .patterns = {.data = (char *)query->data + sizeof(uint32_t),
92                         .size = query->size - sizeof(uint32_t)},
93                 .pm_flags = PM_NO_PATTERN_MATCHES_EVERYTHING | PM_SKIP_EMPTY_NAME,
94                 .match_col_num = BLOBCOL_NAME,
95                 .data = &lbad,
96                 .action = print_blob,
97         };
98         int ret;
99
100         if (lbad.flags & BLOB_LS_FLAG_REVERSE)
101                 pmd.pm_flags |= PM_REVERSE_LOOP;
102         if (!(lbad.flags & BLOB_LS_FLAG_SORT_BY_ID))
103                 pmd.loop_col_num = BLOBCOL_NAME;
104         else
105                 pmd.loop_col_num = BLOBCOL_ID;
106         ret = for_each_matching_row(&pmd);
107         if (ret < 0)
108                 para_printf(&lbad.pb, "%s\n", para_strerror(-ret));
109         if (!lbad.pb.buf)
110                 return 0;
111         result->data = lbad.pb.buf;
112         result->size = lbad.pb.size;
113         return 1;
114 }
115
116 static int com_lsblob(callback_function *f, int fd, int argc, char * const * const argv)
117 {
118         uint32_t flags = 0;
119         struct osl_object options = {.data = &flags, .size = sizeof(flags)},
120                 result;
121         int i, ret;
122
123         for (i = 1; i < argc; i++) {
124                 const char *arg = argv[i];
125                 if (arg[0] != '-')
126                         break;
127                 if (!strcmp(arg, "--")) {
128                         i++;
129                         break;
130                 }
131                 if (!strcmp(arg, "-l")) {
132                         flags |= BLOB_LS_FLAG_LONG;
133                         continue;
134                 }
135                 if (!strcmp(arg, "-i")) {
136                         flags |= BLOB_LS_FLAG_SORT_BY_ID;
137                         continue;
138                 }
139                 if (!strcmp(arg, "-r")) {
140                         flags |= BLOB_LS_FLAG_REVERSE;
141                         continue;
142                 }
143                 break;
144         }
145 //      if (argc > i)
146 //              return -E_BLOB_SYNTAX;
147         ret = send_option_arg_callback_request(&options, argc - i,
148                 argv + i, f, &result);
149         if (!ret)
150                 return 0;
151         if (ret < 0) {
152                 send_va_buffer(fd, "%s\n", para_strerror(-ret));
153                 return ret;
154         }
155         ret = send_buffer(fd, (char *)result.data);
156         free(result.data);
157         return ret;
158 }
159
160 static int cat_blob(struct osl_table *table, struct osl_row *row,
161                 __a_unused const char *name, void *data)
162 {
163         int ret;
164         struct osl_object *blobs = data;
165         struct osl_object obj;
166
167         ret = osl_open_disk_object(table, row, BLOBCOL_DEF, &obj);
168         if (ret < 0)
169                 return ret;
170         if (obj.size) {
171                 blobs->data = para_realloc(blobs->data, blobs->size + obj.size);
172                 memcpy(blobs->data + blobs->size, obj.data, obj.size);
173                 blobs->size += obj.size;
174         }
175         return osl_close_disk_object(&obj);
176 }
177
178 static int com_catblob_callback(struct osl_table *table,
179                 const struct osl_object *query, struct osl_object *result)
180 {
181         int ret;
182         struct pattern_match_data pmd = {
183                 .table = table,
184                 .patterns = *query,
185                 .loop_col_num = BLOBCOL_NAME,
186                 .match_col_num = BLOBCOL_NAME,
187                 .pm_flags = PM_SKIP_EMPTY_NAME,
188                 .data = result,
189                 .action = cat_blob
190         };
191         result->data = NULL;
192         ret = for_each_matching_row(&pmd);
193         if (result->data)
194                 return 1;
195         return (ret > 0)? 0 : ret;
196 }
197
198 static int com_catblob(callback_function *f, int fd, int argc,
199                 char * const * const argv)
200 {
201         struct osl_object result;
202         int ret;
203
204         if (argc < 2)
205                 return -E_BLOB_SYNTAX;
206         ret = send_standard_callback_request(argc - 1, argv + 1, f, &result);
207         if (ret > 0) {
208                 ret = send_bin_buffer(fd, (char *)result.data, result.size);
209                 free(result.data);
210         }
211         return ret;
212 }
213
214 /** Used for removing rows from a blob table. */
215 struct rmblob_data {
216         /** Message buffer. */
217         struct para_buffer pb;
218         /** Number of removed blobs. */
219         unsigned num_removed;
220 };
221
222 static int remove_blob(struct osl_table *table, struct osl_row *row,
223                 const char *name, void *data)
224 {
225         struct rmblob_data *rmbd = data;
226         int ret = osl_del_row(table, row);
227         if (ret < 0) {
228                 para_printf(&rmbd->pb, "%s: %s\n", name, para_strerror(-ret));
229                 return ret;
230         }
231         rmbd->num_removed++;
232         return 1; /* return success to remove other matching blobs. */
233 }
234
235 static int com_rmblob_callback(struct osl_table *table,
236                 const struct osl_object *query,
237                 struct osl_object *result)
238 {
239         int ret;
240         struct rmblob_data rmbd = {.num_removed = 0};
241         struct pattern_match_data pmd = {
242                 .table = table,
243                 .patterns = *query,
244                 .loop_col_num = BLOBCOL_NAME,
245                 .match_col_num = BLOBCOL_NAME,
246                 .pm_flags = PM_SKIP_EMPTY_NAME,
247                 .data = &rmbd,
248                 .action = remove_blob
249         };
250         result->data = NULL;
251         ret = for_each_matching_row(&pmd);
252         if (ret < 0)
253                 para_printf(&rmbd.pb, "%s\n", para_strerror(-ret));
254         if (!rmbd.num_removed)
255                 para_printf(&rmbd.pb, "no matches, nothing removed\n");
256         else {
257                 para_printf(&rmbd.pb, "removed %d blobs\n", rmbd.num_removed);
258                 afs_event(BLOB_RENAME, NULL, table);
259         }
260         result->data = rmbd.pb.buf;
261         result->size = rmbd.pb.size;
262         return 1;
263 }
264
265 static int com_rmblob(callback_function *f, int fd, int argc,
266                 char * const * const argv)
267 {
268         int ret;
269         struct osl_object result;
270
271         if (argc < 2)
272                 return -E_MOOD_SYNTAX;
273         ret = send_option_arg_callback_request(NULL, argc - 1, argv + 1, f,
274                 &result);
275         if (ret > 0) {
276                 send_buffer(fd, (char *)result.data);
277                 free(result.data);
278         }
279         return ret;
280 }
281
282 static int com_addblob_callback(struct osl_table *table,
283                 const struct osl_object *query,
284                 __a_unused struct osl_object *result)
285 {
286         struct osl_object objs[NUM_BLOB_COLUMNS];
287         char *name = query->data;
288         size_t name_len = strlen(name) + 1;
289         uint32_t id;
290         unsigned num_rows;
291         int ret;
292
293         ret = osl_get_num_rows(table, &num_rows);
294         if (ret < 0)
295                 return ret;
296         if (!num_rows) { /* this is the first entry ever added */
297                 /* insert dummy row containing the id */
298                 id = 2; /* this entry will be entry #1, so 2 is the next */
299                 objs[BLOBCOL_ID].data = &id;
300                 objs[BLOBCOL_ID].size = sizeof(id);
301                 objs[BLOBCOL_NAME].data = "";
302                 objs[BLOBCOL_NAME].size = 1;
303                 objs[BLOBCOL_DEF].data = "";
304                 objs[BLOBCOL_DEF].size = 1;
305                 ret = osl_add_row(table, objs);
306                 if (ret < 0)
307                         return ret;
308         } else {
309                 /* check if name already exists */
310                 struct osl_row *row;
311                 struct osl_object obj = {.data = name, .size = name_len};
312                 ret = osl_get_row(table, BLOBCOL_NAME, &obj, &row);
313                 if (ret < 0 && ret != -E_RB_KEY_NOT_FOUND)
314                         return ret;
315                 if (ret >= 0) { /* we already have a blob with this name */
316                         obj.data = name + name_len;
317                         obj.size = query->size - name_len;
318                         return osl_update_object(table, row, BLOBCOL_DEF, &obj);
319                 }
320                 /* new blob, get id of the dummy row and increment it */
321                 obj.data = "";
322                 obj.size = 1;
323                 ret = osl_get_row(table, BLOBCOL_NAME, &obj, &row);
324                 if (ret < 0)
325                         return ret;
326                 ret = osl_get_object(table, row, BLOBCOL_ID, &obj);
327                 if (ret < 0)
328                         return ret;
329                 id = *(uint32_t *)obj.data + 1;
330                 obj.data = &id;
331                 ret = osl_update_object(table, row, BLOBCOL_ID, &obj);
332                 if (ret < 0)
333                         return ret;
334         }
335         id--;
336         objs[BLOBCOL_ID].data = &id;
337         objs[BLOBCOL_ID].size = sizeof(id);
338         objs[BLOBCOL_NAME].data = name;
339         objs[BLOBCOL_NAME].size = name_len;
340         objs[BLOBCOL_DEF].data = name + name_len;
341         objs[BLOBCOL_DEF].size = query->size - name_len;
342         ret = osl_add_row(table, objs);
343         if (ret < 0)
344                 return ret;
345         afs_event(BLOB_ADD, NULL, table);
346         return 1;
347 }
348
349 static int com_addblob(callback_function *f, int fd, int argc,
350                 char * const * const argv)
351 {
352         struct osl_object arg_obj;
353
354         if (argc != 2)
355                 return -E_BLOB_SYNTAX;
356         if (!*argv[1]) /* empty name is reserved for the dummy row */
357                 return -E_BLOB_SYNTAX;
358         PARA_NOTICE_LOG("argv[1]: %s\n", argv[1]);
359         arg_obj.size = strlen(argv[1]) + 1;
360         arg_obj.data = (char *)argv[1];
361         return stdin_command(fd, &arg_obj, f, 10 * 1024 * 1024, NULL);
362 }
363
364 static int com_mvblob_callback(struct osl_table *table,
365                 const struct osl_object *query,
366                 __a_unused struct osl_object *result)
367 {
368         char *src = (char *) query->data;
369         struct osl_object obj = {.data = src, .size = strlen(src) + 1};
370         char *dest = src + obj.size;
371         struct osl_row *row;
372         int ret = osl_get_row(table, BLOBCOL_NAME, &obj, &row);
373
374         if (ret < 0)
375                 return ret;
376         obj.data = dest;
377         obj.size = strlen(dest) + 1;
378         ret = osl_update_object(table, row, BLOBCOL_NAME, &obj);
379         if (ret < 0)
380                 return ret;
381         afs_event(BLOB_RENAME, NULL, table);
382         return 1;
383 }
384
385 static int com_mvblob(callback_function *f, __a_unused int fd,
386                 int argc, char * const * const argv)
387 {
388         if (argc != 3)
389                 return -E_MOOD_SYNTAX;
390         return send_option_arg_callback_request(NULL, argc - 1, argv + 1, f,
391                 NULL);
392 }
393
394 #define DEFINE_BLOB_COMMAND(cmd_name, table_name, cmd_prefix) \
395         static int com_ ## cmd_name ## cmd_prefix ## _callback(const struct osl_object *query, \
396                         struct osl_object *output) \
397         { \
398                 return com_ ## cmd_name ## blob_callback(table_name ## _table, query, output); \
399         } \
400         int com_ ## cmd_name ## cmd_prefix(int fd, int argc, char * const * const argv) \
401         { \
402                 return com_ ## cmd_name ## blob(com_ ## cmd_name ## cmd_prefix ## _callback, fd, argc, argv); \
403         }
404
405 static int blob_get_name_by_id(struct osl_table *table, uint32_t id,
406                 char **name)
407 {
408         struct osl_row *row;
409         struct osl_object obj = {.data = &id, .size = sizeof(id)};
410         int ret;
411
412         *name = NULL;
413         if (!id)
414                 return 1;
415         ret = osl_get_row(table, BLOBCOL_ID, &obj, &row);
416         if (ret < 0)
417                 return ret;
418         ret = osl_get_object(table, row, BLOBCOL_NAME, &obj);
419         if (ret < 0)
420                 return ret;
421         *name = (char *)obj.data;
422         return 1;
423 }
424
425 /** Define the \p get_name_by_id function for this blob type. */
426 #define DEFINE_GET_NAME_BY_ID(table_name, cmd_prefix) \
427         int cmd_prefix ## _get_name_by_id(uint32_t id, char **name) \
428         { \
429                 return blob_get_name_by_id(table_name ## _table, id, name); \
430         }
431
432
433 static int blob_get_def_by_name(struct osl_table *table, char *name,
434                 struct osl_object *def)
435 {
436         struct osl_row *row;
437         struct osl_object obj = {.data = name, .size = strlen(name) + 1};
438         int ret;
439
440         def->data = NULL;
441         if (!*name)
442                 return 1;
443         ret = osl_get_row(table, BLOBCOL_NAME, &obj, &row);
444         if (ret < 0)
445                 return ret;
446         return osl_open_disk_object(table, row, BLOBCOL_DEF, def);
447 }
448
449 /** Define the \p get_def_by_id function for this blob type. */
450 #define DEFINE_GET_DEF_BY_NAME(table_name, cmd_prefix) \
451         int cmd_prefix ## _get_def_by_name(char *name, struct osl_object *def) \
452         { \
453                 return blob_get_def_by_name(table_name ## _table, name, def); \
454         }
455
456 static int blob_get_def_by_id(struct osl_table *table, uint32_t id,
457                 struct osl_object *def)
458 {
459         struct osl_row *row;
460         struct osl_object obj = {.data = &id, .size = sizeof(id)};
461         int ret;
462
463         def->data = NULL;
464         if (!id)
465                 return 1;
466         ret = osl_get_row(table, BLOBCOL_ID, &obj, &row);
467         if (ret < 0)
468                 return ret;
469         return osl_open_disk_object(table, row, BLOBCOL_DEF, def);
470 }
471
472 /** Define the \p get_def_by_id function for this blob type. */
473 #define DEFINE_GET_DEF_BY_ID(table_name, cmd_prefix) \
474         int cmd_prefix ## _get_def_by_id(uint32_t id, struct osl_object *def) \
475         { \
476                 return blob_get_def_by_id(table_name ## _table, id, def); \
477         }
478
479 static int blob_get_name_and_def_by_row(struct osl_table *table,
480                 const struct osl_row *row, char **name, struct osl_object *def)
481 {
482         struct osl_object obj;
483         int ret = osl_get_object(table, row, BLOBCOL_NAME, &obj);
484         if (ret < 0)
485                 return ret;
486         *name = obj.data;
487         return osl_open_disk_object(table, row, BLOBCOL_DEF, def);
488 }
489 /** Define the \p get_name_and_def_by_row function for this blob type. */
490 #define DEFINE_GET_NAME_AND_DEF_BY_ROW(table_name, cmd_prefix) \
491         int cmd_prefix ## _get_name_and_def_by_row(const struct osl_row *row, \
492                 char **name, struct osl_object *def) \
493         { \
494                 return blob_get_name_and_def_by_row(table_name ## _table, \
495                         row, name, def); \
496         }
497
498 /** Define the \p close function for this blob type. */
499 #define DEFINE_BLOB_CLOSE(table_name) \
500         void table_name ## _close(void) \
501         { \
502                 osl_close_table(table_name ## _table, OSL_MARK_CLEAN); \
503                 table_name ## _table = NULL; \
504         }
505
506 /** Define the \p create function for this blob type. */
507 #define DEFINE_BLOB_CREATE(table_name) \
508         int table_name ## _create(const char *dir) \
509         { \
510                 table_name ## _table_desc.dir = dir; \
511                 return osl_create_table(&table_name ## _table_desc); \
512         }
513
514 static int blob_open(struct osl_table **table,
515                 struct osl_table_description *desc,
516                 const char *dir)
517 {
518         int ret;
519         desc->dir = dir;
520         ret = osl_open_table(desc, table);
521         if (ret >= 0)
522                 return ret;
523         *table = NULL;
524         if (ret >= 0 || is_errno(-ret, ENOENT))
525                 return 1;
526         return ret;
527 }
528
529 #define DEFINE_BLOB_OPEN(table_name) \
530         int table_name ## _open(const char *dir) \
531         { \
532                 return blob_open(&table_name ## _table, \
533                         &table_name ## _table_desc, dir); \
534         }
535
536
537 /** Define the \p init function for this blob type. */
538 #define DEFINE_BLOB_INIT(table_name) \
539         void table_name ## _init(struct afs_table *t) \
540         { \
541                 t->name = table_name ## _table_desc.name; \
542                 t->open = table_name ## _open; \
543                 t->close = table_name ## _close; \
544                 t->create = table_name ## _create;\
545                 t->event_handler = table_name ##_event_handler; \
546                 table_name ## _table = NULL; \
547         }
548
549
550 /** Define all functions for this blob type. */
551 #define DEFINE_BLOB_FUNCTIONS(table_name, cmd_prefix) \
552         DEFINE_BLOB_OPEN(table_name) \
553         DEFINE_BLOB_CLOSE(table_name) \
554         DEFINE_BLOB_CREATE(table_name) \
555         DEFINE_BLOB_INIT(table_name) \
556         DEFINE_BLOB_COMMAND(ls, table_name, cmd_prefix) \
557         DEFINE_BLOB_COMMAND(cat, table_name, cmd_prefix) \
558         DEFINE_BLOB_COMMAND(add, table_name, cmd_prefix) \
559         DEFINE_BLOB_COMMAND(rm, table_name, cmd_prefix) \
560         DEFINE_BLOB_COMMAND(mv, table_name, cmd_prefix) \
561         DEFINE_GET_NAME_BY_ID(table_name, cmd_prefix); \
562         DEFINE_GET_DEF_BY_ID(table_name, cmd_prefix); \
563         DEFINE_GET_DEF_BY_NAME(table_name, cmd_prefix); \
564         DEFINE_GET_NAME_AND_DEF_BY_ROW(table_name, cmd_prefix); \
565
566 /** \cond doxygen isn't smart enough to recognize these */
567 DEFINE_BLOB_FUNCTIONS(lyrics, lyr);
568 DEFINE_BLOB_FUNCTIONS(images, img);
569 DEFINE_BLOB_FUNCTIONS(moods, mood);
570 DEFINE_BLOB_FUNCTIONS(playlists, pl);
571 /** \endcond */