Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2708385
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
52 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Feb 1, 10:09 PM (1 d, 16 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
494943
Default Alt Text
(52 KB)
Attached To
Mode
rPMC Process Management Controller
Attached
Detach File
Event Timeline
Log In to Comment