Commit 5b8d4e7f authored by jan.koester's avatar jan.koester
Browse files

test

parent a818ece1
Loading
Loading
Loading
Loading
+51 −0
Changes for modules/system/system.cpp: 51 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -28,13 +28,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/* webctl module "system": what the machine is, and turning it off.
 *
 *   info      -> {hostname, os, kernel, arch, uptime, load[3], memTotal, memAvailable}
 *   hostname {hostname}
 *             set now and in /etc/hostname.yaml, where syslite takes it
 *             from at boot
 *   reboot    stop all services, reboot      (syslite over sysbus; it goes
 *   poweroff  stop all services, power off    down about a second later)
 */

#include <cerrno>
#include <cstring>
#include <fstream>
#include <string>

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/sysinfo.h>
#include <sys/utsname.h>
#include <unistd.h>
@@ -100,10 +107,54 @@ json_object *info() {
    return o;
}

const char *HostnameFile = "/etc/hostname.yaml";

// RFC 1123: dot-separated labels of letters, digits and '-', none starting or
// ending with '-'; at most HOST_NAME_MAX (64) in all
bool validHostname(const std::string &h) {
    if (h.empty() || h.size() > 64) return false;
    size_t start = 0;
    for (;;) {
        size_t end = h.find('.', start);
        std::string label = h.substr(start, end == std::string::npos ? std::string::npos : end - start);
        if (label.empty() || label.size() > 63 || label.front() == '-' || label.back() == '-') return false;
        for (unsigned char c : label)
            if (!isalnum(c) && c != '-') return false;
        if (end == std::string::npos) return true;
        start = end + 1;
    }
}

json_object *setHostname(const std::string &name) {
    if (!validHostname(name))
        return error(tr("\"%1\" isn't a valid host name (letters, digits, '-' and '.', at most 64)",
                        "\"%1\" ist kein gültiger Hostname (Buchstaben, Ziffern, '-' und '.', höchstens 64)", {name}));
    // atomically: a temporary file renamed over it
    std::string content = "# the host name syslite sets at boot (webctl: System)\nhostname: " + name + "\n";
    std::string tmp = std::string(HostnameFile) + ".webctl";
    int fd = open(tmp.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, 0644);
    bool ok = fd >= 0;
    if (ok) {
        fchmod(fd, 0644);
        ok = write(fd, content.data(), content.size()) == (ssize_t)content.size();
        ok = fsync(fd) == 0 && ok;
        ok = close(fd) == 0 && ok;
    }
    if (!ok || rename(tmp.c_str(), HostnameFile) != 0) {
        int e = errno;
        unlink(tmp.c_str());
        return error(tr("writing %1 failed: %2", "Schreiben von %1 fehlgeschlagen: %2", {HostnameFile, strerror(e)}), 500);
    }
    if (sethostname(name.c_str(), name.size()) != 0)
        return error(tr("setting the host name failed: %1", "Setzen des Hostnamens fehlgeschlagen: %1", {strerror(errno)}), 500);
    return webctl::ok();
}

json_object *call(const webctl_request *req) {
    webctl::LanguageScope lang(req->lang);
    std::string action = req->action;
    if (action == "info") return info();
    if (action == "hostname") return setHostname(webctl::str(req->args, "hostname"));
    if (action != "reboot" && action != "poweroff") return error(tr("unknown action %1", "unbekannte Aktion %1", {action}), 404);

    sysbus::Client bus;
+22 −2
Changes for www/modules/system.js: 22 added lines, 2 removed lines.
Original line number Diff line number Diff line
'use strict';
// System: overview, reboot and power off (backend module "system")
(() => {
  const {h, t, dialog, call, run} = webctl;
  const {h, t, dialog, call, run, toast} = webctl;

  webctl.translations({de: {
    'System': 'System', 'Host name': 'Hostname', 'Operating system': 'Betriebssystem', 'Kernel': 'Kernel',
@@ -18,6 +18,9 @@
    'Waiting for {0} to come back.': 'Warte, bis {0} wieder erreichbar ist.',
    'The system is powering off.': 'Das System wird ausgeschaltet.',
    'You can close this page.': 'Diese Seite kann geschlossen werden.',
    'Change': 'Ändern', 'Change the host name': 'Hostname ändern', 'Host name changed.': 'Hostname geändert.',
    'Letters, digits and "-", dot-separated labels; at most 64 characters. Takes effect at once and is kept in /etc/hostname.yaml.':
      'Buchstaben, Ziffern und „-“, durch Punkte getrennt; höchstens 64 Zeichen. Gilt sofort und wird in /etc/hostname.yaml gespeichert.',
  }});

  let root;
@@ -79,6 +82,22 @@
    }
  }

  function editHostname(current) {
    dialog({
      title: t('Change the host name'),
      fields: () => [
        h('label', {}, t('Host name'),
          h('input', {name: 'hostname', value: current, required: true, maxlength: 64, spellcheck: false})),
        h('p', {class: 'hint'}, t('Letters, digits and "-", dot-separated labels; at most 64 characters. Takes effect at once and is kept in /etc/hostname.yaml.')),
      ],
      onSubmit: async d => {
        await call('system', 'hostname', {hostname: d.hostname.trim()});
        toast(t('Host name changed.'));
        load();
      },
    });
  }

  async function load() {
    const i = await run(() => call('system', 'info'));
    if (!i) return;
@@ -87,7 +106,8 @@
      h('section', {class: 'panel'},
        h('div', {class: 'toolbar'}, h('h2', {}, t('System'))),
        h('div', {class: 'table-wrap'}, h('table', {class: 'facts'}, h('tbody', {},
          row(t('Host name'), h('strong', {}, i.hostname)),
          row(t('Host name'), [h('strong', {}, i.hostname), ' ',
            h('button', {class: 'small', onclick: () => editHostname(i.hostname)}, t('Change'))]),
          row(t('Operating system'), i.os),
          row(t('Kernel'), h('span', {class: 'mono'}, i.kernel || '')),
          row(t('Architecture'), i.arch || ''),