mirror of
https://github.com/outbackdingo/sysadm.git
synced 2026-01-27 18:20:23 +00:00
"action"="deviceinfo" will return the full information about all devices attached to the system (via `pciconf -lv`)
-------------------
REST Request (example):
-------------------------------
PUT /sysadm/systemmanager
{
"action" : "deviceinfo"
}
WebSocket Request:
-------------------------------
{
"id" : "fooid",
"name" : "systemmanager",
"namespace" : "sysadm",
"args" : {
"action" : "deviceinfo"
}
}
Response:
-------------------------------
{
"args": {
"deviceinfo": {
"ahci0": {
"class": "mass storage",
"device": "8 Series/C220 Series Chipset Family 6-port SATA Controller 1 [AHCI mode]",
"subclass": "SATA",
"vendor": "Intel Corporation"
},
"ehci0": {
"class": "serial bus",
"device": "8 Series/C220 Series Chipset Family USB EHCI",
"subclass": "USB",
"vendor": "Intel Corporation"
},
"ehci1": {
"class": "serial bus",
"device": "8 Series/C220 Series Chipset Family USB EHCI",
"subclass": "USB",
"vendor": "Intel Corporation"
},
"hdac0": {
"class": "multimedia",
"subclass": "HDA",
"vendor": "NVIDIA Corporation"
},
"hdac1": {
"class": "multimedia",
"device": "8 Series/C220 Series Chipset High Definition Audio Controller",
"subclass": "HDA",
"vendor": "Intel Corporation"
},
"hostb0": {
"class": "bridge",
"device": "4th Gen Core Processor DRAM Controller",
"subclass": "HOST-PCI",
"vendor": "Intel Corporation"
},
"isab0": {
"class": "bridge",
"device": "B85 Express LPC Controller",
"subclass": "PCI-ISA",
"vendor": "Intel Corporation"
},
"none0": {
"class": "simple comms",
"device": "8 Series/C220 Series Chipset Family MEI Controller",
"vendor": "Intel Corporation"
},
"none1": {
"class": "serial bus",
"device": "8 Series/C220 Series Chipset Family SMBus Controller",
"subclass": "SMBus",
"vendor": "Intel Corporation"
},
"pcib1": {
"class": "bridge",
"device": "Xeon E3-1200 v3/4th Gen Core Processor PCI Express x16 Controller",
"subclass": "PCI-PCI",
"vendor": "Intel Corporation"
},
"pcib2": {
"class": "bridge",
"device": "8 Series/C220 Series Chipset Family PCI Express Root Port",
"subclass": "PCI-PCI",
"vendor": "Intel Corporation"
},
"pcib3": {
"class": "bridge",
"device": "8 Series/C220 Series Chipset Family PCI Express Root Port",
"subclass": "PCI-PCI",
"vendor": "Intel Corporation"
},
"pcib4": {
"class": "bridge",
"device": "8 Series/C220 Series Chipset Family PCI Express Root Port",
"subclass": "PCI-PCI",
"vendor": "Intel Corporation"
},
"pcib5": {
"class": "bridge",
"device": "82801 PCI Bridge",
"subclass": "PCI-PCI",
"vendor": "Intel Corporation"
},
"re0": {
"class": "network",
"device": "RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller",
"subclass": "ethernet",
"vendor": "Realtek Semiconductor Co., Ltd."
},
"vgapci0": {
"class": "display",
"device": "GM206 [GeForce GTX 960]",
"subclass": "VGA",
"vendor": "NVIDIA Corporation"
},
"xhci0": {
"class": "serial bus",
"device": "8 Series/C220 Series Chipset Family USB xHCI",
"subclass": "USB",
"vendor": "Intel Corporation"
}
}
},
"id": "fooid",
"name": "response",
"namespace": "sysadm"
}
475 lines
15 KiB
C++
475 lines
15 KiB
C++
//===========================================
|
|
// PC-BSD source code
|
|
// Copyright (c) 2015, PC-BSD Software/iXsystems
|
|
// Available under the 3-clause BSD license
|
|
// See the LICENSE file for full details
|
|
//===========================================
|
|
#include "sysadm-general.h"
|
|
#include "sysadm-systemmanager.h"
|
|
#include "sysadm-global.h"
|
|
|
|
using namespace sysadm;
|
|
|
|
//PLEASE: Keep the functions in the same order as listed in pcbsd-general.h
|
|
|
|
|
|
//Battery Availability
|
|
QJsonObject SysMgmt::batteryInfo(){
|
|
QJsonObject retObject;
|
|
bool ok;
|
|
|
|
int val = General::RunCommand("apm -l").toInt(&ok);
|
|
if ( ok && (val >= 0 && val <= 100) ) {
|
|
retObject.insert("battery", "true");
|
|
} else {
|
|
retObject.insert("battery", "false");
|
|
return retObject;
|
|
}
|
|
|
|
// We have a battery, return info about it
|
|
//Battery Charge Level
|
|
QString tmp;
|
|
tmp.setNum(val);
|
|
retObject.insert("level", tmp);
|
|
|
|
//Battery Charging State
|
|
int state = General::RunCommand("apm -a").toInt(&ok);
|
|
if ( ok && state == 0 )
|
|
retObject.insert("status", "offline");
|
|
else if ( ok && state == 1 )
|
|
retObject.insert("status", "charging");
|
|
else if ( ok && state == 2 )
|
|
retObject.insert("status", "backup");
|
|
else
|
|
retObject.insert("status", "unknown");
|
|
|
|
int timeleft = General::RunCommand("apm -t").toInt(&ok);
|
|
if ( ok ) {
|
|
tmp.setNum(timeleft);
|
|
retObject.insert("timeleft", tmp);
|
|
} else {
|
|
retObject.insert("timeleft", "-1");
|
|
}
|
|
|
|
return retObject;
|
|
}
|
|
|
|
// KPM 1-21-2016
|
|
// This needs to be looked at, I'm not 100% sure it is returning correct busy %
|
|
// We probably want to supply more info as well, such as user,nice,system,interrupt,idle
|
|
QJsonObject SysMgmt::cpuPercentage() {
|
|
QJsonObject retObject;
|
|
QString tmp;
|
|
|
|
//Calculate the percentage based on the kernel information directly - no extra utilities
|
|
QStringList result = General::RunCommand("sysctl -n kern.cp_times").split(" ");
|
|
static QStringList last = QStringList();
|
|
if(last.isEmpty()){
|
|
//need two ticks before it works properly
|
|
sleep(1);
|
|
last = result;
|
|
result = General::RunCommand("sysctl -n kern.cp_times").split(" ");
|
|
}
|
|
double tot = 0;
|
|
double roundtot;
|
|
int cpnum = 0;
|
|
for(int i=4; i<result.length(); i+=5){
|
|
//The values come in blocks of 5 per CPU: [user,nice,system,interrupt,idle]
|
|
cpnum++; //the number of CPU's accounted for (to average out at the end)
|
|
//qDebug() <<"CPU:" << cpnum;
|
|
long sum = 0;
|
|
//Adjust/all the data to correspond to diffs from the previous check
|
|
for(int j=0; j<5; j++){
|
|
QString tmp = result[i-j];
|
|
result[i-j] = QString::number(result[i-j].toLong()-last[i-j].toLong());
|
|
//need the difference between last run and this one
|
|
sum += result[i-j].toLong();
|
|
last[i-j] = tmp; //make sure to keep the original value around for the next run
|
|
}
|
|
QJsonObject vals;
|
|
roundtot = 100.0L - ( (100.0L*result[i].toLong())/sum );
|
|
tmp.setNum(qRound(roundtot));
|
|
vals.insert("busy", tmp );
|
|
tmp.setNum(cpnum);
|
|
retObject.insert("cpu" + tmp, vals);
|
|
//Calculate the percentage used for this CPU (100% - IDLE%)
|
|
tot += 100.0L - ( (100.0L*result[i].toLong())/sum ); //remember IDLE is the last of the five values per CPU
|
|
}
|
|
|
|
// Add the total busy %
|
|
tmp.setNum(qRound(tot/cpnum));
|
|
retObject.insert("busytotal", tmp);
|
|
return retObject;
|
|
}
|
|
|
|
QJsonObject SysMgmt::cpuTemps() {
|
|
// Make sure coretemp is loaded
|
|
if ( General::RunCommand("kldstat").indexOf("coretemp") == -1 )
|
|
General::RunCommand("kldload coretemp");
|
|
|
|
QJsonObject retObject;
|
|
QStringList temps;
|
|
temps = General::RunCommand("sysctl -ai").split("\n").filter(".temperature:");
|
|
temps.sort();
|
|
for(int i=0; i<temps.length(); i++){
|
|
if(temps[i].contains(".acpi.") || temps[i].contains(".cpu")){
|
|
retObject.insert(temps[i].section(":", 0, 0).section(".", 1,2).replace(".", "").simplified(), temps[i].section(":", 1,5).simplified());
|
|
}else{
|
|
//non CPU temperature - skip it
|
|
temps.removeAt(i); i--;
|
|
}
|
|
}
|
|
return retObject;
|
|
}
|
|
|
|
// ==== ExternalDevicePaths() ====
|
|
QJsonObject SysMgmt::externalDevicePaths() {
|
|
QJsonObject retObject;
|
|
|
|
//Returns: QStringList[<type>::::<filesystem>::::<path>]
|
|
//Note: <type> = [USB, HDRIVE, DVD, SDCARD, UNKNOWN]
|
|
QStringList devs = General::RunCommand("mount").split("\n");
|
|
|
|
//Now check the output
|
|
for(int i=0; i<devs.length(); i++){
|
|
if(devs[i].startsWith("/dev/")){
|
|
devs[i].replace("\t"," ");
|
|
QString type = devs[i].section(" on ",0,0);
|
|
type.remove("/dev/");
|
|
//Determine the type of hardware device based on the dev node
|
|
if(type.startsWith("da")){ type = "USB"; }
|
|
else if(type.startsWith("ada")){ type = "HDRIVE"; }
|
|
else if(type.startsWith("mmsd")){ type = "SDCARD"; }
|
|
else if(type.startsWith("cd")||type.startsWith("acd")){ type="DVD"; }
|
|
else{ type = "UNKNOWN"; }
|
|
//Now put the device in the proper output format
|
|
QString filesystem = devs[i].section("(",1,1).section(",",0,0);
|
|
QString path = devs[i].section(" on ",1,50).section("(",0,0).simplified();
|
|
QJsonObject vals;
|
|
vals.insert("type",type);
|
|
vals.insert("filesystem",filesystem);
|
|
vals.insert("path",path);
|
|
retObject.insert(devs[i].section(" ", 0, 0), vals);
|
|
}else{
|
|
//invalid device - remove it from the list
|
|
devs.removeAt(i);
|
|
i--;
|
|
}
|
|
}
|
|
|
|
// Return the devices / mounts
|
|
return retObject;
|
|
}
|
|
|
|
// Kill a process
|
|
QJsonObject SysMgmt::killProc(QJsonObject jsin) {
|
|
QJsonObject retObject;
|
|
|
|
QStringList keys = jsin.keys();
|
|
if (! keys.contains("pid") ) {
|
|
retObject.insert("error", "Missing required key 'pid'");
|
|
return retObject;
|
|
}
|
|
// Save the target
|
|
QString pid, sig, tmp;
|
|
pid = jsin.value("pid").toString();
|
|
|
|
if ( keys.contains("signal") ) {
|
|
tmp = jsin.value("signal").toString();
|
|
if ( tmp == "HUP" ) {
|
|
sig = "-1";
|
|
} else if ( tmp == "INT" ) {
|
|
sig = "-2";
|
|
} else if ( tmp == "QUIT" ) {
|
|
sig = "-3";
|
|
} else if ( tmp == "ABRT" ) {
|
|
sig = "-6";
|
|
} else if ( tmp == "KILL" ) {
|
|
sig = "-9";
|
|
} else if ( tmp == "ALRM" ) {
|
|
sig = "-14";
|
|
} else if ( tmp == "TERM" ) {
|
|
sig = "-15";
|
|
} else {
|
|
retObject.insert("error", "Invalid signal! (INT|QUIT|ABRT|KILL|ALRM|TERM)");
|
|
return retObject;
|
|
}
|
|
} else {
|
|
sig = "-9";
|
|
}
|
|
|
|
// This could be switched to kill(2) at some point
|
|
General::RunCommand("kill " + sig + " " + pid);
|
|
|
|
return jsin;
|
|
}
|
|
|
|
// Return information about memory
|
|
QJsonObject SysMgmt::memoryStats() {
|
|
QJsonObject retObject;
|
|
|
|
QString tmp;
|
|
long pageSize;
|
|
bool ok;
|
|
|
|
// Get the page size
|
|
tmp = General::RunCommand("sysctl -n vm.stats.vm.v_page_size").simplified();
|
|
tmp.toLong(&ok);
|
|
if ( ok )
|
|
pageSize = tmp.toLong();
|
|
else
|
|
return retObject;
|
|
|
|
// Get the free size
|
|
tmp = General::RunCommand("sysctl -n vm.stats.vm.v_free_count").simplified();
|
|
tmp.toLong(&ok);
|
|
if ( ok )
|
|
retObject.insert("free", tmp.setNum((tmp.toLong() * pageSize) / 1024 / 1024));
|
|
|
|
// Get the inactive size
|
|
tmp = General::RunCommand("sysctl -n vm.stats.vm.v_inactive_count").simplified();
|
|
tmp.toLong(&ok);
|
|
if ( ok )
|
|
retObject.insert("inactive", tmp.setNum((tmp.toLong() * pageSize) / 1024 / 1024));
|
|
|
|
// Get the cache size
|
|
tmp = General::RunCommand("sysctl -n vm.stats.vm.v_cache_count").simplified();
|
|
tmp.toLong(&ok);
|
|
if ( ok )
|
|
retObject.insert("cache", tmp.setNum((tmp.toLong() * pageSize) / 1024 / 1024));
|
|
|
|
// Get the wired size
|
|
tmp = General::RunCommand("sysctl -n vm.stats.vm.v_wire_count").simplified();
|
|
tmp.toLong(&ok);
|
|
if ( ok )
|
|
retObject.insert("wired", tmp.setNum((tmp.toLong() * pageSize) / 1024 / 1024));
|
|
|
|
// Get the active size
|
|
tmp = General::RunCommand("sysctl -n vm.stats.vm.v_active_count").simplified();
|
|
tmp.toLong(&ok);
|
|
if ( ok )
|
|
retObject.insert("active", tmp.setNum((tmp.toLong() * pageSize) / 1024 / 1024));
|
|
|
|
return retObject;
|
|
}
|
|
|
|
// Return a json list of process information
|
|
QJsonObject SysMgmt::procInfo() {
|
|
QJsonObject retObject;
|
|
QStringList output;
|
|
output = General::RunCommand("top -t -n -a all").split("\n");
|
|
bool inSection = false;
|
|
for(int i=0; i<output.length(); i++){
|
|
if (output.at(i).contains("PID") && output.at(i).contains("USERNAME")){
|
|
inSection = true;
|
|
continue;
|
|
}
|
|
if (!inSection)
|
|
continue;
|
|
if ( output.at(i).isEmpty())
|
|
continue;
|
|
|
|
// Now, lets break down the various elements of process information
|
|
QJsonObject values;
|
|
QString line = output.at(i).simplified();
|
|
QString pid = line.section(" ", 0, 0);
|
|
values.insert("username", line.section(" ", 1, 1));
|
|
values.insert("thr", line.section(" ", 2, 2));
|
|
values.insert("pri", line.section(" ", 3, 3));
|
|
values.insert("nice", line.section(" ", 4, 4));
|
|
values.insert("size", line.section(" ", 5, 5));
|
|
values.insert("res", line.section(" ", 6, 6));
|
|
values.insert("state", line.section(" ", 7, 7));
|
|
values.insert("cpu", line.section(" ", 8, 8));
|
|
values.insert("time", line.section(" ", 9, 9));
|
|
values.insert("wcpu", line.section(" ", 10, 10));
|
|
values.insert("command", line.section(" ", 11, -1));
|
|
|
|
// Add the PID object
|
|
retObject.insert(pid, values);
|
|
}
|
|
return retObject;
|
|
}
|
|
|
|
// Set a sysctl
|
|
QJsonObject SysMgmt::setSysctl(QJsonObject jsin) {
|
|
QJsonObject retObject;
|
|
|
|
QStringList keys = jsin.keys();
|
|
if (! keys.contains("sysctl") || ! keys.contains("value") ) {
|
|
retObject.insert("error", "Missing required keys 'sysctl / value'");
|
|
return retObject;
|
|
}
|
|
QString sysctl, value;
|
|
sysctl = jsin.value("sysctl").toString();
|
|
value = jsin.value("value").toString();
|
|
QString output = General::RunCommand("sysctl " + sysctl + "=" + value);
|
|
|
|
retObject.insert("sysctl", sysctl);
|
|
retObject.insert("value", value);
|
|
retObject.insert("response", output.simplified());
|
|
|
|
return retObject;
|
|
}
|
|
|
|
// Return list of sysctls and their values
|
|
QJsonObject SysMgmt::sysctlList() {
|
|
QJsonObject retObject;
|
|
|
|
// This can be cleaned up and not use CLI
|
|
QStringList output = General::RunCommand("sysctl -W -a").split("\n");
|
|
|
|
QString sysctl, value;
|
|
for(int i=0; i<output.length(); i++){
|
|
if ( output.at(i).isEmpty())
|
|
continue;
|
|
|
|
sysctl = output.at(i).section(":", 0, 0);
|
|
value = output.at(i).section(":", 1, -1).simplified();
|
|
retObject.insert(sysctl, value);
|
|
}
|
|
|
|
return retObject;
|
|
}
|
|
|
|
// Return a bunch of various system information
|
|
QJsonObject SysMgmt::systemInfo() {
|
|
QJsonObject retObject;
|
|
|
|
QString arch = General::RunCommand("uname -m").simplified();
|
|
retObject.insert("arch", arch);
|
|
|
|
QString sysver = General::RunCommand("freebsd-version").simplified();
|
|
retObject.insert("systemversion", sysver);
|
|
|
|
QString kernver = General::RunCommand("uname -r").simplified();
|
|
retObject.insert("kernelversion", kernver);
|
|
|
|
QString kernident = General::RunCommand("uname -i").simplified();
|
|
retObject.insert("kernelident", kernident);
|
|
|
|
QString host = General::RunCommand("hostname").simplified();
|
|
retObject.insert("hostname", host);
|
|
|
|
QString uptime = General::RunCommand("uptime").simplified().section(" ", 1, 4).simplified().replace(",", "");
|
|
retObject.insert("uptime", uptime);
|
|
|
|
QString cputype = General::RunCommand("sysctl -n hw.model").simplified();
|
|
retObject.insert("cputype", cputype);
|
|
|
|
QString cpucores = General::RunCommand("sysctl -n kern.smp.cpus").simplified();
|
|
retObject.insert("cpucores", cpucores);
|
|
|
|
bool ok;
|
|
QString tmp;
|
|
QString totalmem = General::RunCommand("sysctl -n hw.realmem").simplified();
|
|
totalmem.toLong(&ok);
|
|
if ( ok ) {
|
|
retObject.insert("totalmem", tmp.setNum(totalmem.toLong() / 1024 / 1024));
|
|
}
|
|
|
|
return retObject;
|
|
}
|
|
|
|
// Halt the box
|
|
QJsonObject SysMgmt::systemHalt() {
|
|
QJsonObject retObject;
|
|
|
|
QString output = General::RunCommand("shutdown -p now");
|
|
retObject.insert("response", "true");
|
|
|
|
return retObject;
|
|
}
|
|
|
|
|
|
// Reboot the box
|
|
QJsonObject SysMgmt::systemReboot() {
|
|
QJsonObject retObject;
|
|
|
|
QString output = General::RunCommand("shutdown -r now");
|
|
retObject.insert("response", "true");
|
|
|
|
return retObject;
|
|
}
|
|
|
|
// Get all device information about the system
|
|
QJsonObject SysMgmt::systemDevices(){
|
|
QJsonObject pciconf_info;
|
|
QStringList lines = General::RunCommand("pciconf -lv").split("\n");
|
|
//qDebug() << "Raw pciconf:" << lines;
|
|
QJsonObject cobj; QString cid;
|
|
for(int i=0; i<lines.length(); i++){
|
|
if(!lines[i].contains(" = ") && !cid.isEmpty()){ pciconf_info.insert(cid,cobj); cid.clear(); cobj = QJsonObject(); }
|
|
if(lines[i].contains(" = ")){
|
|
QString var = lines[i].section("=",0,0).simplified();
|
|
QString val = lines[i].section("=",1,-1).simplified();
|
|
if(val.startsWith("\'") && val.endsWith("\'")){ val.chop(1); val = val.remove(0,1); }
|
|
//qDebug() << "PCICONF LINE:" << lines[i];
|
|
//qDebug() << "\t\t" << var << val;
|
|
cobj.insert(var,val);
|
|
}else{
|
|
//New device section
|
|
cid = lines[i].section("@",0,0);
|
|
}
|
|
}
|
|
if(!cid.isEmpty() && cobj.keys().isEmpty()){ pciconf_info.insert(cid,cobj); }
|
|
return pciconf_info;
|
|
}
|
|
|
|
// Source Management
|
|
/*
|
|
void SysMgmt::fetchPortsTree(QStringList &cmds, QStringList &dirs){
|
|
//Clear the output variables
|
|
cmds.clear(); dirs.clear();
|
|
//Does Ports tree exist? If not create it.
|
|
if(!QFile::exists("/usr/ports")){
|
|
cmds << "mkdir /usr/ports"; dirs << "";
|
|
}
|
|
//Does a local git repo exist? If not create it.
|
|
QString URL = "https://www.github.com/trueos/freebsd-ports.git";
|
|
if(QFile::exists("/usr/ports/.git")){
|
|
//Check if the remote URL is correct
|
|
QString origin = General::gitCMD("/usr/ports", "git remote show -n origin").filter("Fetch URL:").join("").section("URL:",1,30).simplified();
|
|
if(origin != URL){
|
|
cmds << "git remote remove origin"; dirs <<"/usr/ports";
|
|
cmds << "git remote add origin "+URL; dirs << "/usr/ports/.git";
|
|
}
|
|
}else{
|
|
//new GIT setup
|
|
General::emptyDir("/usr/ports");
|
|
cmds << "git init"; dirs << "/usr/ports"; //setup git
|
|
cmds << "git remote add origin "+URL; dirs << "/usr/ports/.git"; //setup TrueOS git repo
|
|
}
|
|
//Now update the tree with git
|
|
cmds << "git fetch --depth=1"; dirs << "/usr/ports/.git";
|
|
cmds << "git checkout master"; dirs << "/usr/ports";
|
|
}
|
|
|
|
void SysMgmt::fetchSourceTree(QString branch, QStringList &cmds, QStringList &dirs, QStringList &info){
|
|
//Clear the output variables
|
|
cmds.clear(); dirs.clear();
|
|
//Check if the source directory even exists
|
|
if(!QFile::exists("/usr/src")){
|
|
cmds << "mkdir /usr/src"; dirs << ""; //Create the ports tree
|
|
}
|
|
//Now check if the git directory exists
|
|
QString URL = "https://www.github.com/pcbsd/freebsd.git";
|
|
if(QFile::exists("/usr/src/.git")){
|
|
//Check if the remote URL is correct
|
|
QString origin = General::gitCMD("/usr/src", "git remote show -n origin").filter("Fetch URL:").join("").section("URL:",1,30).simplified();
|
|
if(origin != URL){
|
|
cmds << "git remote remove origin"; dirs <<"/usr/src";
|
|
cmds << "git remote add origin "+URL; dirs << "/usr/src/.git"; //setup PC-BSD git repo
|
|
}
|
|
}else{
|
|
//new GIT setup
|
|
General::emptyDir("/usr/src");
|
|
cmds << "git init"; dirs << "/usr/src"; //setup git
|
|
cmds << "git remote add origin "+URL; dirs << "/usr/src/.git"; //setup PC-BSD git repo
|
|
}
|
|
//Now update the tree with git
|
|
cmds << "git fetch --depth=1"; dirs << "/usr/src/.git";
|
|
cmds << "git checkout "+branch; dirs << "/usr/src";
|
|
}
|
|
*/
|