Loading modules/system/system.cpp +51 −0 Changes for modules/system/system.cpp: 51 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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; Loading www/modules/system.js +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', Loading @@ -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; Loading Loading @@ -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; Loading @@ -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 || ''), Loading Loading
modules/system/system.cpp +51 −0 Changes for modules/system/system.cpp: 51 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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; Loading
www/modules/system.js +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', Loading @@ -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; Loading Loading @@ -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; Loading @@ -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 || ''), Loading