Commit 89f1638d authored by jan.koester's avatar jan.koester
Browse files

test

parent 60c4e58b
Loading
Loading
Loading
Loading
+8 −7
Changes for CMakeLists.txt: 8 added lines, 7 removed lines.
Original line number Diff line number Diff line
@@ -91,13 +91,14 @@ webctl_module(updates modules/updates/updates.cpp modules/common/jobs.cpp)
# needs sharedd/api.h (sharedd-dev)
webctl_module(shares modules/shares/shares.cpp)
target_link_libraries(webctl_mod_shares PRIVATE sysbus::sysbus)
# storage modules: modules/common/blockdev.cpp (free devices, guarded
# probing), modules/common/jobs.cpp (actions in child processes)
# needs the kernel's <linux/raid/md_p.h>, <linux/raid/md_u.h>
webctl_module(raid modules/raid/raid.cpp modules/common/blockdev.cpp modules/common/jobs.cpp)
# needs the kernel's <linux/dm-ioctl.h>
webctl_module(lvm modules/lvm/lvm.cpp modules/common/blockdev.cpp modules/common/jobs.cpp)
# mounts are syslite .mount units (sysbus CreateMount); mke2fs/mkfs.fat format
# storage modules: clients of storaged (modules/common/storaged.cpp),
# which does RAID and LVM; needs storaged/api.h (storaged-dev)
webctl_module(raid modules/raid/raid.cpp modules/common/storaged.cpp)
target_link_libraries(webctl_mod_raid PRIVATE sysbus::sysbus)
webctl_module(lvm modules/lvm/lvm.cpp modules/common/storaged.cpp)
target_link_libraries(webctl_mod_lvm PRIVATE sysbus::sysbus)
# mounts are syslite .mount units (sysbus CreateMount); mke2fs/mkfs.fat
# format; modules/common/blockdev.cpp: free devices, guarded probing
webctl_module(filesystems modules/filesystems/filesystems.cpp modules/common/blockdev.cpp)
target_link_libraries(webctl_mod_filesystems PRIVATE sysbus::sysbus)

+3 −3
Changes for modules/common/blockdev.h: 3 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -25,9 +25,9 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/

/* Block devices for the storage modules (raid, lvm): which ones nothing
 * uses, what is on them, and O_DIRECT I/O on them. Compiled into each
 * module that needs it. */
/* Block devices for the filesystems module: which ones nothing uses,
 * what is on them, and O_DIRECT I/O on them (storaged, which does RAID
 * and LVM, has its own copy). Compiled into each module that needs it. */

#pragma once

