Page MenuHomePhorge

No OneTemporary

Size
52 KB
Referenced Files
None
Subscribers
None
diff --git a/lib/bridge.cc b/lib/bridge.cc
index 9611b91..f53737a 100644
--- a/lib/bridge.cc
+++ b/lib/bridge.cc
@@ -1,128 +1,130 @@
#include <bridge.h>
#include <process.h>
#include <iostream>
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <vector>
+
#ifdef __linux__
#include <cstring>
#include <string>
#include <cstdlib>
#include <dirent.h>
#include <sys/prctl.h>
+
#elif __APPLE__
#include <libproc.h>
#include <sys/proc_info.h>
#include <libproc.h>
#include <sys/proc_info.h>
#include <iostream>
#include <crt_externs.h>
#endif
using namespace std;
void set_program_name(String name) {
#ifdef __linux__
prctl(PR_SET_NAME, name.c_str());
#elif __APPLE__
setprogname(name.c_str());
#endif
}
int64_t get_child_pid(int64_t parentPID) {
#ifdef __linux__
DIR *dir = opendir("/proc");
if (!dir) {
std::cerr << "[PMC] (cc) Error opening /proc directory.\n";
perror("get_child_pid");
return -1;
}
int targetPID = -1;
dirent *entry;
while ((entry = readdir(dir)) != nullptr) {
if (entry->d_type == DT_DIR && isdigit(entry->d_name[0])) {
int pid = atoi(entry->d_name);
char statusPath[256];
snprintf(statusPath, sizeof(statusPath), "/proc/%d/status", pid);
FILE *statusFile = fopen(statusPath, "r");
if (statusFile) {
char buffer[256];
while (fgets(buffer, sizeof(buffer), statusFile) != nullptr) {
if (strncmp(buffer, "PPid:", 5) == 0) {
int parentID;
if (sscanf(buffer + 5, "%d", &parentID) == 1 && parentID == parentPID) {
targetPID = pid; break;
} break;
}
}
fclose(statusFile);
}
}
}
closedir(dir);
return targetPID;
#elif __APPLE__
pid_t pidList[1024];
int count = proc_listpids(PROC_ALL_PIDS, 0, pidList, sizeof(pidList));
if (count <= 0) {
std::cerr << "Error retrieving process list." << std::endl;
perror("get_child_pid");
return -1;
}
for (int i = 0; i < count; ++i) {
struct proc_bsdinfo procInfo;
if (proc_pidinfo(pidList[i], PROC_PIDTBSDINFO, 0, &procInfo, sizeof(procInfo)) > 0) {
if (procInfo.pbi_ppid == parentPID) {
return static_cast<int>(pidList[i]);
}
}
}
return -1;
#else
return -1;
#endif
}
rust::Vec<rust::i64> find_chidren(int64_t pid) {
rust::Vec<rust::i64> children;
int64_t child;
while ((child = get_child_pid(pid)) != -1) {
children.push_back(child);
pid = child;
}
return children;
}
int64_t stop(int64_t pid) {
vector<pid_t> children;
int64_t child;
while ((child = get_child_pid(pid)) != -1) {
children.push_back(child);
pid = child;
}
for (size_t i = 0; i < children.size(); i++) {
kill(children[i], SIGTERM);
}
return kill(pid, SIGTERM);
}
int64_t run(ProcessMetadata metadata) {
process::Runner runner;
runner.New(std::string(metadata.name), std::string(metadata.log_path));
return runner.Run(std::string(metadata.command), std::string(metadata.shell), metadata.args, metadata.env);
}
\ No newline at end of file
diff --git a/lib/include/fork.h b/lib/include/fork.h
index 204c869..7ce089a 100644
--- a/lib/include/fork.h
+++ b/lib/include/fork.h
@@ -1,19 +1,20 @@
#ifndef FORK_H
#define FORK_H
+
#include <string>
#include <cstdint>
#ifndef CXXBRIDGE1_ENUM_Fork
#define CXXBRIDGE1_ENUM_Fork
enum class Fork: std::uint8_t {
Parent,
Child
};
#endif
using Callback = void(*)();
pid_t set_sid();
void close_fd();
Fork fork_process();
extern "C" int32_t try_fork(bool nochdir, bool noclose, Callback callback);
#endif
diff --git a/lib/include/rust.h b/lib/include/rust.h
index e1da023..56440ae 100644
--- a/lib/include/rust.h
+++ b/lib/include/rust.h
@@ -1,1074 +1,1075 @@
#pragma once
#include <algorithm>
#include <array>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <exception>
#include <initializer_list>
#include <iosfwd>
#include <iterator>
#include <new>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
+
#if defined(_WIN32)
#include <basetsd.h>
#else
#include <sys/types.h>
#endif
namespace rust {
inline namespace cxxbridge1 {
struct unsafe_bitcopy_t;
namespace {
template <typename T>
class impl;
}
#ifndef CXXBRIDGE1_RUST_STRING
#define CXXBRIDGE1_RUST_STRING
class String final {
public:
String() noexcept;
String(const String &) noexcept;
String(String &&) noexcept;
~String() noexcept;
String(const std::string &);
String(const char *);
String(const char *, std::size_t);
String(const char16_t *);
String(const char16_t *, std::size_t);
static String lossy(const std::string &) noexcept;
static String lossy(const char *) noexcept;
static String lossy(const char *, std::size_t) noexcept;
static String lossy(const char16_t *) noexcept;
static String lossy(const char16_t *, std::size_t) noexcept;
String &operator=(const String &) &noexcept;
String &operator=(String &&) &noexcept;
explicit operator std::string() const;
const char *data() const noexcept;
std::size_t size() const noexcept;
std::size_t length() const noexcept;
bool empty() const noexcept;
const char *c_str() noexcept;
std::size_t capacity() const noexcept;
void reserve(size_t new_cap) noexcept;
using iterator = char *;
iterator begin() noexcept;
iterator end() noexcept;
using const_iterator = const char *;
const_iterator begin() const noexcept;
const_iterator end() const noexcept;
const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;
bool operator==(const String &) const noexcept;
bool operator!=(const String &) const noexcept;
bool operator<(const String &) const noexcept;
bool operator<=(const String &) const noexcept;
bool operator>(const String &) const noexcept;
bool operator>=(const String &) const noexcept;
void swap(String &) noexcept;
String(unsafe_bitcopy_t, const String &) noexcept;
private:
struct lossy_t;
String(lossy_t, const char *, std::size_t) noexcept;
String(lossy_t, const char16_t *, std::size_t) noexcept;
friend void swap(String &lhs, String &rhs) noexcept { lhs.swap(rhs); }
std::array<std::uintptr_t, 3> repr;
};
#endif
#ifndef CXXBRIDGE1_RUST_STR
#define CXXBRIDGE1_RUST_STR
class Str final {
public:
Str() noexcept;
Str(const String &) noexcept;
Str(const std::string &);
Str(const char *);
Str(const char *, std::size_t);
Str &operator=(const Str &) &noexcept = default;
explicit operator std::string() const;
const char *data() const noexcept;
std::size_t size() const noexcept;
std::size_t length() const noexcept;
bool empty() const noexcept;
Str(const Str &) noexcept = default;
~Str() noexcept = default;
using iterator = const char *;
using const_iterator = const char *;
const_iterator begin() const noexcept;
const_iterator end() const noexcept;
const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;
bool operator==(const Str &) const noexcept;
bool operator!=(const Str &) const noexcept;
bool operator<(const Str &) const noexcept;
bool operator<=(const Str &) const noexcept;
bool operator>(const Str &) const noexcept;
bool operator>=(const Str &) const noexcept;
void swap(Str &) noexcept;
private:
class uninit;
Str(uninit) noexcept;
friend impl<Str>;
std::array<std::uintptr_t, 2> repr;
};
#endif
#ifndef CXXBRIDGE1_RUST_SLICE
namespace detail {
template <bool>
struct copy_assignable_if {};
template <>
struct copy_assignable_if<false> {
copy_assignable_if() noexcept = default;
copy_assignable_if(const copy_assignable_if &) noexcept = default;
copy_assignable_if &operator=(const copy_assignable_if &) &noexcept = delete;
copy_assignable_if &operator=(copy_assignable_if &&) &noexcept = default;
};
}
template <typename T>
class Slice final
: private detail::copy_assignable_if<std::is_const<T>::value> {
public:
using value_type = T;
Slice() noexcept;
Slice(T *, std::size_t count) noexcept;
Slice &operator=(const Slice<T> &) &noexcept = default;
Slice &operator=(Slice<T> &&) &noexcept = default;
T *data() const noexcept;
std::size_t size() const noexcept;
std::size_t length() const noexcept;
bool empty() const noexcept;
T &operator[](std::size_t n) const noexcept;
T &at(std::size_t n) const;
T &front() const noexcept;
T &back() const noexcept;
Slice(const Slice<T> &) noexcept = default;
~Slice() noexcept = default;
class iterator;
iterator begin() const noexcept;
iterator end() const noexcept;
void swap(Slice &) noexcept;
private:
class uninit;
Slice(uninit) noexcept;
friend impl<Slice>;
friend void sliceInit(void *, const void *, std::size_t) noexcept;
friend void *slicePtr(const void *) noexcept;
friend std::size_t sliceLen(const void *) noexcept;
std::array<std::uintptr_t, 2> repr;
};
template <typename T>
class Slice<T>::iterator final {
public:
using iterator_category = std::random_access_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = typename std::add_pointer<T>::type;
using reference = typename std::add_lvalue_reference<T>::type;
reference operator*() const noexcept;
pointer operator->() const noexcept;
reference operator[](difference_type) const noexcept;
iterator &operator++() noexcept;
iterator operator++(int) noexcept;
iterator &operator--() noexcept;
iterator operator--(int) noexcept;
iterator &operator+=(difference_type) noexcept;
iterator &operator-=(difference_type) noexcept;
iterator operator+(difference_type) const noexcept;
iterator operator-(difference_type) const noexcept;
difference_type operator-(const iterator &) const noexcept;
bool operator==(const iterator &) const noexcept;
bool operator!=(const iterator &) const noexcept;
bool operator<(const iterator &) const noexcept;
bool operator<=(const iterator &) const noexcept;
bool operator>(const iterator &) const noexcept;
bool operator>=(const iterator &) const noexcept;
private:
friend class Slice;
void *pos;
std::size_t stride;
};
#endif
#ifndef CXXBRIDGE1_RUST_BOX
template <typename T>
class Box final {
public:
using element_type = T;
using const_pointer =
typename std::add_pointer<typename std::add_const<T>::type>::type;
using pointer = typename std::add_pointer<T>::type;
Box() = delete;
Box(Box &&) noexcept;
~Box() noexcept;
explicit Box(const T &);
explicit Box(T &&);
Box &operator=(Box &&) &noexcept;
const T *operator->() const noexcept;
const T &operator*() const noexcept;
T *operator->() noexcept;
T &operator*() noexcept;
template <typename... Fields>
static Box in_place(Fields &&...);
void swap(Box &) noexcept;
static Box from_raw(T *) noexcept;
T *into_raw() noexcept;
using value_type = element_type;
private:
class uninit;
class allocation;
Box(uninit) noexcept;
void drop() noexcept;
friend void swap(Box &lhs, Box &rhs) noexcept { lhs.swap(rhs); }
T *ptr;
};
#endif
#ifndef CXXBRIDGE1_RUST_VEC
template <typename T>
class Vec final {
public:
using value_type = T;
Vec() noexcept;
Vec(std::initializer_list<T>);
Vec(const Vec &);
Vec(Vec &&) noexcept;
~Vec() noexcept;
Vec &operator=(Vec &&) &noexcept;
Vec &operator=(const Vec &) &;
std::size_t size() const noexcept;
bool empty() const noexcept;
const T *data() const noexcept;
T *data() noexcept;
std::size_t capacity() const noexcept;
const T &operator[](std::size_t n) const noexcept;
const T &at(std::size_t n) const;
const T &front() const noexcept;
const T &back() const noexcept;
T &operator[](std::size_t n) noexcept;
T &at(std::size_t n);
T &front() noexcept;
T &back() noexcept;
void reserve(std::size_t new_cap);
void push_back(const T &value);
void push_back(T &&value);
template <typename... Args>
void emplace_back(Args &&...args);
void truncate(std::size_t len);
void clear();
using iterator = typename Slice<T>::iterator;
iterator begin() noexcept;
iterator end() noexcept;
using const_iterator = typename Slice<const T>::iterator;
const_iterator begin() const noexcept;
const_iterator end() const noexcept;
const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;
void swap(Vec &) noexcept;
Vec(unsafe_bitcopy_t, const Vec &) noexcept;
private:
void reserve_total(std::size_t new_cap) noexcept;
void set_len(std::size_t len) noexcept;
void drop() noexcept;
friend void swap(Vec &lhs, Vec &rhs) noexcept { lhs.swap(rhs); }
std::array<std::uintptr_t, 3> repr;
};
#endif
#ifndef CXXBRIDGE1_RUST_FN
template <typename Signature>
class Fn;
template <typename Ret, typename... Args>
class Fn<Ret(Args...)> final {
public:
Ret operator()(Args... args) const noexcept;
Fn operator*() const noexcept;
private:
Ret (*trampoline)(Args..., void *fn) noexcept;
void *fn;
};
#endif
#ifndef CXXBRIDGE1_RUST_ERROR
#define CXXBRIDGE1_RUST_ERROR
class Error final : public std::exception {
public:
Error(const Error &);
Error(Error &&) noexcept;
~Error() noexcept override;
Error &operator=(const Error &) &;
Error &operator=(Error &&) &noexcept;
const char *what() const noexcept override;
private:
Error() noexcept = default;
friend impl<Error>;
const char *msg;
std::size_t len;
};
#endif
#ifndef CXXBRIDGE1_RUST_ISIZE
#define CXXBRIDGE1_RUST_ISIZE
#if defined(_WIN32)
using isize = SSIZE_T;
#else
using isize = ssize_t;
#endif
#endif
std::ostream &operator<<(std::ostream &, const String &);
std::ostream &operator<<(std::ostream &, const Str &);
#ifndef CXXBRIDGE1_RUST_OPAQUE
#define CXXBRIDGE1_RUST_OPAQUE
class Opaque {
public:
Opaque() = delete;
Opaque(const Opaque &) = delete;
~Opaque() = delete;
};
#endif
template <typename T>
std::size_t size_of();
template <typename T>
std::size_t align_of();
template <typename T>
struct IsRelocatable;
using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;
using usize = std::size_t;
using i8 = std::int8_t;
using i16 = std::int16_t;
using i32 = std::int32_t;
using i64 = std::int64_t;
using f32 = float;
using f64 = double;
using string = String;
using str = Str;
template <typename T>
using slice = Slice<T>;
template <typename T>
using box = Box<T>;
template <typename T>
using vec = Vec<T>;
using error = Error;
template <typename Signature>
using fn = Fn<Signature>;
template <typename T>
using is_relocatable = IsRelocatable<T>;
#ifndef CXXBRIDGE1_PANIC
#define CXXBRIDGE1_PANIC
template <typename Exception>
void panic [[noreturn]] (const char *msg);
#endif
#ifndef CXXBRIDGE1_RUST_FN
#define CXXBRIDGE1_RUST_FN
template <typename Ret, typename... Args>
Ret Fn<Ret(Args...)>::operator()(Args... args) const noexcept {
return (*this->trampoline)(std::forward<Args>(args)..., this->fn);
}
template <typename Ret, typename... Args>
Fn<Ret(Args...)> Fn<Ret(Args...)>::operator*() const noexcept {
return *this;
}
#endif
#ifndef CXXBRIDGE1_RUST_BITCOPY_T
#define CXXBRIDGE1_RUST_BITCOPY_T
struct unsafe_bitcopy_t final {
explicit unsafe_bitcopy_t() = default;
};
#endif
#ifndef CXXBRIDGE1_RUST_BITCOPY
#define CXXBRIDGE1_RUST_BITCOPY
constexpr unsafe_bitcopy_t unsafe_bitcopy{};
#endif
#ifndef CXXBRIDGE1_RUST_SLICE
#define CXXBRIDGE1_RUST_SLICE
template <typename T>
Slice<T>::Slice() noexcept {
sliceInit(this, reinterpret_cast<void *>(align_of<T>()), 0);
}
template <typename T>
Slice<T>::Slice(T *s, std::size_t count) noexcept {
assert(s != nullptr || count == 0);
sliceInit(this,
s == nullptr && count == 0
? reinterpret_cast<void *>(align_of<T>())
: const_cast<typename std::remove_const<T>::type *>(s),
count);
}
template <typename T>
T *Slice<T>::data() const noexcept {
return reinterpret_cast<T *>(slicePtr(this));
}
template <typename T>
std::size_t Slice<T>::size() const noexcept {
return sliceLen(this);
}
template <typename T>
std::size_t Slice<T>::length() const noexcept {
return this->size();
}
template <typename T>
bool Slice<T>::empty() const noexcept {
return this->size() == 0;
}
template <typename T>
T &Slice<T>::operator[](std::size_t n) const noexcept {
assert(n < this->size());
auto ptr = static_cast<char *>(slicePtr(this)) + size_of<T>() * n;
return *reinterpret_cast<T *>(ptr);
}
template <typename T>
T &Slice<T>::at(std::size_t n) const {
if (n >= this->size()) {
panic<std::out_of_range>("rust::Slice index out of range");
}
return (*this)[n];
}
template <typename T>
T &Slice<T>::front() const noexcept {
assert(!this->empty());
return (*this)[0];
}
template <typename T>
T &Slice<T>::back() const noexcept {
assert(!this->empty());
return (*this)[this->size() - 1];
}
template <typename T>
typename Slice<T>::iterator::reference
Slice<T>::iterator::operator*() const noexcept {
return *static_cast<T *>(this->pos);
}
template <typename T>
typename Slice<T>::iterator::pointer
Slice<T>::iterator::operator->() const noexcept {
return static_cast<T *>(this->pos);
}
template <typename T>
typename Slice<T>::iterator::reference Slice<T>::iterator::operator[](
typename Slice<T>::iterator::difference_type n) const noexcept {
auto ptr = static_cast<char *>(this->pos) + this->stride * n;
return *reinterpret_cast<T *>(ptr);
}
template <typename T>
typename Slice<T>::iterator &Slice<T>::iterator::operator++() noexcept {
this->pos = static_cast<char *>(this->pos) + this->stride;
return *this;
}
template <typename T>
typename Slice<T>::iterator Slice<T>::iterator::operator++(int) noexcept {
auto ret = iterator(*this);
this->pos = static_cast<char *>(this->pos) + this->stride;
return ret;
}
template <typename T>
typename Slice<T>::iterator &Slice<T>::iterator::operator--() noexcept {
this->pos = static_cast<char *>(this->pos) - this->stride;
return *this;
}
template <typename T>
typename Slice<T>::iterator Slice<T>::iterator::operator--(int) noexcept {
auto ret = iterator(*this);
this->pos = static_cast<char *>(this->pos) - this->stride;
return ret;
}
template <typename T>
typename Slice<T>::iterator &Slice<T>::iterator::operator+=(
typename Slice<T>::iterator::difference_type n) noexcept {
this->pos = static_cast<char *>(this->pos) + this->stride * n;
return *this;
}
template <typename T>
typename Slice<T>::iterator &Slice<T>::iterator::operator-=(
typename Slice<T>::iterator::difference_type n) noexcept {
this->pos = static_cast<char *>(this->pos) - this->stride * n;
return *this;
}
template <typename T>
typename Slice<T>::iterator Slice<T>::iterator::operator+(
typename Slice<T>::iterator::difference_type n) const noexcept {
auto ret = iterator(*this);
ret.pos = static_cast<char *>(this->pos) + this->stride * n;
return ret;
}
template <typename T>
typename Slice<T>::iterator Slice<T>::iterator::operator-(
typename Slice<T>::iterator::difference_type n) const noexcept {
auto ret = iterator(*this);
ret.pos = static_cast<char *>(this->pos) - this->stride * n;
return ret;
}
template <typename T>
typename Slice<T>::iterator::difference_type
Slice<T>::iterator::operator-(const iterator &other) const noexcept {
auto diff = std::distance(static_cast<char *>(other.pos),
static_cast<char *>(this->pos));
return diff / static_cast<typename Slice<T>::iterator::difference_type>(
this->stride);
}
template <typename T>
bool Slice<T>::iterator::operator==(const iterator &other) const noexcept {
return this->pos == other.pos;
}
template <typename T>
bool Slice<T>::iterator::operator!=(const iterator &other) const noexcept {
return this->pos != other.pos;
}
template <typename T>
bool Slice<T>::iterator::operator<(const iterator &other) const noexcept {
return this->pos < other.pos;
}
template <typename T>
bool Slice<T>::iterator::operator<=(const iterator &other) const noexcept {
return this->pos <= other.pos;
}
template <typename T>
bool Slice<T>::iterator::operator>(const iterator &other) const noexcept {
return this->pos > other.pos;
}
template <typename T>
bool Slice<T>::iterator::operator>=(const iterator &other) const noexcept {
return this->pos >= other.pos;
}
template <typename T>
typename Slice<T>::iterator Slice<T>::begin() const noexcept {
iterator it;
it.pos = slicePtr(this);
it.stride = size_of<T>();
return it;
}
template <typename T>
typename Slice<T>::iterator Slice<T>::end() const noexcept {
iterator it = this->begin();
it.pos = static_cast<char *>(it.pos) + it.stride * this->size();
return it;
}
template <typename T>
void Slice<T>::swap(Slice &rhs) noexcept {
std::swap(*this, rhs);
}
#endif
#ifndef CXXBRIDGE1_RUST_BOX
#define CXXBRIDGE1_RUST_BOX
template <typename T>
class Box<T>::uninit {};
template <typename T>
class Box<T>::allocation {
static T *alloc() noexcept;
static void dealloc(T *) noexcept;
public:
allocation() noexcept : ptr(alloc()) {}
~allocation() noexcept {
if (this->ptr) {
dealloc(this->ptr);
}
}
T *ptr;
};
template <typename T>
Box<T>::Box(Box &&other) noexcept : ptr(other.ptr) {
other.ptr = nullptr;
}
template <typename T>
Box<T>::Box(const T &val) {
allocation alloc;
::new (alloc.ptr) T(val);
this->ptr = alloc.ptr;
alloc.ptr = nullptr;
}
template <typename T>
Box<T>::Box(T &&val) {
allocation alloc;
::new (alloc.ptr) T(std::move(val));
this->ptr = alloc.ptr;
alloc.ptr = nullptr;
}
template <typename T>
Box<T>::~Box() noexcept {
if (this->ptr) {
this->drop();
}
}
template <typename T>
Box<T> &Box<T>::operator=(Box &&other) &noexcept {
if (this->ptr) {
this->drop();
}
this->ptr = other.ptr;
other.ptr = nullptr;
return *this;
}
template <typename T>
const T *Box<T>::operator->() const noexcept {
return this->ptr;
}
template <typename T>
const T &Box<T>::operator*() const noexcept {
return *this->ptr;
}
template <typename T>
T *Box<T>::operator->() noexcept {
return this->ptr;
}
template <typename T>
T &Box<T>::operator*() noexcept {
return *this->ptr;
}
template <typename T>
template <typename... Fields>
Box<T> Box<T>::in_place(Fields &&...fields) {
allocation alloc;
auto ptr = alloc.ptr;
::new (ptr) T{std::forward<Fields>(fields)...};
alloc.ptr = nullptr;
return from_raw(ptr);
}
template <typename T>
void Box<T>::swap(Box &rhs) noexcept {
using std::swap;
swap(this->ptr, rhs.ptr);
}
template <typename T>
Box<T> Box<T>::from_raw(T *raw) noexcept {
Box box = uninit{};
box.ptr = raw;
return box;
}
template <typename T>
T *Box<T>::into_raw() noexcept {
T *raw = this->ptr;
this->ptr = nullptr;
return raw;
}
template <typename T>
Box<T>::Box(uninit) noexcept {}
#endif
#ifndef CXXBRIDGE1_RUST_VEC
#define CXXBRIDGE1_RUST_VEC
template <typename T>
Vec<T>::Vec(std::initializer_list<T> init) : Vec{} {
this->reserve_total(init.size());
std::move(init.begin(), init.end(), std::back_inserter(*this));
}
template <typename T>
Vec<T>::Vec(const Vec &other) : Vec() {
this->reserve_total(other.size());
std::copy(other.begin(), other.end(), std::back_inserter(*this));
}
template <typename T>
Vec<T>::Vec(Vec &&other) noexcept : repr(other.repr) {
new (&other) Vec();
}
template <typename T>
Vec<T>::~Vec() noexcept {
this->drop();
}
template <typename T>
Vec<T> &Vec<T>::operator=(Vec &&other) &noexcept {
this->drop();
this->repr = other.repr;
new (&other) Vec();
return *this;
}
template <typename T>
Vec<T> &Vec<T>::operator=(const Vec &other) & {
if (this != &other) {
this->drop();
new (this) Vec(other);
}
return *this;
}
template <typename T>
bool Vec<T>::empty() const noexcept {
return this->size() == 0;
}
template <typename T>
T *Vec<T>::data() noexcept {
return const_cast<T *>(const_cast<const Vec<T> *>(this)->data());
}
template <typename T>
const T &Vec<T>::operator[](std::size_t n) const noexcept {
assert(n < this->size());
auto data = reinterpret_cast<const char *>(this->data());
return *reinterpret_cast<const T *>(data + n * size_of<T>());
}
template <typename T>
const T &Vec<T>::at(std::size_t n) const {
if (n >= this->size()) {
panic<std::out_of_range>("rust::Vec index out of range");
}
return (*this)[n];
}
template <typename T>
const T &Vec<T>::front() const noexcept {
assert(!this->empty());
return (*this)[0];
}
template <typename T>
const T &Vec<T>::back() const noexcept {
assert(!this->empty());
return (*this)[this->size() - 1];
}
template <typename T>
T &Vec<T>::operator[](std::size_t n) noexcept {
assert(n < this->size());
auto data = reinterpret_cast<char *>(this->data());
return *reinterpret_cast<T *>(data + n * size_of<T>());
}
template <typename T>
T &Vec<T>::at(std::size_t n) {
if (n >= this->size()) {
panic<std::out_of_range>("rust::Vec index out of range");
}
return (*this)[n];
}
template <typename T>
T &Vec<T>::front() noexcept {
assert(!this->empty());
return (*this)[0];
}
template <typename T>
T &Vec<T>::back() noexcept {
assert(!this->empty());
return (*this)[this->size() - 1];
}
template <typename T>
void Vec<T>::reserve(std::size_t new_cap) {
this->reserve_total(new_cap);
}
template <typename T>
void Vec<T>::push_back(const T &value) {
this->emplace_back(value);
}
template <typename T>
void Vec<T>::push_back(T &&value) {
this->emplace_back(std::move(value));
}
template <typename T>
template <typename... Args>
void Vec<T>::emplace_back(Args &&...args) {
auto size = this->size();
this->reserve_total(size + 1);
::new (reinterpret_cast<T *>(reinterpret_cast<char *>(this->data()) +
size * size_of<T>()))
T(std::forward<Args>(args)...);
this->set_len(size + 1);
}
template <typename T>
void Vec<T>::clear() {
this->truncate(0);
}
template <typename T>
typename Vec<T>::iterator Vec<T>::begin() noexcept {
return Slice<T>(this->data(), this->size()).begin();
}
template <typename T>
typename Vec<T>::iterator Vec<T>::end() noexcept {
return Slice<T>(this->data(), this->size()).end();
}
template <typename T>
typename Vec<T>::const_iterator Vec<T>::begin() const noexcept {
return this->cbegin();
}
template <typename T>
typename Vec<T>::const_iterator Vec<T>::end() const noexcept {
return this->cend();
}
template <typename T>
typename Vec<T>::const_iterator Vec<T>::cbegin() const noexcept {
return Slice<const T>(this->data(), this->size()).begin();
}
template <typename T>
typename Vec<T>::const_iterator Vec<T>::cend() const noexcept {
return Slice<const T>(this->data(), this->size()).end();
}
template <typename T>
void Vec<T>::swap(Vec &rhs) noexcept {
using std::swap;
swap(this->repr, rhs.repr);
}
template <typename T>
Vec<T>::Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {}
#endif
#ifndef CXXBRIDGE1_IS_COMPLETE
#define CXXBRIDGE1_IS_COMPLETE
namespace detail {
namespace {
template <typename T, typename = std::size_t>
struct is_complete : std::false_type {};
template <typename T>
struct is_complete<T, decltype(sizeof(T))> : std::true_type {};
}
}
#endif
#ifndef CXXBRIDGE1_LAYOUT
#define CXXBRIDGE1_LAYOUT
class layout {
template <typename T>
friend std::size_t size_of();
template <typename T>
friend std::size_t align_of();
template <typename T>
static typename std::enable_if<std::is_base_of<Opaque, T>::value,
std::size_t>::type
do_size_of() {
return T::layout::size();
}
template <typename T>
static typename std::enable_if<!std::is_base_of<Opaque, T>::value,
std::size_t>::type
do_size_of() {
return sizeof(T);
}
template <typename T>
static
typename std::enable_if<detail::is_complete<T>::value, std::size_t>::type
size_of() {
return do_size_of<T>();
}
template <typename T>
static typename std::enable_if<std::is_base_of<Opaque, T>::value,
std::size_t>::type
do_align_of() {
return T::layout::align();
}
template <typename T>
static typename std::enable_if<!std::is_base_of<Opaque, T>::value,
std::size_t>::type
do_align_of() {
return alignof(T);
}
template <typename T>
static
typename std::enable_if<detail::is_complete<T>::value, std::size_t>::type
align_of() {
return do_align_of<T>();
}
};
template <typename T>
std::size_t size_of() {
return layout::size_of<T>();
}
template <typename T>
std::size_t align_of() {
return layout::align_of<T>();
}
#endif
#ifndef CXXBRIDGE1_RELOCATABLE
#define CXXBRIDGE1_RELOCATABLE
namespace detail {
template <typename... Ts>
struct make_void {
using type = void;
};
template <typename... Ts>
using void_t = typename make_void<Ts...>::type;
template <typename Void, template <typename...> class, typename...>
struct detect : std::false_type {};
template <template <typename...> class T, typename... A>
struct detect<void_t<T<A...>>, T, A...> : std::true_type {};
template <template <typename...> class T, typename... A>
using is_detected = detect<void, T, A...>;
template <typename T>
using detect_IsRelocatable = typename T::IsRelocatable;
template <typename T>
struct get_IsRelocatable
: std::is_same<typename T::IsRelocatable, std::true_type> {};
}
template <typename T>
struct IsRelocatable
: std::conditional<
detail::is_detected<detail::detect_IsRelocatable, T>::value,
detail::get_IsRelocatable<T>,
std::integral_constant<
bool, std::is_trivially_move_constructible<T>::value &&
std::is_trivially_destructible<T>::value>>::type {};
#endif
}}
\ No newline at end of file
diff --git a/lib/process.cc b/lib/process.cc
index 38543ec..679844b 100644
--- a/lib/process.cc
+++ b/lib/process.cc
@@ -1,109 +1,133 @@
#include <process.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
+#include <chrono>
+#include <thread>
#include <iostream>
#include <algorithm>
+#include <fstream>
+#include <sstream>
+
+#ifdef __APPLE__
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#endif
using namespace std;
namespace process {
volatile sig_atomic_t childExitStatus = 0;
std::string format(std::string text) {
std::replace(text.begin(), text.end(), ' ', '_');
return text;
}
pair<std::string, std::string> split(const std::string& str) {
size_t length = str.length();
size_t midpoint = length / 2;
std::string firstHalf = str.substr(0, midpoint);
std::string secondHalf = str.substr(midpoint);
return make_pair(firstHalf, secondHalf);
}
void sigchld_handler(int signo) {
(void)signo;
int status;
while (waitpid(-1, &status, WNOHANG) > 0) {
childExitStatus = status;
}
}
void Runner::New(const std::string &name, const std::string &logPath) {
std::string formattedName = format(name);
std::string stdoutFileName = logPath + "/" + formattedName + "-out.log";
std::string stderrFileName = logPath + "/" + formattedName + "-error.log";
stdout_fd = open(stdoutFileName.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0644);
stderr_fd = open(stderrFileName.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0644);
struct sigaction sa;
sa.sa_handler = sigchld_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
std::cerr << "[PMC] (cc) Error setting up SIGCHLD handler\n";
perror("Runner::New");
}
}
Runner::~Runner() {
if (stdout_fd != -1) {
close(stdout_fd);
}
if (stderr_fd != -1) {
close(stderr_fd);
}
}
int64_t Runner::Run(const std::string &command, const std::string &shell, Vec<String> args, Vec<String> env) {
pid_t pid = fork();
if (pid == -1) {
std::cerr << "[PMC] (cc) Unable to fork\n";
perror("Runner::Run");
return -1;
} else if (pid == 0) {
setsid();
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
dup2(stdout_fd, STDOUT_FILENO);
dup2(stderr_fd, STDERR_FILENO);
std::vector<const char*> argsArray;
std::vector<const char*> envArray;
argsArray.push_back(shell.c_str());
transform(args.begin(), args.end(), std::back_inserter(argsArray),
[](rust::String& arg) { return arg.c_str(); });
transform(env.begin(), env.end(), std::back_inserter(envArray),
[](rust::String& env) { return env.c_str(); });
argsArray.push_back(command.c_str());
argsArray.push_back(nullptr);
envArray.push_back(nullptr);
if (execve(shell.c_str(), const_cast<char* const*>(argsArray.data()), const_cast<char* const*>(envArray.data())) == -1) {
std::cerr << "[PMC] (cc) Unable to execute the command\n";
perror("execvp");
exit(EXIT_FAILURE);
}
} else {
close(stdout_fd);
close(stderr_fd);
-
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ std::string proc_path = "/proc/" + std::to_string(pid) + "/task/" + std::to_string(pid) + "/children";
+
+ std::ifstream proc_file(proc_path);
+ if (proc_file.is_open()) {
+ std::string line;
+ if (std::getline(proc_file, line)) {
+ std::istringstream iss(line);
+ pid_t child_pid;
+ if (iss >> child_pid) {
+ return child_pid;
+ }
+ }
+ }
+
return pid;
}
return -1;
}}
diff --git a/lib/psutil.cc b/lib/psutil.cc
index f1e77f7..8b3df3e 100644
--- a/lib/psutil.cc
+++ b/lib/psutil.cc
@@ -1,76 +1,99 @@
-#include <iostream>
-#include <string>
-#include <fstream>
-#include <sstream>
-#include <vector>
-#include <cstdlib>
-#include <unistd.h>
#include <chrono>
#include <thread>
#include <cmath>
+
#ifdef __APPLE__
#include <libproc.h>
#include <sys/proc_info.h>
+#include <sys/sysctl.h>
+#else
+#include <fstream>
+#include <sstream>
+#include <vector>
+#include <cstdlib>
+#include <unistd.h>
#endif
-double get_process_cpu_usage_percentage(int64_t pid) {
- auto get_cpu_time = [](int64_t pid) -> double {
+int get_num_cores() {
#ifdef __APPLE__
- struct proc_taskinfo pti;
- int ret = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti));
- if (ret <= 0) {
- return 0;
+ int nm[2];
+ size_t len = 4;
+ uint32_t count;
+
+ nm[0] = CTL_HW; nm[1] = HW_AVAILCPU;
+ sysctl(nm, 2, &count, &len, NULL, 0);
+
+ if(count < 1) {
+ nm[1] = HW_NCPU;
+ sysctl(nm, 2, &count, &len, NULL, 0);
}
- return (pti.pti_total_user + pti.pti_total_system) / 100000000.0; // Convert nanoseconds to seconds
+ return count > 0 ? static_cast<int>(count) : 1;
#else
- std::string stat_path = "/proc/" + std::to_string(pid) + "/stat";
- std::ifstream stat_file(stat_path);
-
- if (!stat_file.is_open()) {
- std::cerr << "Error: Unable to open " << stat_path << std::endl;
- return -1.0;
- }
-
- std::string line;
- std::getline(stat_file, line);
- stat_file.close();
-
- std::istringstream iss(line);
- std::string token;
- std::vector<std::string> tokens;
-
- while (std::getline(iss, token, ' ')) {
- tokens.push_back(token);
- }
-
- if (tokens.size() < 15) {
- std::cerr << "Error: Invalid stat file format" << std::endl;
- return -1.0;
- }
-
- unsigned long long utime = std::stoull(tokens[13]);
- unsigned long long stime = std::stoull(tokens[14]);
-
- return (utime + stime) / sysconf(_SC_CLK_TCK);
+ return static_cast<int>(sysconf(_SC_NPROCESSORS_ONLN));
+#endif
+}
+
+double get_cpu_time(int64_t pid) {
+#ifdef __APPLE__
+ struct proc_taskinfo pti;
+ int ret = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti));
+ if (ret <= 0) {
+ return 0.0;
+ }
+ return (pti.pti_total_user + pti.pti_total_system) / 1e9;
+#else
+ std::string stat_path = "/proc/" + std::to_string(pid) + "/stat";
+ std::ifstream stat_file(stat_path);
+
+ if (!stat_file.is_open()) {
+ return 0.0;
+ }
+
+ std::string line;
+ std::getline(stat_file, line);
+
+ std::istringstream iss(line);
+ std::string token;
+ std::vector<std::string> tokens;
+
+ while (std::getline(iss, token, ' ')) {
+ tokens.push_back(token);
+ }
+
+ if (tokens.size() < 15) {
+ return 0.0;
+ }
+
+ unsigned long long utime = std::stoull(tokens[13]);
+ unsigned long long stime = std::stoull(tokens[14]);
+
+ return (utime + stime) / sysconf(_SC_CLK_TCK);
#endif
- };
+}
+
+extern "C++" double get_process_cpu_usage_percentage(int64_t pid) {
+ const std::chrono::milliseconds measurement_interval(300);
- double cpu_time1 = get_cpu_time(pid);
- if (cpu_time1 < 0) return -1.0;
+ double cpu_time_start = get_cpu_time(pid);
+ if (cpu_time_start < 0) {
+ return 0.0;
+ }
- auto start = std::chrono::high_resolution_clock::now();
- std::this_thread::sleep_for(std::chrono::milliseconds(1000));
- auto end = std::chrono::high_resolution_clock::now();
+ auto start_time = std::chrono::steady_clock::now();
+ std::this_thread::sleep_for(measurement_interval);
+ auto end_time = std::chrono::steady_clock::now();
- double cpu_time2 = get_cpu_time(pid);
- if (cpu_time2 < 0) return -1.0;
+ double cpu_time_end = get_cpu_time(pid);
+ if (cpu_time_end < 0) {
+ return 0.0;
+ }
- std::chrono::duration<double> elapsed = end - start;
+ double cpu_time_diff = cpu_time_end - cpu_time_start;
+ std::chrono::duration<double> elapsed = end_time - start_time;
double elapsed_seconds = elapsed.count();
- double cpu_time_diff = cpu_time2 - cpu_time1;
+ double cpu_usage_percentage = (cpu_time_diff / elapsed_seconds) * 100.0;
- long num_cores = sysconf(_SC_NPROCESSORS_ONLN);
- double cpu_usage_percentage = (cpu_time_diff / elapsed_seconds) * 100.0 * num_cores;
+ long num_cores = get_num_cores();
return std::min(cpu_usage_percentage, 100.0 * num_cores);
-}
+}
\ No newline at end of file
diff --git a/src/webui/src/components/react/servers.tsx b/src/webui/src/components/react/servers.tsx
index 4bc4e7b..68c7b3c 100644
--- a/src/webui/src/components/react/servers.tsx
+++ b/src/webui/src/components/react/servers.tsx
@@ -1,209 +1,209 @@
import { api } from '@/api';
import { useEffect, Fragment } from 'react';
import Loader from '@/components/react/loader';
import Header from '@/components/react/header';
import { version } from '../../../package.json';
import { useArray, classNames, isVersionTooFar, startDuration } from '@/helpers';
const getStatus = (remote: string, status: string): string => {
const badge = {
updated: 'bg-emerald-700/40 text-emerald-400',
behind: 'bg-gray-700/40 text-gray-400',
critical: 'bg-red-700/40 text-red-400'
};
if (remote == 'v0.0.0') {
return badge['behind'];
} else if (isVersionTooFar(version, remote.slice(1))) {
return badge['behind'];
} else if (remote == `v${version}`) {
return badge['updated'];
} else {
return badge[status ?? 'critical'];
}
};
const skeleton = {
os: { name: '' },
version: {
pkg: 'v0.0.0',
hash: 'none',
build_date: 'none',
target: ''
},
daemon: {
pid: 'none',
running: false,
uptime: 0,
process_count: 'none'
}
};
const getServerIcon = (base: string, distro: string): string => {
const distroList = [
'Alpine',
'Arch',
'Amazon',
'Macos',
'Linux',
'Fedora',
'Debian',
'CentOS',
'NixOS',
'FreeBSD',
'OpenBSD',
'OracleLinux',
'Pop',
'Raspbian',
'Redhat',
'Ubuntu'
];
const isDistroKnown = distroList.includes(distro);
return `${base}/assets/${isDistroKnown ? distro.toLowerCase() : 'unknown'}.svg`;
};
const Index = (props: { base: string }) => {
const items = useArray([]);
const badge = {
online: 'bg-emerald-400/10 text-emerald-400',
offline: 'bg-red-500/10 text-red-500'
};
async function fetch() {
items.clear();
const metrics = await api.get(props.base + '/daemon/metrics').json();
items.push({ ...metrics, name: 'local' });
try {
const servers = await api.get(props.base + '/daemon/servers').json();
await servers.forEach(async (name) => {
api
.get(props.base + `/remote/${name}/metrics`)
.json()
.then((metrics) => items.push({ ...metrics, name }))
.catch(() => items.push({ ...skeleton, name }));
});
} catch {}
}
useEffect(() => {
fetch();
}, []);
if (items.isEmpty()) {
return <Loader />;
} else {
return (
<Fragment>
<Header name="Servers" description="A list of all the servers in your daemon config.">
<button
type="button"
onClick={fetch}
className="transition inline-flex items-center justify-center space-x-1.5 border focus:outline-none focus:ring-0 focus:ring-offset-0 focus:z-10 shrink-0 border-zinc-900 hover:border-zinc-800 bg-zinc-950 text-zinc-50 hover:bg-zinc-900 px-4 py-2 text-sm font-semibold rounded-lg">
Refresh
</button>
</Header>
<table className="w-full whitespace-nowrap text-left">
<colgroup>
<col className="w-full sm:w-3/12" />
<col className="lg:w-[10%]" />
<col className="lg:w-2/12" />
<col className="lg:w-2/12" />
<col className="lg:w-1/12" />
<col className="lg:w-1/12" />
<col className="lg:w-1/12" />
<col className="lg:w-1/12" />
</colgroup>
<thead className="sticky top-0 z-10 bg-zinc-950 bg-opacity-75 backdrop-blur backdrop-filter border-b border-white/10 text-sm leading-6 text-white">
<tr>
<th scope="col" className="py-2 pl-4 pr-8 font-semibold sm:pl-6 lg:pl-8">
Server
</th>
<th scope="col" className="py-2 pl-0 pr-8 font-semibold table-cell">
Version
</th>
<th scope="col" className="hidden py-2 pl-0 pr-8 font-semibold sm:table-cell">
Build
</th>
<th scope="col" className="hidden py-2 pl-0 pr-8 font-semibold sm:table-cell">
Hash
</th>
<th scope="col" className="hidden py-2 pl-0 pr-8 font-semibold sm:table-cell">
Process Id
</th>
<th scope="col" className="hidden py-2 pl-0 pr-8 font-semibold md:table-cell lg:pr-20">
Count
</th>
<th scope="col" className="hidden py-2 pl-0 pr-4 font-semibold md:table-cell lg:pr-20">
Status
</th>
<th scope="col" className="py-2 pl-0 pr-4 text-right font-semibold sm:table-cell sm:pr-6 lg:pr-8">
Uptime
</th>
</tr>
</thead>
<tbody className="divide-y divide-white/5 border-b border-white/5">
{items.value.sort().map((server) => (
<tr
className={classNames(server.daemon.running && 'hover:bg-zinc-800/30 cursor-pointer', 'transition')}
key={server.name}
onClick={() => server.daemon.running && (window.location.href = props.base + '/status/' + server.name)}>
<td className="py-4 pl-4 pr-8 sm:pl-6 lg:pl-8">
<div className="flex items-center gap-x-4">
<img
src={getServerIcon(props.base, server.os.name)}
className={classNames(
server.daemon.running ? 'ring-emerald-400 bg-white' : 'ring-red-400 bg-red-500',
- 'h-8 w-8 rounded-full ring-1'
+ 'h-8 w-8 rounded-full ring-2'
)}
/>
<div className="truncate text-sm font-medium leading-6 text-white">{server.name == 'local' ? 'Internal' : server.name}</div>
</div>
</td>
<td className="py-4 pl-0 pr-4 table-cell sm:pr-8">
<div className="flex gap-x-3">
<div
className={classNames(
getStatus(server.version.pkg, server.version.status),
'rounded-md px-2 py-1 text-xs font-medium ring-1 ring-inset ring-white/10'
)}>
{server.version.pkg}
</div>
</div>
</td>
<td className="hidden py-4 pl-0 pr-4 sm:table-cell sm:pr-8">
<div className="font-mono text-sm leading-6 text-gray-400">
{server.version.target} {server.version.build_date}
</div>
</td>
<td className="hidden py-4 pl-0 pr-4 sm:table-cell sm:pr-8">
<div className="font-mono text-sm leading-6 text-gray-400">{server.version.hash.slice(0, 16)}</div>
</td>
<td className="hidden py-4 pl-0 pr-8 text-sm leading-6 text-gray-400 md:table-cell lg:pr-20 font-mono">{server.daemon.pid}</td>
<td className="hidden py-4 pl-0 pr-8 text-sm leading-6 text-gray-400 md:table-cell lg:pr-20">{server.daemon.process_count}</td>
<td className="py-4 pl-0 pr-4 text-sm leading-6 sm:pr-8 lg:pr-20">
<div className="flex items-center justify-end gap-x-2 sm:justify-start">
<span className="text-gray-400 sm:hidden">{server.daemon.uptime == 0 ? 'none' : startDuration(server.daemon.uptime, false)}</span>
<div className={classNames(badge[server.daemon.running ? 'online' : 'offline'], 'flex-none rounded-full p-1')}>
<div className="h-1.5 w-1.5 rounded-full bg-current" />
</div>
<div className="hidden text-white sm:block">{server.daemon.running ? 'Online' : 'Offline'}</div>
</div>
</td>
<td className="hidden py-4 pl-0 pr-4 text-right text-sm leading-6 text-gray-400 sm:table-cell sm:pr-6 lg:pr-8">
{server.daemon.uptime == 0 ? 'none' : startDuration(server.daemon.uptime, false)}
</td>
</tr>
))}
</tbody>
</table>
</Fragment>
);
}
};
export default Index;
diff --git a/src/webui/src/components/react/status.tsx b/src/webui/src/components/react/status.tsx
index 6b8ae4f..58f4c1e 100644
--- a/src/webui/src/components/react/status.tsx
+++ b/src/webui/src/components/react/status.tsx
@@ -1,211 +1,211 @@
import { Line } from 'react-chartjs-2';
import { SSE, api, headers } from '@/api';
import Loader from '@/components/react/loader';
import { useEffect, useState, useRef, Fragment } from 'react';
import { classNames, isRunning, formatMemory, startDuration, useArray } from '@/helpers';
import { Chart, CategoryScale, LinearScale, PointElement, LineElement, Filler } from 'chart.js';
Chart.register(CategoryScale, LinearScale, PointElement, LineElement, Filler);
const bytesToSize = (bytes: number, precision: number) => {
if (isNaN(bytes) || bytes === 0) return '0b';
const sizes = ['b', 'kb', 'mb', 'gb', 'tb'];
const kilobyte = 1024;
const index = Math.floor(Math.log(bytes) / Math.log(kilobyte));
const size = (bytes / Math.pow(kilobyte, index)).toFixed(precision);
return size + sizes[index];
};
const Status = (props: { name: string; base: string }) => {
const bufferLength = 21;
const memoryUsage = useArray([], bufferLength);
const cpuPercentage = useArray([], bufferLength);
const [item, setItem] = useState<any>();
const [loaded, setLoaded] = useState(false);
const [live, setLive] = useState<SSE | null>(null);
const options = {
responsive: true,
maintainAspectRatio: false,
animation: { duration: 0 },
layout: {
padding: {
left: 0,
right: 0,
bottom: 0,
top: 0
}
},
plugins: {
tooltips: { enabled: false },
title: { display: false }
},
elements: {
point: { radius: 0 },
line: { tension: 0.5, borderWidth: 1 }
},
scales: {
x: { display: false },
y: { display: false, suggestedMin: 0 }
},
data: {
labels: Array(20).fill(''),
datasets: [{ fill: true, data: Array(20).fill(0) }]
}
};
const chartContainerStyle = {
borderRadius: '0 0 0.5rem 0.5rem',
marginBottom: '0.5px',
zIndex: 1
};
const cpuChart = {
labels: Array(20).fill(''),
datasets: [
{
fill: true,
data: cpuPercentage.value,
borderColor: '#0284c7',
backgroundColor: (ctx: any) => {
const chart = ctx.chart;
const { ctx: context, chartArea } = chart;
if (!chartArea) {
return null;
}
const gradient = context.createLinearGradient(0, chartArea.bottom, 0, chartArea.top);
gradient.addColorStop(0, 'rgba(14, 165, 233, 0.1)');
gradient.addColorStop(1, 'rgba(14, 165, 233, 0.5)');
return gradient;
}
}
]
};
const memoryChart = {
labels: Array(20).fill(''),
datasets: [
{
fill: true,
data: memoryUsage.value,
borderColor: '#0284c7',
backgroundColor: (ctx: any) => {
const chart = ctx.chart;
const { ctx: context, chartArea } = chart;
if (!chartArea) {
return null;
}
const gradient = context.createLinearGradient(0, chartArea.bottom, 0, chartArea.top);
gradient.addColorStop(0, 'rgba(14, 165, 233, 0.1)');
gradient.addColorStop(1, 'rgba(14, 165, 233, 0.5)');
return gradient;
}
}
]
};
const openConnection = () => {
let retryTimeout;
let hasRun = false;
const source = new SSE(`${props.base}/live/daemon/${props.server}/metrics`, { headers });
setLive(source);
source.onmessage = (event) => {
const data = JSON.parse(event.data);
setItem(data);
memoryUsage.pushMax(data.raw.memory_usage.rss);
- cpuPercentage.pushMax(data.raw.cpu_percent + 1);
+ cpuPercentage.pushMax(data.raw.cpu_percent);
if (!hasRun) {
setLoaded(true);
hasRun = true;
}
};
source.onerror = (error) => {
source.close();
retryTimeout = setTimeout(() => {
openConnection();
}, 5000);
};
return retryTimeout;
};
useEffect(() => {
const retryTimeout = openConnection();
return () => {
live && live.close();
clearTimeout(retryTimeout);
};
}, []);
if (!loaded) {
return <Loader />;
} else {
const stats = [
{ name: 'Uptime', stat: startDuration(item.daemon.uptime, false) },
{ name: 'Count', stat: item.daemon.process_count },
{ name: 'Version', stat: item.version.pkg },
{ name: 'Process Id', stat: item.daemon.pid },
{ name: 'Build date', stat: item.version.build_date },
{ name: 'Hash', stat: item.version.hash.slice(0, 18) },
{ name: 'Platform', stat: `${item.os.name} ${item.os.version} (${item.os.arch})` },
{ name: 'Daemon', stat: item.daemon.daemon_type }
];
return (
<Fragment>
<div className="absolute top-2 right-3 z-[200]">
<span className="inline-flex items-center gap-x-1.5 rounded-md px-2 py-1 text-xs font-medium text-white ring-1 ring-inset ring-zinc-800">
<svg viewBox="0 0 6 6" aria-hidden="true" className="h-1.5 w-1.5 fill-green-400">
<circle r={3} cx={3} cy={3} />
</svg>
{props.name != 'local' ? props.name : 'Internal'}
</span>
</div>
<dl className="mt-8 grid grid-cols-1 gap-5 sm:grid-cols-2 px-5">
<div className="overflow-hidden rounded-lg bg-zinc-900/20 border border-zinc-800 shadow">
<dt className="truncate text-sm font-bold text-zinc-400 pt-4 px-4">CPU Usage</dt>
<dt className="truncate text-xl font-bold text-zinc-100 p-1 px-4">
{cpuPercentage.value.slice(-1)[0].toFixed(2)}
<span className="text-base text-zinc-400">%</span>
</dt>
<dd className="mt-2 text-3xl font-semibold tracking-tight text-zinc-100 h-96" style={chartContainerStyle}>
<Line data={cpuChart} options={options} />
</dd>
</div>
<div className="overflow-hidden rounded-lg bg-zinc-900/20 border border-zinc-800 shadow">
<dt className="truncate text-sm font-bold text-zinc-400 pt-4 px-4">Memory Usage</dt>
<dt className="truncate text-xl font-bold text-zinc-100 p-1 px-4">{bytesToSize(memoryUsage.value.slice(-1)[0], 2)}</dt>
<dd className="mt-2 text-3xl font-semibold tracking-tight text-zinc-100 h-96" style={chartContainerStyle}>
<Line data={memoryChart} options={options} />
</dd>
</div>
</dl>
<dl className="mt-5 pb-5 grid grid-cols-2 gap-5 lg:grid-cols-4 px-5 h-3/10">
{stats.map((item: any) => (
<div key={item.name} className="overflow-hidden rounded-lg bg-zinc-900/20 border border-zinc-800 px-4 py-5 shadow sm:p-6">
<dt className="truncate text-sm font-medium text-zinc-400">{item.name}</dt>
<dd className="mt-1 text-2xl font-semibold tracking-tight text-zinc-100">{item.stat}</dd>
</div>
))}
</dl>
</Fragment>
);
}
};
export default Status;

File Metadata

Mime Type
text/x-diff
Expires
Sun, Feb 1, 10:09 PM (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
494943
Default Alt Text
(52 KB)

Event Timeline