root/luci2/cbi2/cbi.c @ 5709

Revision 5709, 16.0 KB (checked in by blogic, 3 years ago)

cleanup widgets

Line 
1#include <stdio.h>
2#include <stdlib.h>
3#include <stdarg.h>
4#include <string.h>
5#include <assert.h>
6#include <fcntl.h>
7#include <unistd.h>
8#include <sys/types.h>
9#include <sys/stat.h>
10#include <sys/mman.h>
11#include <glob.h>
12#include <libgen.h>
13
14#include "lucic.h"
15#include "list.h"
16#include "blob.h"
17#include "cbi.h"
18#include "json.h"
19
20struct list_head cbi_widgets;
21int cbi_widget_init = 0;
22
23void cbi_widget_register(char *name, struct cbi_widget *w)
24{
25    if(!cbi_widget_init)
26    {
27        INIT_LIST_HEAD(&cbi_widgets);
28        cbi_widget_init = 1;
29    }
30    list_add_tail(&w->list, &cbi_widgets);
31}
32
33struct cbi_widget* cbi_widget_find(const char *name)
34{
35    struct list_head *p;
36    list_for_each(p, &cbi_widgets)
37    {
38        struct cbi_widget *w = container_of(p, struct cbi_widget, list);
39        if(!strcmp(w->name, name))
40            return w;
41    }
42    return 0;
43}
44
45struct cbi_ctx* cbi_alloc_ctx(void)
46{
47    struct cbi_ctx *ctx = malloc(sizeof(struct cbi_ctx));
48
49    assert(ctx != NULL);
50    memset(ctx, 0, sizeof(struct cbi_ctx));
51    INIT_LIST_HEAD(&ctx->files);
52    INIT_LIST_HEAD(&ctx->pages);
53    INIT_LIST_HEAD(&ctx->globals);
54    INIT_LIST_HEAD(&ctx->menus);
55    INIT_LIST_HEAD(&ctx->elements);
56
57    return ctx;
58}
59
60struct cbi_property* cbi_parse_properties(struct blob_attr *prop)
61{
62    int rem = blob_len(prop);
63    struct cbi_property *p = malloc(sizeof(struct cbi_property));
64
65    assert(p != NULL);
66    memset(p, 0, sizeof(struct cbi_property));
67    prop = blob_data(prop);
68    assert(blob_id(prop) == CBI_KEY);
69    assert(blob_pad_len(prop) < rem);
70    p->key = strdup(blob_get_string(prop));
71    rem -= blob_pad_len(prop);
72    prop = blob_next(prop);
73    assert(blob_id(prop) == CBI_VALUE);
74    assert(blob_pad_len(prop) == rem);
75    p->value = strdup(blob_get_string(prop));
76    p->generated = 0;
77    return p;
78}
79
80void cbi_add_property(struct cbi_element *e, const char *name, const char *val, int generated)
81{
82    struct cbi_property *p = malloc(sizeof(struct cbi_property));
83
84    memset(p, 0, sizeof(struct cbi_property));
85    p->key = strdup(name);
86    p->value = strdup(val);
87    p->generated = generated;
88    list_add_tail(&p->list, &e->properties);
89}
90
91const char* cbi_find_prop(struct cbi_element *e, const char *key)
92{
93    struct list_head *q;
94
95    list_for_each(q, &e->properties)
96    {
97        struct cbi_property *p = container_of(q, struct cbi_property, list);
98        if(!strcmp(p->key, key))
99            return p->value;
100    }
101    return 0;
102}
103
104int cbi_find_prop_int(struct cbi_element *e, char *key, int def)
105{
106    const char *c = cbi_find_prop(e, key);
107    if(c)
108        return atoi(c);
109    return def;
110}
111
112void cbi_get_element_id(struct cbi_element *e, char *buffer, int file)
113{
114    const char *id;
115    if(!e)
116        return;
117    cbi_get_element_id(e->parent, buffer, file);
118    id = cbi_find_prop(e, "id");
119    if(!id && (CAPS(e) & CAP_DATA))
120        id = cbi_find_prop(e, "src");
121    if(!id)
122        return;
123    if(e->parent)
124    {   if(e->parent->parent)
125            strcat(buffer, ".");
126        else
127            strcat(buffer, ":");
128    }
129    strcat(buffer, id);
130}
131
132void cbi_gen_default_id(struct cbi_element *e, int *count)
133{
134    char tmp[128];
135    char buffer[256];
136    const char *id = cbi_find_prop(e, "id");
137    if(id)
138        return;
139    if(e->w)
140        if(e->w->caps & CAP_DATA)
141            id = cbi_find_prop(e, "src");
142    if(!id)
143        id = cbi_find_prop(e, "name");
144    if(id)
145    {
146        const char *a, *b;
147        a = b = id;
148        while((a = strstr(b, ".")))
149            b = ++a;
150        if(*b == '@')
151            b++;
152        cbi_add_property(e, "id", b, 1);
153        return;
154    }
155    snprintf(tmp, 127, "%s%d", e->widget, *count);
156    tmp[127] = '\0';
157    cbi_add_property(e, "id", tmp, 1);
158    *buffer = 0;
159    cbi_get_element_id(e->parent, buffer, 1);
160    LOG("generating id for %s\n", tmp);
161    (*count)++;
162}
163
164int cbi_init_element(struct cbi_ctx *ctx, struct cbi_element *e, int *count)
165{
166    e->w = cbi_widget_find(e->widget);
167    if(!(e->w))
168        return 1;
169    list_add_tail(&e->widget_ref, &e->w->refs);
170    if(widget_find_prop(e->w, "id"))
171        cbi_gen_default_id(e, count);
172    if(e->w->init)
173        if(e->w->init(ctx, e))
174            return 1;
175    return 0;
176}
177
178struct cbi_element* cbi_find_element(struct cbi_element *e, const char *id)
179{
180    struct list_head *p;
181    list_for_each(p, &e->elements)
182    {
183        struct cbi_element *e = container_of(p, struct cbi_element, list);
184        const char *id2 = cbi_find_prop(e, "id");
185        if(id2)
186        {
187            if(!strcmp(id, id2))
188                return e;
189        }
190    }
191    return 0;
192}
193
194struct cbi_element* cbi_parse_element(struct cbi_ctx *ctx, struct blob_attr *attr, int *count, struct cbi_element *parent)
195{
196    struct blob_attr *element, *element2;
197    int rem = blob_len(attr), rem2;
198    struct cbi_element *e = malloc(sizeof(struct cbi_element));
199    int sub_count = 0;
200    assert(e != NULL);
201    memset(e, 0, sizeof(struct cbi_element));
202    INIT_LIST_HEAD(&e->properties);
203    INIT_LIST_HEAD(&e->elements);
204
205    attr = blob_data(attr);
206    assert(blob_id(attr) == CBI_NAMESPACE);
207    e->namespace = strdup(blob_get_string(attr));
208    assert(blob_pad_len(attr) < rem);
209    rem -= blob_pad_len(attr);
210    attr = blob_next(attr);
211    assert(blob_id(attr) == CBI_WIDGET);
212    e->widget = strdup(blob_get_string(attr));
213    assert(blob_pad_len(attr) <= rem);
214    e->parent = parent;
215    rem -= blob_pad_len(attr);
216    rem2 = rem;
217    element2 = element = blob_next(attr);
218    for(;
219        (blob_pad_len(element) <= rem) && (blob_pad_len(element) >= sizeof(struct blob_attr));
220        rem -= blob_pad_len(element), element = blob_next(element))
221    {
222        struct cbi_property *p;
223        switch(blob_id(element))
224        {
225        case CBI_VALUE:
226            e->value = strdup(blob_get_string(element));
227            break;
228        case CBI_PROPERTY:
229            p = cbi_parse_properties(element);
230            list_add_tail(&p->list, &e->properties);
231            break;
232        case CBI_ELEMENT:
233            break;
234        default:
235            LOG("unknown blob_id %d unknown\n", blob_id(element));
236            abort();
237            break;
238        }
239    }
240    if(cbi_init_element(ctx, e, count))
241    {
242        LOG("unknown widget type %s\n", e->widget);
243        free(e);
244        return 0;
245    }
246    element = element2;
247    rem = rem2;
248    for(;
249        (blob_pad_len(element2) <= rem2) && (blob_pad_len(element2) >= sizeof(struct blob_attr));
250        rem2 -= blob_pad_len(element2), element2 = blob_next(element2))
251    {
252        struct cbi_element *sub_e;
253        switch(blob_id(element2))
254        {
255        case CBI_PROPERTY:
256        case CBI_VALUE:
257            break;
258        case CBI_ELEMENT:
259            sub_e = cbi_parse_element(ctx, element2, &sub_count, e);
260            if(sub_e)
261                list_add_tail(&sub_e->list, &e->elements);
262            break;
263        default:
264            LOG("unknown blob_id %d unknown\n", blob_id(element2));
265            abort();
266            break;
267        }
268    }
269    if(e->w->caps & CAP_TEMPLATE)
270    {
271        e->blob = (struct cbi_blob*)malloc(sizeof(struct cbi_blob));
272        e->blob->rem = rem;
273        e->blob->blob = malloc(rem);
274        memcpy(e->blob->blob, element, rem);
275    }
276    return e;
277}
278
279void cbi_create_instance(struct cbi_ctx *ctx, struct cbi_element *e1, struct cbi_element *e2)
280{
281    struct blob_attr *element;
282    int rem;
283    int sub_count = 0;
284    if(!e2->blob)
285    {
286        CBILOG(e2, "failed to create instance\n");
287        return;
288    }
289    element = (struct blob_attr*)e2->blob->blob;
290    rem = e2->blob->rem;
291    for(;
292        (blob_pad_len(element) <= rem) && (blob_pad_len(element) >= sizeof(struct blob_attr));
293        rem -= blob_pad_len(element), element = blob_next(element))
294    {
295        struct cbi_element *sub_e;
296        switch(blob_id(element))
297        {
298        case CBI_PROPERTY:
299        case CBI_VALUE:
300            break;
301        case CBI_ELEMENT:
302            sub_e = cbi_parse_element(ctx, element, &sub_count, e1);
303            if(sub_e)
304                list_add_tail(&sub_e->list, &e1->elements);
305            break;
306        default:
307            LOG("unknown blob_id %d unknown\n", blob_id(element));
308            abort();
309            break;
310        }
311    }
312}
313
314struct cbi_file* cbi_find_file(struct cbi_ctx *ctx, const char *name)
315{
316    struct list_head *p;
317
318    list_for_each(p, &ctx->files)
319    {
320        struct cbi_file *f = container_of(p, struct cbi_file, list);
321        if(!strcmp(f->file, name))
322            return f;
323    }
324    return 0;
325}
326
327static struct cbi_element* cbi_resolv_element(struct cbi_element *e, char *ref)
328{
329    char *tok;
330    struct cbi_element *next;
331    tok = strstr(ref, ".");
332    if(!tok)
333    {
334        const char *id = cbi_find_prop(e, "id");
335        if(!strcmp(id, ref))
336            return e;
337        else
338            return 0;
339    }
340    *tok = '\0';
341    tok++;
342    next = cbi_find_element(e, tok);
343    if(next)
344        return cbi_resolv_element(next, tok);
345    return 0;
346}
347
348struct cbi_element* cbi_resolv(struct cbi_ctx *ctx, const char *ref)
349{
350    struct cbi_element *ret = 0;
351    struct list_head *p;
352    list_for_each(p, &ctx->elements)
353    {
354        struct cbi_element *e2 = container_of(p, struct cbi_element, list);
355        char *buf = strdup(ref);
356        ret = cbi_resolv_element(e2, buf);
357        free(buf);
358        if(ret)
359            break;
360    }
361    return ret;
362}
363
364void cbi_index_verify(struct cbi_ctx *ctx, struct cbi_element *e)
365{
366    struct list_head *p;
367    if(e->w->caps & CAP_TEMPLATE)
368        return;
369    widget_verify(ctx, e);
370    if(e->broken)
371        return;
372    list_for_each(p, &e->elements)
373    {
374        struct cbi_element *e = container_of(p, struct cbi_element, list);
375        cbi_index_verify(ctx, e);
376    }
377}
378
379void cbi_index_add(struct cbi_ctx *ctx, struct cbi_file *f)
380{
381    struct list_head *p;
382    LOG("indexing %s\n", f->name);
383    list_for_each(p, &f->e->elements)
384    {
385        struct cbi_element *e = container_of(p, struct cbi_element, list);
386        cbi_index_verify(ctx, e);
387        if(CAPS(e) & CAP_PAGE)
388        {
389            list_add_tail(&e->index, &ctx->pages);
390        } else {
391            list_add_tail(&e->index, &ctx->globals);
392        }
393    }
394}
395
396void cbi_index(struct cbi_ctx *ctx)
397{
398    struct list_head *p;
399    list_for_each(p, &ctx->files)
400    {
401        struct cbi_file *f = container_of(p, struct cbi_file, list);
402        cbi_index_add(ctx, f);
403    }
404}
405
406void cbi_index_remove(struct cbi_file *f)
407{
408    struct list_head *p;
409    LOG("unindexing %s\n", f->name);
410    list_for_each(p, &f->e->elements)
411    {
412        struct cbi_element *e = container_of(p, struct cbi_element, list);
413        list_del(&e->index);
414    }
415}
416
417void cbi_free_property(struct cbi_property *prop)
418{
419    free(prop->key);
420    free(prop->value);
421    list_del(&prop->list);
422    free(prop);
423}
424
425void cbi_free_element(struct cbi_element *element)
426{
427    struct list_head *p, *q;
428    list_for_each_safe(p, q, &element->elements)
429    {
430        struct cbi_element *e = container_of(p, struct cbi_element, list);
431        cbi_free_element(e);
432    }
433    list_for_each_safe(p, q, &element->properties)
434    {
435        struct cbi_property *r = container_of(p, struct cbi_property, list);
436        cbi_free_property(r);
437    }
438    free(element->namespace);
439    free(element->widget);
440    if(element->value)
441        free(element->value);
442    list_del(&element->list);
443    free(element);
444}
445
446void cbi_free_file(struct cbi_ctx *ctx, struct cbi_file *file)
447{
448    struct list_head *p, *q;
449    cbi_index_remove(file);
450    LOG("dropping %s\n", file->name);
451    list_for_each_safe(p, q, &file->e->elements)
452    {
453        struct cbi_element *e = container_of(p, struct cbi_element, list);
454        cbi_free_element(e);
455    }
456    free(file->name);
457    free(file->file);
458    list_del(&file->list);
459    list_del(&file->e->list);
460    free(file);
461}
462
463void cbi_free_context(struct cbi_ctx *ctx)
464{
465    struct list_head *p, *q;
466    list_for_each_safe(p, q, &ctx->files)
467    {
468        struct cbi_file *f = container_of(p, struct cbi_file, list);
469        cbi_free_file(ctx, f);
470    }
471    free(ctx);
472}
473
474void cbi_index_flush(struct cbi_ctx *ctx)
475{
476    INIT_LIST_HEAD(&ctx->pages);
477    INIT_LIST_HEAD(&ctx->globals);
478    INIT_LIST_HEAD(&ctx->menus);
479}
480
481int cbi_add_file(struct cbi_ctx *ctx, char *filename, int index)
482{
483    struct stat st;
484    int fd, err, rem;
485    void *map = NULL;
486    struct cbi_file *f;
487    struct blob_attr *pos;
488    struct stat s;
489    int count = 0;
490    assert(ctx != NULL);
491    f = cbi_find_file(ctx, filename);
492    if(f)
493    {
494        if(!stat(filename, &s))
495        {
496            if(f->mtime == s.st_mtime)
497                return 0;
498            cbi_free_file(ctx, f);
499            f = 0;
500        }
501    }
502    LOG("loading %s\n", filename);
503    fd = open(filename, O_RDONLY);
504    if(fd < 0)
505        return fd;
506    err = fstat(fd, &st);
507    if (err < 0)
508        goto error;
509    err = -EINVAL;
510    if (st.st_size == 0)
511        goto error;
512    map = mmap(NULL, st.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
513    if (map == MAP_FAILED)
514        goto error;
515    if (blob_pad_len(map) > st.st_size)
516        goto error;
517    err = -ENOMEM;
518    f = malloc(sizeof(struct cbi_file));
519    assert(f != NULL);
520    memset(f, 0, sizeof(struct cbi_file));
521    f->file = strdup(filename);
522    f->name = strdup(basename(filename));
523    f->name[strlen(f->name) - 5] = '\0';
524    f->mtime = st.st_mtime;
525    f->e = malloc(sizeof(struct cbi_element));
526    memset(f->e, 0, sizeof(struct cbi_element));
527    INIT_LIST_HEAD(&f->e->elements);
528    INIT_LIST_HEAD(&f->e->properties);
529    f->e->namespace = strdup("luci");
530    f->e->widget = strdup("file");
531    f->e->w = cbi_widget_find("file");
532    cbi_add_property(f->e, "src", f->name, 1);
533    cbi_add_property(f->e, "id", f->name, 1);
534    blob_for_each_attr(pos, map, rem)
535    {
536        struct cbi_element *e;
537        if(blob_id(pos) != CBI_ELEMENT)
538            continue;
539        e = cbi_parse_element(ctx, pos, &count, 0);
540        if(e)
541            list_add_tail(&e->list, &f->e->elements);
542    }
543    list_add_tail(&f->e->list, &ctx->elements);
544    list_add_tail(&f->list, &ctx->files);
545    if(index)
546        cbi_index_add(ctx, f);
547    return 0;
548
549error:
550    return err;
551}
552
553static inline void print_spaces(int d)
554{
555    while(d--)
556        printf("\t");
557}
558
559void cbi_dump_element(struct cbi_element *e, int depth)
560{
561    struct list_head *p;
562    char nl = (!list_empty(&e->elements))?('\n'):('\0');
563    if(e->broken)
564        return;
565    if(e->w->caps & CAP_TEMPLATE)
566        return;
567    print_spaces(depth);
568    printf("<%s:%s", e->namespace, e->widget);
569    list_for_each(p, &e->properties)
570    {
571        struct cbi_property *q = container_of(p, struct cbi_property, list);
572        printf(" %s=\"%s\"", q->key, q->value);
573    }
574    if(!nl && !e->value)
575        printf("/");
576    printf(">%c", nl);
577    if(e->value)
578    {
579        if(nl)
580            print_spaces(depth + 1);
581        printf("%s%c", e->value, nl);
582    }
583    list_for_each(p, &e->elements)
584    {
585        struct cbi_element *e = container_of(p, struct cbi_element, list);
586        cbi_dump_element(e, depth + 1);
587    }
588    if(nl)
589        print_spaces(depth);
590    if(nl || e->value)
591        printf("</%s:%s>\n", e->namespace, e->widget);
592    else
593        printf("\n");
594}
595
596void cbi_dump_list(struct list_head *h)
597{
598    struct list_head *p;
599    list_for_each(p, h)
600    {
601        struct cbi_element *e = container_of(p, struct cbi_element, index);
602        const char *id = cbi_find_prop(e, "id");
603        if(id)
604            printf("%s -> %s\n", e->widget, id);
605    }
606}
607
608void cbi_dump(struct cbi_ctx *ctx)
609{
610    struct list_head *p;
611
612    list_for_each(p, &ctx->elements)
613    {
614        struct cbi_element *e = container_of(p, struct cbi_element, list);
615        cbi_dump_element(e, 0);
616    }
617}
618
619const char* cbi_virt_path(struct cbi_ctx *ctx, const char *path, const char *file)
620{
621    static char buf[256];
622    buf[255] = '\0';
623    snprintf(buf, 255, "%s%s%s", (ctx->vroot)?(ctx->vroot):(""), path, (file)?(file):(""));
624    return buf;
625}
626
627struct cbi_ctx* cbi_load_files(struct cbi_ctx *ctx)
628{
629    glob_t gl;
630    int gl_flags = GLOB_NOESCAPE | GLOB_NOSORT | GLOB_MARK;
631    if(glob(cbi_virt_path(ctx, ctx->path, "*.lucio"), gl_flags, NULL, &gl) >= 0)
632    {
633        int i;
634        for(i = 0; i < gl.gl_pathc; i++)
635            cbi_add_file(ctx, gl.gl_pathv[i], 0);
636        globfree(&gl);
637    }
638    return ctx;
639}
640
641void cbi_check_old_files(struct cbi_ctx *ctx)
642{
643    struct list_head *p, *q;
644
645    list_for_each_safe(p, q, &ctx->files)
646    {
647        struct cbi_file *f = container_of(p, struct cbi_file, list);
648        struct stat s;
649        if(stat(f->file, &s))
650            cbi_free_file(ctx, f);
651    }
652}
653
654void cbi_check_new_files(struct cbi_ctx *ctx)
655{
656    glob_t gl;
657    int gl_flags = GLOB_NOESCAPE | GLOB_NOSORT | GLOB_MARK;
658    if(glob(cbi_virt_path(ctx, ctx->path, "*.luco"), gl_flags, NULL, &gl) >= 0)
659    {
660        int i;
661        for(i = 0; i < gl.gl_pathc; i++)
662            cbi_add_file(ctx, gl.gl_pathv[i], 1);
663        globfree(&gl);
664    }
665}
666
667void cbi_check_new_files_src(struct cbi_ctx *ctx)
668{
669    glob_t gl;
670    int gl_flags = GLOB_NOESCAPE | GLOB_NOSORT | GLOB_MARK;
671    if(glob(cbi_virt_path(ctx, ctx->path, "*.luci"), gl_flags, NULL, &gl) >= 0)
672    {
673        int i;
674        for(i = 0; i < gl.gl_pathc; i++)
675        {
676            char *out = strdup(gl.gl_pathv[i]);
677            out[strlen(out) - 1] = 'o';
678            LOG("compiling %s -> %s\n", gl.gl_pathv[i], out);
679            if(!lucic_main(gl.gl_pathv[i], out))
680            {
681                LOG("deleting %s\n", gl.gl_pathv[i]);
682                unlink(gl.gl_pathv[i]);
683            } else {
684                LOG("failed\n");
685            }
686        }
687        globfree(&gl);
688    }
689}
690
691void cbi_reindex(struct cbi_ctx *ctx)
692{
693    cbi_check_old_files(ctx);
694    cbi_check_new_files_src(ctx);
695    cbi_check_new_files(ctx);
696}
697
698int cbi_dump_blob(char *file)
699{
700    struct cbi_ctx *ctx = cbi_alloc_ctx();
701    cbi_add_file(ctx, file, 0);
702    cbi_index(ctx);
703    cbi_dump(ctx);
704    cbi_free_context(ctx);
705    return 0;
706}
707
708struct cbi_ctx* cbi_init(const char *vroot, const char *path)
709{
710    struct cbi_ctx *ctx = cbi_alloc_ctx();
711    if(!path)
712        ctx->path = strdup("/lib/luci2/");
713    else
714        ctx->path = strdup(path);
715    if(vroot)
716        ctx->vroot = strdup(vroot);
717    else
718        ctx->vroot = 0;
719    validate_init(ctx);
720    cbi_load_files(ctx);
721    cbi_index(ctx);
722    cbi_reindex(ctx);
723    return ctx;
724}
725
726void cbi_request(struct cbi_ctx *ctx, char *request)
727{
728    json_object *out, *in = json_tokener_parse(request);
729    if(is_error(in))
730    {
731        printf("bad json\n");
732        return;
733    }
734    out = json_request(ctx, in);
735    if(out)
736    {
737        printf("res -> %s\n", json_object_to_json_string(out));
738        json_object_put(out);
739    }
740    json_object_put(in);
741}
Note: See TracBrowser for help on using the browser.