+179 −0
Changes for modules/common/storaged.cpp: 179 added lines, 0 removed lines.
Original line number Diff line number Diff line
/*******************************************************************************
Copyright (c) 2026, Jan Koester jan.koester@gmx.net
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the <organization> nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/

#include "storaged.h"

#include <cctype>
#include <cstdlib>
#include <cstring>
#include <map>
#include <set>

#include <storaged/api.h>
#include <sysbus/client.h>
#include <webctl/module.h>

namespace webctl::storage {

namespace {

const std::set<std::string> Flags = {"bitmap", "in_use", "partition", "active", "open", "plain"};
const std::set<std::string> Numbers = {"raid_disks", "degraded", "size", "component_size", "chunk", "sync_speed",
                                       "slot", "present", "partitions", "extent_size", "free", "used",
                                       "seconds", "finished"};

std::string camel(const std::string &k) {
    std::string out;
    for (size_t i = 0; i < k.size(); ++i) {
        if (k[i] == '_' && i + 1 < k.size()) out += (char)toupper((unsigned char)k[++i]);
        else out += k[i];
    }
    return out;
}

json_object *list(json_object *o, const char *key) {
    json_object *a = nullptr;
    if (!json_object_object_get_ex(o, key, &a) || !a) {
        a = json_object_new_array();
        json_object_object_add(o, key, a);
    }
    return a;
}

void addField(json_object *o, const std::string &record, const std::string &k, const std::string &v) {
    if (k == "area") return;
    if (k == "dev") {
        if (record == "pv") json_object_object_add(o, "device", jstr(v));
        else json_object_array_add(list(o, "devices"), jstr(v));
        return;
    }
    if (k == "content_type" || k == "content_label") {
        json_object *c = nullptr;
        if (!json_object_object_get_ex(o, "content", &c) || !c) {
            c = json_object_new_object();
            json_object_object_add(o, "content", c);
        }
        json_object_object_add(c, k == "content_type" ? "type" : "label", jstr(v));
        return;
    }
    std::string name = camel(k);
    if (Flags.count(k)) json_object_object_add(o, name.c_str(), json_object_new_boolean(v == "yes"));
    else if (k == "sync_percent") json_object_object_add(o, name.c_str(), json_object_new_double(std::strtod(v.c_str(), nullptr)));
    else if (Numbers.count(k)) json_object_object_add(o, name.c_str(), json_object_new_int64(std::strtoll(v.c_str(), nullptr, 10)));
    else json_object_object_add(o, name.c_str(), jstr(v));
}

} // namespace

json_object *call(const char *method, const Fields &args, sysbus::Message &reply) {
    sysbus::Client bus;
    if (!bus.connect())
        return error(tr("sysbus isn't reachable: %1", "sysbus ist nicht erreichbar: %1", {bus.lastError()}), 503);
    Fields all = args;
    all.emplace_back("lang", language());
    reply = bus.callService(storaged::Service, method, all);
    if (reply.type != sysbus::MessageType::Error) return nullptr;
    const std::string &name = reply.member;
    if (name == sysbus::error::ServiceUnknown)
        return error(tr("storaged isn't running (Services: storaged)", "storaged läuft nicht (Dienste: storaged)"), 503);
    int status = name == sysbus::error::InvalidArgs ? 400
               : name == sysbus::error::AccessDenied ? 403
               : name == storaged::error::NotFound ? 404
               : name == storaged::error::Busy ? 409
               : name == storaged::error::Unavailable ? 503 : 500;
    return error(reply.get("message", name), status);
}

json_object *change(const char *method, const Fields &args) {
    sysbus::Message r;
    if (json_object *e = call(method, args, r)) return e;
    json_object *o = ok();
    if (r.get("background") == "yes") {
        json_object *jobs = records(r, {{"job", "jobs", nullptr, "id"}});
        json_object *list = json_object_object_get(jobs, "jobs");
        json_object_object_add(o, "background", json_object_new_boolean(1));
        if (json_object_array_length(list))
            json_object_object_add(o, "job", json_object_get(json_object_array_get_idx(list, 0)));
        json_object_put(jobs);
        return o;
    }
    for (auto &f : r.fields) json_object_object_add(o, f.first.c_str(), jstr(f.second));
    return o;
}

json_object *records(const sysbus::Message &reply, std::initializer_list<Kind> kinds) {
    json_object *out = json_object_new_object();
    for (const Kind &k : kinds)
        if (!k.parent) list(out, k.list);
    std::map<std::string, json_object *> current;  // the newest record of each kind
    json_object *cur = nullptr;
    std::string curRecord;
    for (auto &f : reply.fields) {
        const Kind *kind = nullptr;
        for (const Kind &k : kinds)
            if (f.first == k.record) kind = &k;
        if (!kind) {
            if (cur) addField(cur, curRecord, f.first, f.second);
            continue;
        }
        json_object *into = out;
        if (kind->parent) {
            auto p = current.find(kind->parent);
            into = p == current.end() ? nullptr : p->second;
        }
        if (!into) {  // a child without its parent: not ours
            cur = nullptr;
            continue;
        }
        cur = json_object_new_object();
        curRecord = kind->record;
        if (curRecord == "job") json_object_object_add(cur, kind->nameKey, json_object_new_int64(std::strtoll(f.second.c_str(), nullptr, 10)));
        else json_object_object_add(cur, kind->nameKey, jstr(f.second));
        // lists a record always has, and a missing PV's device
        for (const Kind &k : kinds)
            if (k.parent && curRecord == k.parent) list(cur, k.list);
        if (curRecord == "lv") list(cur, "devices");
        if (curRecord == "pv") json_object_object_add(cur, "device", nullptr);
        json_object_array_add(list(into, kind->list), cur);
        current[curRecord] = cur;
    }
    return out;
}

bool listFields(json_object *args, const char *key, const char *field, Fields &out) {
    json_object *v = nullptr;
    if (!json_object_object_get_ex(args, key, &v) || !v) return true;
    if (!json_object_is_type(v, json_type_array)) return false;
    for (size_t i = 0; i < json_object_array_length(v); ++i) {
        const char *s = json_object_get_string(json_object_array_get_idx(v, i));
        if (!s) return false;
        out.emplace_back(field, s);
    }
    return true;
}

} // namespace webctl::storage
+71 −0
Changes for modules/common/storaged.h: 71 added lines, 0 removed lines.
Original line number Diff line number Diff line
/*******************************************************************************
Copyright (c) 2026, Jan Koester jan.koester@gmx.net
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the <organization> nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/

/* The storage modules (raid, lvm) are clients of storaged, over its sysbus
 * API (include/storaged/api.h): the daemon does the work, these turn its
 * flat replies into the JSON the UI reads. Compiled into each module that
 * needs it. */

#pragma once

#include <initializer_list>
#include <string>
#include <utility>
#include <vector>

#include <json-c/json.h>
#include <sysbus/message.h>

namespace webctl::storage {

using Fields = std::vector<std::pair<std::string, std::string>>;

// a storaged call; on failure the error object to return
json_object *call(const char *method, const Fields &args, sysbus::Message &reply);

// a call that changes something: {"ok": true, ...} with the reply's
// fields (array, path), or {"ok": true, "background": true, "job": {...}}
json_object *change(const char *method, const Fields &args);

// a record kind of a list reply: its key, the JSON list it goes into
// (of the reply, or of the record parent), the name its value gets
struct Kind {
    const char *record;
    const char *list;
    const char *parent;   // nullptr: top level
    const char *nameKey;
};

// the records of a list reply as JSON: top-level lists always present,
// fields in camelCase, flags and numbers typed, content_type/_label as
// content {type, label}, "dev" as the list "devices" (pv: "device")
json_object *records(const sysbus::Message &reply, std::initializer_list<Kind> kinds);

// a JSON list of strings as repeated fields; false if it isn't one
bool listFields(json_object *args, const char *key, const char *field, Fields &out);

} // namespace webctl::storage
+54 −1518

File changed.

Preview size limit exceeded, changes collapsed.

Loading