| | 318 | static u_int64_t |
|---|
| | 319 | usec_now() { |
|---|
| | 320 | u_int64_t usec; |
|---|
| | 321 | struct timeval tv; |
|---|
| | 322 | gettimeofday(&tv, NULL); |
|---|
| | 323 | usec = tv.tv_sec * 1000000UL; |
|---|
| | 324 | usec += tv.tv_usec; |
|---|
| | 325 | return usec; |
|---|
| | 326 | } |
|---|
| | 327 | void |
|---|
| | 328 | noit_conf_backingstore_remove(noit_conf_section_t vnode) { |
|---|
| | 329 | xmlNodePtr node = vnode; |
|---|
| | 330 | noit_xml_userdata_t *subctx = node->_private; |
|---|
| | 331 | if(subctx) { |
|---|
| | 332 | noitL(noit_debug, "marking %s for removal\n", subctx->path); |
|---|
| | 333 | if(!backingstore_freelist) backingstore_freelist = subctx; |
|---|
| | 334 | else { |
|---|
| | 335 | noit_xml_userdata_t *fl = backingstore_freelist; |
|---|
| | 336 | while(fl->freelist) fl = fl->freelist; |
|---|
| | 337 | fl->freelist = subctx; |
|---|
| | 338 | } |
|---|
| | 339 | node->_private = NULL; |
|---|
| | 340 | } |
|---|
| | 341 | /* If we're deleted, we'll mark the parent as dirty */ |
|---|
| | 342 | if(node->parent) noit_conf_backingstore_dirty(node->parent); |
|---|
| | 343 | } |
|---|
| | 344 | void |
|---|
| | 345 | noit_conf_backingstore_dirty(noit_conf_section_t vnode) { |
|---|
| | 346 | xmlNodePtr node = vnode; |
|---|
| | 347 | noit_xml_userdata_t *subctx = node->_private; |
|---|
| | 348 | if(subctx) { |
|---|
| | 349 | subctx->dirty_time = usec_now(); |
|---|
| | 350 | return; |
|---|
| | 351 | } |
|---|
| | 352 | if(node->parent) noit_conf_backingstore_dirty(node->parent); |
|---|
| | 353 | } |
|---|
| | 354 | int |
|---|
| | 355 | noit_conf_backingstore_write(noit_xml_userdata_t *ctx, noit_boolean skip, |
|---|
| | 356 | xmlAttrPtr attrs, xmlNodePtr node) { |
|---|
| | 357 | int failure = 0; |
|---|
| | 358 | char newpath[PATH_MAX]; |
|---|
| | 359 | xmlNodePtr n; |
|---|
| | 360 | snprintf(newpath, sizeof(newpath), "%s/.attrs", ctx->path); |
|---|
| | 361 | if(attrs) { |
|---|
| | 362 | xmlDocPtr tmpdoc; |
|---|
| | 363 | xmlNodePtr tmpnode; |
|---|
| | 364 | noitL(noit_debug, " **> %s\n", newpath); |
|---|
| | 365 | tmpdoc = xmlNewDoc((xmlChar *)"1.0"); |
|---|
| | 366 | tmpnode = xmlNewNode(NULL, ctx->name ? (xmlChar *)ctx->name : (xmlChar *)"stub"); |
|---|
| | 367 | xmlDocSetRootElement(tmpdoc, tmpnode); |
|---|
| | 368 | tmpnode->properties = attrs; |
|---|
| | 369 | failure = noit_xmlSaveToFile(tmpdoc, newpath); |
|---|
| | 370 | tmpnode->properties = NULL; |
|---|
| | 371 | xmlFreeDoc(tmpdoc); |
|---|
| | 372 | if(failure) return -1; |
|---|
| | 373 | } |
|---|
| | 374 | else if(!skip) { |
|---|
| | 375 | unlink(newpath); |
|---|
| | 376 | } |
|---|
| | 377 | for(n = node; n; n = n->next) { |
|---|
| | 378 | int leaf; |
|---|
| | 379 | noit_xml_userdata_t *subctx; |
|---|
| | 380 | subctx = n->_private; |
|---|
| | 381 | leaf = is_stopnode(n); |
|---|
| | 382 | if(!subctx) { /* This has never been written out */ |
|---|
| | 383 | subctx = calloc(1, sizeof(*subctx)); |
|---|
| | 384 | subctx->name = strdup((char *)n->name); |
|---|
| | 385 | snprintf(newpath, sizeof(newpath), "%s/%s#%llu", ctx->path, n->name, ++max_gen_count); |
|---|
| | 386 | if(leaf) strlcat(newpath, ".xml", sizeof(newpath)); |
|---|
| | 387 | subctx->path = strdup(newpath); |
|---|
| | 388 | subctx->dirty_time = usec_now(); |
|---|
| | 389 | n->_private = subctx; |
|---|
| | 390 | noitL(noit_debug, " !!> %s\n", subctx->path); |
|---|
| | 391 | } |
|---|
| | 392 | if(leaf) { |
|---|
| | 393 | xmlDocPtr tmpdoc; |
|---|
| | 394 | xmlNodePtr tmpnode; |
|---|
| | 395 | if(subctx->dirty_time > last_config_flush) { |
|---|
| | 396 | tmpdoc = xmlNewDoc((xmlChar *)"1.0"); |
|---|
| | 397 | tmpnode = xmlNewNode(NULL, n->name); |
|---|
| | 398 | xmlDocSetRootElement(tmpdoc, tmpnode); |
|---|
| | 399 | tmpnode->properties = n->properties; |
|---|
| | 400 | tmpnode->children = n->children; |
|---|
| | 401 | failure = noit_xmlSaveToFile(tmpdoc, subctx->path); |
|---|
| | 402 | tmpnode->properties = NULL; |
|---|
| | 403 | tmpnode->children = NULL; |
|---|
| | 404 | xmlFreeDoc(tmpdoc); |
|---|
| | 405 | noitL(noit_debug, " ==> %s\n", subctx->path); |
|---|
| | 406 | if(failure) return -1; |
|---|
| | 407 | } |
|---|
| | 408 | } |
|---|
| | 409 | else { |
|---|
| | 410 | noit_boolean skip_attrs; |
|---|
| | 411 | skip_attrs = leaf || (subctx->dirty_time <= last_config_flush); |
|---|
| | 412 | noitL(noit_debug, " --> %s\n", subctx->path); |
|---|
| | 413 | if(noit_conf_backingstore_write(subctx, skip_attrs, skip_attrs ? NULL : n->properties, n->children)) |
|---|
| | 414 | return -1; |
|---|
| | 415 | } |
|---|
| | 416 | } |
|---|
| | 417 | return 0; |
|---|
| | 418 | } |
|---|
| | 419 | void |
|---|
| | 420 | noit_conf_shatter_write(xmlDocPtr doc) { |
|---|
| | 421 | if(backingstore_freelist) { |
|---|
| | 422 | noit_xml_userdata_t *fl, *last; |
|---|
| | 423 | for(fl = backingstore_freelist; fl; ) { |
|---|
| | 424 | last = fl; |
|---|
| | 425 | fl = fl->freelist; |
|---|
| | 426 | /* If it is a file, we'll unlink it, otherwise, |
|---|
| | 427 | * we need to delete the attributes and the directory. |
|---|
| | 428 | */ |
|---|
| | 429 | if(unlink(last->path)) { |
|---|
| | 430 | char attrpath[PATH_MAX]; |
|---|
| | 431 | snprintf(attrpath, sizeof(attrpath), "%s/.attrs", last->path); |
|---|
| | 432 | unlink(attrpath); |
|---|
| | 433 | if(rmdir(last->path) && errno != ENOENT) { |
|---|
| | 434 | /* This shouldn't happen, but if it does we risk |
|---|
| | 435 | * leaving a mess. Don't do that. |
|---|
| | 436 | */ |
|---|
| | 437 | noitL(noit_error, "backingstore mess %s: %s\n", |
|---|
| | 438 | last->path, strerror(errno)); |
|---|
| | 439 | } |
|---|
| | 440 | } |
|---|
| | 441 | noit_xml_userdata_free(last); |
|---|
| | 442 | } |
|---|
| | 443 | backingstore_freelist = NULL; |
|---|
| | 444 | } |
|---|
| | 445 | if(backingstore_include_nodes) { |
|---|
| | 446 | int i; |
|---|
| | 447 | for(i=0; i<backingstore_include_cnt; i++) { |
|---|
| | 448 | if(backingstore_include_nodes[i].doc) { |
|---|
| | 449 | xmlNodePtr n; |
|---|
| | 450 | noit_xml_userdata_t *what = backingstore_include_nodes[i].doc->_private; |
|---|
| | 451 | |
|---|
| | 452 | for(n=backingstore_include_nodes[i].insertion_point->children; |
|---|
| | 453 | n; n = n->next) |
|---|
| | 454 | n->parent = backingstore_include_nodes[i].root; |
|---|
| | 455 | backingstore_include_nodes[i].insertion_point->children = |
|---|
| | 456 | backingstore_include_nodes[i].old_children; |
|---|
| | 457 | noit_conf_backingstore_write(what, noit_false, NULL, backingstore_include_nodes[i].root->children); |
|---|
| | 458 | } |
|---|
| | 459 | } |
|---|
| | 460 | last_config_flush = usec_now(); |
|---|
| | 461 | } |
|---|
| | 462 | } |
|---|
| | 463 | void |
|---|
| | 464 | noit_conf_shatter_postwrite(xmlDocPtr doc) { |
|---|
| | 465 | if(backingstore_include_nodes) { |
|---|
| | 466 | int i; |
|---|
| | 467 | for(i=0; i<backingstore_include_cnt; i++) { |
|---|
| | 468 | if(backingstore_include_nodes[i].doc) { |
|---|
| | 469 | xmlNodePtr n; |
|---|
| | 470 | backingstore_include_nodes[i].insertion_point->children = |
|---|
| | 471 | backingstore_include_nodes[i].root->children; |
|---|
| | 472 | for(n=backingstore_include_nodes[i].insertion_point->children; |
|---|
| | 473 | n; n = n->next) |
|---|
| | 474 | n->parent = backingstore_include_nodes[i].insertion_point; |
|---|
| | 475 | } |
|---|
| | 476 | } |
|---|
| | 477 | } |
|---|
| | 478 | } |
|---|
| | 479 | |
|---|
| | 480 | int |
|---|
| | 481 | noit_conf_read_into_node(xmlNodePtr node, const char *path) { |
|---|
| | 482 | DIR *dirroot; |
|---|
| | 483 | struct dirent *de, *entry; |
|---|
| | 484 | char filepath[PATH_MAX]; |
|---|
| | 485 | xmlDocPtr doc; |
|---|
| | 486 | xmlNodePtr root = NULL; |
|---|
| | 487 | xmlAttrPtr a; |
|---|
| | 488 | struct stat sb; |
|---|
| | 489 | int size, rv; |
|---|
| | 490 | |
|---|
| | 491 | noitL(noit_debug, "read backing store: %s\n", path); |
|---|
| | 492 | snprintf(filepath, sizeof(filepath), "%s/.attrs", path); |
|---|
| | 493 | while((rv = stat(filepath, &sb)) < 0 && errno == EINTR); |
|---|
| | 494 | if(rv == 0) { |
|---|
| | 495 | doc = xmlParseFile(filepath); |
|---|
| | 496 | if(doc) root = xmlDocGetRootElement(doc); |
|---|
| | 497 | if(doc && root) { |
|---|
| | 498 | node->properties = root->properties; |
|---|
| | 499 | for(a = node->properties; a; a = a->next) { |
|---|
| | 500 | a->parent = node; |
|---|
| | 501 | a->doc = node->doc; |
|---|
| | 502 | } |
|---|
| | 503 | root->properties = NULL; |
|---|
| | 504 | xmlFreeDoc(doc); |
|---|
| | 505 | doc = NULL; |
|---|
| | 506 | } |
|---|
| | 507 | } |
|---|
| | 508 | #ifdef _PC_NAME_MAX |
|---|
| | 509 | size = pathconf(path, _PC_NAME_MAX); |
|---|
| | 510 | #endif |
|---|
| | 511 | size = MAX(size, PATH_MAX + 128); |
|---|
| | 512 | de = alloca(size); |
|---|
| | 513 | dirroot = opendir(path); |
|---|
| | 514 | if(!dirroot) return -1; |
|---|
| | 515 | while(portable_readdir_r(dirroot, de, &entry) == 0 && entry != NULL) { |
|---|
| | 516 | noit_xml_userdata_t *udata; |
|---|
| | 517 | char name[PATH_MAX]; |
|---|
| | 518 | char *sep; |
|---|
| | 519 | xmlNodePtr child; |
|---|
| | 520 | u_int64_t gen; |
|---|
| | 521 | |
|---|
| | 522 | sep = strchr(entry->d_name, '#'); |
|---|
| | 523 | if(!sep) continue; |
|---|
| | 524 | snprintf(filepath, sizeof(filepath), "%s/%s", path, entry->d_name); |
|---|
| | 525 | while((rv = stat(filepath, &sb)) < 0 && errno == EINTR); |
|---|
| | 526 | if(rv == 0) { |
|---|
| | 527 | strlcpy(name, entry->d_name, sizeof(name)); |
|---|
| | 528 | name[sep - entry->d_name] = '\0'; |
|---|
| | 529 | gen = strtoull(sep+1, NULL, 10); |
|---|
| | 530 | if(gen > max_gen_count) max_gen_count = gen; |
|---|
| | 531 | |
|---|
| | 532 | if(S_ISDIR(sb.st_mode)) { |
|---|
| | 533 | noitL(noit_debug, "<DIR< %s\n", entry->d_name); |
|---|
| | 534 | child = xmlNewNode(NULL, (xmlChar *)name); |
|---|
| | 535 | noit_conf_read_into_node(child, filepath); |
|---|
| | 536 | udata = calloc(1, sizeof(*udata)); |
|---|
| | 537 | udata->name = strdup(name); |
|---|
| | 538 | udata->path = strdup(filepath); |
|---|
| | 539 | child->_private = udata; |
|---|
| | 540 | xmlAddChild(node, child); |
|---|
| | 541 | } |
|---|
| | 542 | else if(S_ISREG(sb.st_mode)) { |
|---|
| | 543 | xmlDocPtr cdoc; |
|---|
| | 544 | xmlNodePtr cnode = NULL; |
|---|
| | 545 | noitL(noit_debug, "<FILE< %s\n", entry->d_name); |
|---|
| | 546 | cdoc = xmlParseFile(filepath); |
|---|
| | 547 | if(cdoc) { |
|---|
| | 548 | cnode = xmlDocGetRootElement(cdoc); |
|---|
| | 549 | xmlDocSetRootElement(cdoc, xmlNewNode(NULL, (xmlChar *)"dummy")); |
|---|
| | 550 | if(cnode) { |
|---|
| | 551 | udata = calloc(1, sizeof(*udata)); |
|---|
| | 552 | udata->name = strdup(name); |
|---|
| | 553 | udata->path = strdup(filepath); |
|---|
| | 554 | cnode->_private = udata; |
|---|
| | 555 | xmlAddChild(node, cnode); |
|---|
| | 556 | } |
|---|
| | 557 | xmlFreeDoc(cdoc); |
|---|
| | 558 | } |
|---|
| | 559 | } |
|---|
| | 560 | } |
|---|
| | 561 | } |
|---|
| | 562 | closedir(dirroot); |
|---|
| | 563 | return 0; |
|---|
| | 564 | } |
|---|
| | 565 | |
|---|
| | 566 | xmlDocPtr |
|---|
| | 567 | noit_conf_read_backing_store(const char *path) { |
|---|
| | 568 | xmlDocPtr doc; |
|---|
| | 569 | xmlNodePtr root; |
|---|
| | 570 | noit_xml_userdata_t *what; |
|---|
| | 571 | |
|---|
| | 572 | doc = xmlNewDoc((xmlChar *)"1.0"); |
|---|
| | 573 | what = calloc(1, sizeof(*what)); |
|---|
| | 574 | what->path = strdup(path); |
|---|
| | 575 | doc->_private = what; |
|---|
| | 576 | root = xmlNewNode(NULL, (xmlChar *)"stub"); |
|---|
| | 577 | xmlDocSetRootElement(doc, root); |
|---|
| | 578 | noit_conf_read_into_node(root, path); |
|---|
| | 579 | return doc; |
|---|
| | 580 | } |
|---|
| 282 | | |
|---|
| | 589 | assert(backingstore_include_cnt == -1); |
|---|
| | 590 | |
|---|
| | 591 | backingstore_include_cnt = 0; |
|---|
| | 592 | mix_ctxt = xmlXPathNewContext(doc); |
|---|
| | 593 | pobj = xmlXPathEval((xmlChar *)"//*[@backingstore]", mix_ctxt); |
|---|
| | 594 | if(!pobj) goto includes; |
|---|
| | 595 | if(pobj->type != XPATH_NODESET) goto includes; |
|---|
| | 596 | if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto includes; |
|---|
| | 597 | cnt = xmlXPathNodeSetGetLength(pobj->nodesetval); |
|---|
| | 598 | if(cnt > 0) |
|---|
| | 599 | backingstore_include_nodes = calloc(cnt, sizeof(*backingstore_include_nodes)); |
|---|
| | 600 | for(i=0; i<cnt; i++) { |
|---|
| | 601 | char *path, *infile; |
|---|
| | 602 | node = xmlXPathNodeSetItem(pobj->nodesetval, i); |
|---|
| | 603 | path = (char *)xmlGetProp(node, (xmlChar *)"backingstore"); |
|---|
| | 604 | if(!path) continue; |
|---|
| | 605 | if(*path == '/') infile = strdup(path); |
|---|
| | 606 | else { |
|---|
| | 607 | char *cp; |
|---|
| | 608 | infile = malloc(PATH_MAX); |
|---|
| | 609 | strlcpy(infile, parentfile, PATH_MAX); |
|---|
| | 610 | for(cp = infile + strlen(infile) - 1; cp > infile; cp--) { |
|---|
| | 611 | if(*cp == '/') { *cp = '\0'; break; } |
|---|
| | 612 | else *cp = '\0'; |
|---|
| | 613 | } |
|---|
| | 614 | strlcat(infile, "/", PATH_MAX); |
|---|
| | 615 | strlcat(infile, path, PATH_MAX); |
|---|
| | 616 | } |
|---|
| | 617 | xmlFree(path); |
|---|
| | 618 | backingstore_include_nodes[i].doc = noit_conf_read_backing_store(infile); |
|---|
| | 619 | if(backingstore_include_nodes[i].doc) { |
|---|
| | 620 | xmlNodePtr n, lchild; |
|---|
| | 621 | backingstore_include_nodes[i].insertion_point = node; |
|---|
| | 622 | backingstore_include_nodes[i].root = xmlDocGetRootElement(backingstore_include_nodes[i].doc); |
|---|
| | 623 | /* for backing store, they are permanently reattached under the backing store. |
|---|
| | 624 | * so for any children, we need to glue them into the new parent document. |
|---|
| | 625 | */ |
|---|
| | 626 | lchild = backingstore_include_nodes[i].root->children; |
|---|
| | 627 | while(lchild && lchild->next) lchild = lchild->next; |
|---|
| | 628 | if(lchild) { |
|---|
| | 629 | lchild->next = node->children; |
|---|
| | 630 | if(node->children) node->children->prev = lchild; |
|---|
| | 631 | } |
|---|
| | 632 | else |
|---|
| | 633 | backingstore_include_nodes[i].root->children = node->children; |
|---|
| | 634 | for(n=node->children; n; n = n->next) { |
|---|
| | 635 | n->parent = backingstore_include_nodes[i].root; /* this gets mapped right back, just for clarity */ |
|---|
| | 636 | n->doc = backingstore_include_nodes[i].doc; |
|---|
| | 637 | } |
|---|
| | 638 | backingstore_include_nodes[i].old_children = NULL; |
|---|
| | 639 | node->children = backingstore_include_nodes[i].root->children; |
|---|
| | 640 | for(n=node->children; n; n = n->next) |
|---|
| | 641 | n->parent = backingstore_include_nodes[i].insertion_point; |
|---|
| | 642 | } |
|---|
| | 643 | else { |
|---|
| | 644 | noitL(noit_error, "Could not load: '%s'\n", infile); |
|---|
| | 645 | rv = -1; |
|---|
| | 646 | } |
|---|
| | 647 | free(infile); |
|---|
| | 648 | } |
|---|
| | 649 | mix_ctxt = xmlXPathNewContext(doc); |
|---|
| | 650 | backingstore_include_cnt = cnt; |
|---|
| | 651 | noitL(noit_debug, "Processed %d backing stores.\n", backingstore_include_cnt); |
|---|
| | 652 | if(pobj) xmlXPathFreeObject(pobj); |
|---|
| | 653 | |
|---|
| | 654 | includes: |
|---|