Implement server side afs callbacks.
[paraslash.git] / blob.c
1 #include "para.h"
2 #include "error.h"
3 #include "afh.h"
4 #include "afs.h"
5 #include "string.h"
6
7 /** \file blob.c Macros and functions for blob handling. */
8
9 static struct osl_column_description blob_cols[] = {
10         [BLOBCOL_ID] = {
11                 .storage_type = OSL_MAPPED_STORAGE,
12                 .storage_flags = OSL_RBTREE | OSL_UNIQUE | OSL_FIXED_SIZE,
13                 .name = "id",
14                 .data_size = 4,
15                 .compare_function = uint32_compare
16         },
17         [BLOBCOL_NAME] = {
18                 .storage_type = OSL_MAPPED_STORAGE,
19                 .storage_flags = OSL_RBTREE | OSL_UNIQUE,
20                 .name = "name",
21                 .compare_function = string_compare
22         },
23         [BLOBCOL_DEF] = {
24                 .storage_type = OSL_DISK_STORAGE,
25                 .storage_flags = 0,
26                 .name = "definition"
27         }
28 };
29
30 /** \cond doxygen isn't smart enough to recognize these */
31 INIT_BLOB_TABLE(lyrics);
32 INIT_BLOB_TABLE(images);
33 INIT_BLOB_TABLE(moods);
34 INIT_BLOB_TABLE(playlists);
35 /** \endcond */
36
37 /** Flags that may be passed to the \p ls functions of each blob  type. */
38 enum blob_ls_flags {
39         /** List both id and name. */
40         BLOB_LS_FLAG_LONG = 1,
41         /** Reverse sort order. */
42         BLOB_LS_FLAG_REVERSE = 2,
43         /** Sort by id instead of name. */
44         BLOB_LS_FLAG_SORT_BY_ID = 4,
45 };
46
47 /** Data passed to \p com_lsbob_callback(). */
48 struct com_lsblob_options {
49         /** Given flags for the ls command. */
50         uint32_t flags;
51 };
52
53 /** Structure passed to the \p print_blob loop function. */
54 struct lsblob_loop_data {
55         struct com_lsblob_options *opts;
56         struct para_buffer *pb;
57         struct osl_table *table;
58 };
59
60 static int print_blob(struct osl_row *row, void *loop_data)
61 {
62         struct osl_object obj;
63         char *name;
64         uint32_t id;
65         int ret;
66         struct lsblob_loop_data *lld = loop_data;
67
68         ret = osl_get_object(lld->table, row, BLOBCOL_NAME, &obj);
69         if (ret < 0)
70                 return ret;
71         name = obj.data;
72         if (!*name) /* ignore dummy row */
73                 return 1;
74         ret = osl_get_object(lld->table, row, BLOBCOL_ID, &obj);
75         if (ret < 0)
76                 return ret;
77         id = *(uint32_t *)obj.data;
78         if (lld->opts->flags & BLOB_LS_FLAG_LONG)
79                 para_printf(lld->pb, "%u\t%s\n", id, name);
80         else
81                 para_printf(lld->pb, "%s\n", name);
82         return 1;
83 }
84
85 int com_lsblob_callback(struct osl_table *table,
86                 const struct osl_object *query, struct osl_object *ls_output)
87 {
88         struct para_buffer pb = {.buf = NULL};
89         struct lsblob_loop_data lld = {.opts = query->data, .pb = &pb, .table = table};
90         int ret;
91
92         if (lld.opts->flags & BLOB_LS_FLAG_REVERSE) {
93                 if (lld.opts->flags & BLOB_LS_FLAG_SORT_BY_ID)
94                         ret = osl_rbtree_loop(lld.table, BLOBCOL_ID, &lld, print_blob);
95                 else
96                         ret = osl_rbtree_loop_reverse(lld.table, BLOBCOL_NAME, &lld, print_blob);
97         } else {
98                 if (lld.opts->flags & BLOB_LS_FLAG_SORT_BY_ID)
99                         ret = osl_rbtree_loop_reverse(lld.table, BLOBCOL_ID, &lld, print_blob);
100                 else
101                         ret = osl_rbtree_loop(lld.table, BLOBCOL_NAME, &lld, print_blob);
102         }
103         ls_output->data = pb.buf;
104         ls_output->size = pb.size;
105         return ret;
106 }
107
108 static int com_lsblob(callback_function *f, __a_unused int fd, int argc, const char **argv)
109 {
110         struct com_lsblob_options clbo = {.flags = 0};
111         struct osl_object query = {.data = &clbo, .size = sizeof(clbo)},
112                 ls_output;
113         int i, ret;
114
115         for (i = 1; i < argc; i++) {
116                 const char *arg = argv[i];
117                 if (arg[0] != '-')
118                         break;
119                 if (!strcmp(arg, "--")) {
120                         i++;
121                         break;
122                 }
123                 if (!strcmp(arg, "-l")) {
124                         clbo.flags |= BLOB_LS_FLAG_LONG;
125                         continue;
126                 }
127                 if (!strcmp(arg, "-i")) {
128                         clbo.flags |= BLOB_LS_FLAG_SORT_BY_ID;
129                         continue;
130                 }
131                 if (!strcmp(arg, "-r")) {
132                         clbo.flags |= BLOB_LS_FLAG_REVERSE;
133                         continue;
134                 }
135         }
136         if (argc > i)
137                 return -E_BLOB_SYNTAX;
138         ret = send_option_arg_callback_request(&query, argc - i,
139                 argv + i, f, &ls_output);
140         if (ret >= 0 && ls_output.data)
141                 printf("%s\n", (char *)ls_output.data);
142         free(ls_output.data);
143         return ret;
144 }
145
146 static int com_catblob_callback(struct osl_table *table,
147                 const struct osl_object *query, struct osl_object *output)
148 {
149         struct osl_object obj;
150         int ret;
151         struct osl_row *row;
152
153         ret = osl_get_row(table, BLOBCOL_NAME, query, &row);
154         if (ret < 0)
155                 return ret;
156         ret = osl_open_disk_object(table, row, BLOBCOL_DEF, &obj);
157         if (ret < 0)
158                 return ret;
159         output->data = para_malloc(obj.size);
160         output->size = obj.size;
161         memcpy(output->data, obj.data, obj.size);
162         return osl_close_disk_object(&obj);
163 }
164 static int com_catblob(callback_function *f, __a_unused int fd, int argc,
165                 const char **argv)
166 {
167         struct osl_object cat_output = {.data = NULL};
168         int ret;
169
170         if (argc != 2)
171                 return -E_BLOB_SYNTAX;
172         if (!*argv[1]) /* empty name is reserved of the dummy row */
173                 return -E_BLOB_SYNTAX;
174         ret = send_standard_callback_request(1, argv + 1, f, &cat_output);
175         if (ret >= 0 && cat_output.data)
176                 printf("%s\n", (char *)cat_output.data);
177         free(cat_output.data);
178         return ret;
179
180 }
181
182 static int com_addblob_callback(struct osl_table *table,
183                 const struct osl_object *query,
184                 __a_unused struct osl_object *result)
185 {
186         struct osl_object objs[NUM_BLOB_COLUMNS];
187         char *name = query->data;
188         size_t name_len = strlen(name) + 1;
189         uint32_t id;
190         unsigned num_rows;
191         int ret;
192
193         ret = osl_get_num_rows(table, &num_rows);
194         if (ret < 0)
195                 return ret;
196         if (!num_rows) { /* this is the first entry ever added */
197                 /* insert dummy row containing the id */
198                 id = 2; /* this entry will be entry #1, so 2 is the next */
199                 objs[BLOBCOL_ID].data = &id;
200                 objs[BLOBCOL_ID].size = sizeof(id);
201                 objs[BLOBCOL_NAME].data = "";
202                 objs[BLOBCOL_NAME].size = 1;
203                 objs[BLOBCOL_DEF].data = "";
204                 objs[BLOBCOL_DEF].size = 1;
205                 ret = osl_add_row(table, objs);
206                 if (ret < 0)
207                         return ret;
208         } else { /* get id of the dummy row and increment it */
209                 struct osl_row *row;
210                 struct osl_object obj = {.data = "", .size = 1};
211                 ret = osl_get_row(table, BLOBCOL_NAME, &obj, &row);
212                 if (ret < 0)
213                         return ret;
214                 ret = osl_get_object(table, row, BLOBCOL_ID, &obj);
215                 if (ret < 0)
216                         return ret;
217                 id = *(uint32_t *)obj.data + 1;
218                 obj.data = &id;
219                 ret = osl_update_object(table, row, BLOBCOL_ID, &obj);
220                 if (ret < 0)
221                         return ret;
222         }
223         id--;
224         objs[BLOBCOL_ID].data = &id;
225         objs[BLOBCOL_ID].size = sizeof(id);
226         objs[BLOBCOL_NAME].data = name;
227         objs[BLOBCOL_NAME].size = name_len;
228         objs[BLOBCOL_DEF].data = name + name_len;
229         objs[BLOBCOL_DEF].size = query->size - name_len;
230         return osl_add_row(table, objs);
231 }
232
233 static int com_addblob(callback_function *f, __a_unused int fd, int argc,
234                 const char **argv)
235 {
236         struct osl_object arg_obj;
237
238         if (argc != 2)
239                 return -E_BLOB_SYNTAX;
240         if (!*argv[1]) /* empty name is reserved for the dummy row */
241                 return -E_BLOB_SYNTAX;
242         PARA_NOTICE_LOG("argv[1]: %s\n", argv[1]);
243         arg_obj.size = strlen(argv[1]) + 1;
244         arg_obj.data = (char *)argv[1];
245         return stdin_command(&arg_obj, f, 10 * 1024 * 1024, NULL);
246 }
247
248 static int com_rmblob_callback(struct osl_table *table,
249                 const struct osl_object *query,
250                 __a_unused struct osl_object *result)
251 {
252         char *p = query->data;
253         size_t len;
254         int ret;
255
256         for (; p < (char *)query->data + query->size; p += len + 1) {
257                 struct osl_row *row;
258                 struct osl_object obj;
259
260                 len = strlen(p);
261                 obj.data = p;
262                 obj.size = len + 1;
263                 ret = osl_get_row(table, BLOBCOL_NAME, &obj, &row);
264                 if (ret < 0)
265                         return ret;
266                 ret = osl_del_row(table, row);
267                 if (ret < 0)
268                         return ret;
269         }
270         return 1;
271 }
272
273 static int com_rmblob(callback_function *f, __a_unused int fd, int argc,
274                 const char **argv)
275 {
276         if (argc < 2)
277                 return -E_MOOD_SYNTAX;
278         return send_option_arg_callback_request(NULL, argc - 1, argv + 1, f,
279                 NULL);
280 }
281
282 static int com_mvblob_callback(struct osl_table *table,
283                 const struct osl_object *query,
284                 __a_unused struct osl_object *result)
285 {
286         char *src = (char *) query->data;
287         struct osl_object obj = {.data = src, .size = strlen(src) + 1};
288         char *dest = src + obj.size;
289         struct osl_row *row;
290         int ret = osl_get_row(table, BLOBCOL_NAME, &obj, &row);
291
292         if (ret < 0)
293                 return ret;
294         obj.data = dest;
295         obj.size = strlen(dest) + 1;
296         return osl_update_object(table, row, BLOBCOL_NAME, &obj);
297 }
298
299 static int com_mvblob(callback_function *f,  __a_unused int fd,
300                 int argc, const char **argv)
301 {
302         if (argc != 3)
303                 return -E_MOOD_SYNTAX;
304         return send_option_arg_callback_request(NULL, argc - 1, argv + 1, f,
305                 NULL);
306 }
307
308 #define DEFINE_BLOB_COMMAND(cmd_name, table_name, cmd_prefix) \
309         static int com_ ## cmd_name ## cmd_prefix ## _callback(const struct osl_object *query, \
310                         struct osl_object *output) \
311         { \
312                 return com_ ## cmd_name ## blob_callback(table_name ## _table, query, output); \
313         } \
314         int com_ ## cmd_name ## cmd_prefix(__a_unused int fd, int argc, const char **argv) \
315         { \
316                 return com_ ## cmd_name ## blob(com_ ## cmd_name ## cmd_prefix ## _callback, fd, argc, argv); \
317         }
318
319 static int blob_get_name_by_id(struct osl_table *table, uint32_t id,
320                 char **name)
321 {
322         struct osl_row *row;
323         struct osl_object obj = {.data = &id, .size = sizeof(id)};
324         int ret;
325
326         *name = NULL;
327         if (!id)
328                 return 1;
329         ret = osl_get_row(table, BLOBCOL_ID, &obj, &row);
330         if (ret < 0)
331                 return ret;
332         ret = osl_get_object(table, row, BLOBCOL_NAME, &obj);
333         if (ret < 0)
334                 return ret;
335         *name = (char *)obj.data;
336         return 1;
337 }
338 /** Define the \p get_name_by_id function for this blob type. */
339 #define DEFINE_GET_NAME_BY_ID(table_name, cmd_prefix) \
340         int cmd_prefix ## _get_name_by_id(uint32_t id, char **name) \
341         { \
342                 return blob_get_name_by_id(table_name ## _table, id, name); \
343         }
344
345 /** Define the \p shutdown function for this blob type. */
346 #define DEFINE_BLOB_SHUTDOWN(table_name) \
347         void table_name ## _shutdown(enum osl_close_flags flags) \
348         { \
349                 osl_close_table(table_name ## _table, flags); \
350                 table_name ## _table = NULL; \
351         }
352
353 static int blob_init(struct osl_table **table,
354                 const struct osl_table_description *desc,
355                 struct table_info *ti)
356 {
357         int ret;
358
359         ti->desc = desc;
360         ret = osl_open_table(ti->desc, &ti->table);
361         if (ret >= 0) {
362                 *table = ti->table;
363                 return ret;
364         }
365         *table = NULL;
366         return ret == -E_NOENT? 1 : ret;
367 }
368
369 /** Define the \p init function for this blob type. */
370 #define DEFINE_BLOB_INIT(table_name) \
371         int table_name ## _init(struct table_info *ti) \
372         { \
373                 return blob_init(&table_name ## _table, \
374                         &table_name ## _table_desc, ti); \
375         }
376
377
378 /** Define all functions for this blob type. */
379 #define DEFINE_BLOB_FUNCTIONS(table_name, cmd_prefix) \
380         DEFINE_BLOB_COMMAND(ls, table_name, cmd_prefix) \
381         DEFINE_BLOB_COMMAND(cat, table_name, cmd_prefix) \
382         DEFINE_BLOB_COMMAND(add, table_name, cmd_prefix) \
383         DEFINE_BLOB_COMMAND(rm, table_name, cmd_prefix) \
384         DEFINE_BLOB_COMMAND(mv, table_name, cmd_prefix) \
385         DEFINE_GET_NAME_BY_ID(table_name, cmd_prefix); \
386         DEFINE_BLOB_SHUTDOWN(table_name); \
387         DEFINE_BLOB_INIT(table_name);
388
389 /** \cond doxygen isn't smart enough to recognize these */
390 DEFINE_BLOB_FUNCTIONS(lyrics, lyr);
391 DEFINE_BLOB_FUNCTIONS(images, img);
392 DEFINE_BLOB_FUNCTIONS(moods, mood);
393 DEFINE_BLOB_FUNCTIONS(playlists, pl);
394 /** \endcond */