| 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 | |
|---|
| 20 | struct list_head cbi_widgets; |
|---|
| 21 | int cbi_widget_init = 0; |
|---|
| 22 | |
|---|
| 23 | void 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 | |
|---|
| 33 | struct 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 | |
|---|
| 45 | struct 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 | |
|---|
| 60 | struct 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 | |
|---|
| 80 | void 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 | |
|---|
| 91 | const 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 | |
|---|
| 104 | int 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 | |
|---|
| 112 | void 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 | |
|---|
| 132 | void 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 | |
|---|
| 164 | int 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 | |
|---|
| 178 | struct 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 | |
|---|
| 194 | struct 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 | |
|---|
| 279 | void 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 | |
|---|
| 314 | struct 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 | |
|---|
| 327 | static 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 | |
|---|
| 348 | struct 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 | |
|---|
| 364 | void 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 | |
|---|
| 379 | void 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 | |
|---|
| 396 | void 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 | |
|---|
| 406 | void 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 | |
|---|
| 417 | void 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 | |
|---|
| 425 | void 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 | |
|---|
| 446 | void 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 | |
|---|
| 463 | void 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 | |
|---|
| 474 | void 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 | |
|---|
| 481 | int 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 | |
|---|
| 549 | error: |
|---|
| 550 | return err; |
|---|
| 551 | } |
|---|
| 552 | |
|---|
| 553 | static inline void print_spaces(int d) |
|---|
| 554 | { |
|---|
| 555 | while(d--) |
|---|
| 556 | printf("\t"); |
|---|
| 557 | } |
|---|
| 558 | |
|---|
| 559 | void 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 | |
|---|
| 596 | void 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 | |
|---|
| 608 | void 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 | |
|---|
| 619 | const 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 | |
|---|
| 627 | struct 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 | |
|---|
| 641 | void 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 | |
|---|
| 654 | void 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 | |
|---|
| 667 | void 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 | |
|---|
| 691 | void 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 | |
|---|
| 698 | int 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 | |
|---|
| 708 | struct 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 | |
|---|
| 726 | void 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 | } |
|---|