mirror of
https://github.com/outbackdingo/sysadm.git
synced 2026-01-28 10:20:22 +00:00
Compare commits
72 Commits
stable/201
...
v1.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
694951384d | ||
|
|
10d00d97d9 | ||
|
|
32d74530ad | ||
|
|
4b0d6334d1 | ||
|
|
ed2c244282 | ||
|
|
8ba0b72cd0 | ||
|
|
67e687ba4c | ||
|
|
0fdcdb2c9b | ||
|
|
aab6f484ef | ||
|
|
25177739c2 | ||
|
|
25ca2d430d | ||
|
|
6630432922 | ||
|
|
d7c4e29d9a | ||
|
|
fd5504b304 | ||
|
|
b08d8461fc | ||
|
|
c1be586f87 | ||
|
|
dd4ab15bb4 | ||
|
|
6b43bc6a29 | ||
|
|
d6147d70f4 | ||
|
|
77986e0312 | ||
|
|
06e2f83902 | ||
|
|
42adfc6ca1 | ||
|
|
f4a2459429 | ||
|
|
c5bf944017 | ||
|
|
2157dbb6c2 | ||
|
|
d07658ee21 | ||
|
|
355b47d93a | ||
|
|
62655ebd41 | ||
|
|
ce53715265 | ||
|
|
4247c3529a | ||
|
|
bb1ec413eb | ||
|
|
d7ddcb5ae0 | ||
|
|
654b1a72d5 | ||
|
|
3cb89665b7 | ||
|
|
5807bedd68 | ||
|
|
2240fdd8c7 | ||
|
|
52ae9e8e5c | ||
|
|
068ef24a66 | ||
|
|
fba91d29c9 | ||
|
|
a239e43c05 | ||
|
|
991dcdd2f9 | ||
|
|
e9a338fa0f | ||
|
|
6447b0fed1 | ||
|
|
cf5b11a539 | ||
|
|
93f4d89b7d | ||
|
|
4e23691d50 | ||
|
|
376eaa4e37 | ||
|
|
4dded17898 | ||
|
|
6ab7c3dc01 | ||
|
|
34b388277d | ||
|
|
00251895a3 | ||
|
|
09c41edaca | ||
|
|
4a866b924f | ||
|
|
58d309031f | ||
|
|
e78f6a003f | ||
|
|
724c8ac7a8 | ||
|
|
c4750152a6 | ||
|
|
a0a585353c | ||
|
|
3276b32f80 | ||
|
|
c744785115 | ||
|
|
acbf8d6730 | ||
|
|
b428fb531a | ||
|
|
737e90c42a | ||
|
|
a3c44a5f5b | ||
|
|
fdd864e298 | ||
|
|
4333ac6fba | ||
|
|
859b441ae2 | ||
|
|
ebae121639 | ||
|
|
2bf23b8aaf | ||
|
|
f1c3651ba5 | ||
|
|
35759b12a1 | ||
|
|
0e2d941ae1 |
@@ -162,7 +162,7 @@ Due to the number of repositories under the TrueOS "umbrella", the TrueOS Projec
|
||||
|
||||
* [trueos-core](https://github.com/trueos/trueos-core) : Used for general TrueOS issues, Pico issues, and feature requests.
|
||||
* [lumina](https://github.com/trueos/lumina) : Issues related to using the Lumina Desktop Environment.
|
||||
* (Coming Soon) [sysadm](https://github.com/trueos/sysadm) : Issues with using the SysAdm client or server.
|
||||
* [sysadm](https://github.com/trueos/sysadm) : Issues with using the SysAdm client or server.
|
||||
* [trueos-docs](https://github.com/trueos/trueos-docs) : Issues related to the TrueOS Handbook.
|
||||
* [lumina-docs](https://github.com/trueos/lumina-docs) : Issues related to the Lumina Handbook.
|
||||
* [sysadm-docs](https://github.com/trueos/sysadm-docs) : Issues related to the SysAdm API Guide, Client, and Server Handbooks.
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
# Created by: Kris Moore <kmoore@FreeBSD.org>
|
||||
# $FreeBSD$
|
||||
|
||||
PORTNAME= sysadm
|
||||
PORTVERSION= %%CHGVERSION%%
|
||||
CATEGORIES= sysutils
|
||||
|
||||
MAINTAINER= kmoore@FreeBSD.org
|
||||
MAINTAINER= jt@ixsystems.com
|
||||
COMMENT= SysAdm API server
|
||||
|
||||
LICENSE= BSD3CLAUSE
|
||||
|
||||
WRKSRC_SUBDIR= src
|
||||
USE_QT5= concurrent core network buildtools qmake gui websockets sql
|
||||
USES= pkgconfig tar:xz qmake ssl
|
||||
USES= pkgconfig tar:xz qmake ssl qt:5
|
||||
USE_QT= concurrent core network buildtools_build qmake_build websockets sql
|
||||
MAKE_ARGS= PREFIX=${STAGEDIR}${PREFIX}
|
||||
|
||||
USE_GITHUB= yes
|
||||
|
||||
21
src/qMDNS/LICENSE.md
Normal file
21
src/qMDNS/LICENSE.md
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Alex Spataru
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
701
src/qMDNS/qMDNS.cpp
Normal file
701
src/qMDNS/qMDNS.cpp
Normal file
@@ -0,0 +1,701 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Alex Spataru <alex_spataru@outlook.com>
|
||||
*
|
||||
* This file is part of qMDNS, which is released under the MIT license.
|
||||
* For more information, please read the LICENSE file in the root directory
|
||||
* of this project.
|
||||
*/
|
||||
|
||||
#include "qMDNS.h"
|
||||
|
||||
#include <QHostInfo>
|
||||
#include <QUdpSocket>
|
||||
#include <QHostAddress>
|
||||
#include <QNetworkInterface>
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* DNS port and mutlicast addresses
|
||||
*/
|
||||
const quint16 MDNS_PORT = 5353;
|
||||
const QHostAddress IPV6_ADDRESS = QHostAddress ("FF02::FB");
|
||||
const QHostAddress IPV4_ADDRESS = QHostAddress ("224.0.0.251");
|
||||
|
||||
/*
|
||||
* mDNS/DNS operation flags
|
||||
*/
|
||||
const quint16 kQR_Query = 0x0000;
|
||||
const quint16 kQR_Response = 0x8000;
|
||||
const quint16 kRecordA = 0x0001;
|
||||
const quint16 kRecordAAAA = 0x001C;
|
||||
const quint16 kNsecType = 0x002F;
|
||||
const quint16 kFQDN_Separator = 0x0000;
|
||||
const quint16 kFQDN_Length = 0xC00C;
|
||||
const quint16 kIN_BitFlush = 0x8001;
|
||||
const quint16 kIN_Normal = 0x0001;
|
||||
|
||||
/*
|
||||
* DNS query properties
|
||||
*/
|
||||
const quint16 kQuery_QDCOUNT = 0x02;
|
||||
const quint16 kQuery_ANCOUNT = 0x00;
|
||||
const quint16 kQuery_NSCOUNT = 0x00;
|
||||
const quint16 kQuery_ARCOUNT = 0x00;
|
||||
|
||||
/*
|
||||
* DNS response properties
|
||||
*/
|
||||
const quint16 kResponse_QDCOUNT = 0x00;
|
||||
const quint16 kResponse_ANCOUNT = 0x01;
|
||||
const quint16 kResponse_NSCOUNT = 0x00;
|
||||
const quint16 kResponse_ARCOUNT = 0x02;
|
||||
|
||||
/* Packet constants */
|
||||
const int MIN_LENGTH = 13;
|
||||
const int IPI_LENGTH = 10;
|
||||
const int IP4_LENGTH = IPI_LENGTH + 4;
|
||||
const int IP6_LENGTH = IPI_LENGTH + 16;
|
||||
|
||||
/**
|
||||
* Encondes the 16-bit \a number as two 8-bit numbers in a byte array
|
||||
*/
|
||||
QByteArray ENCODE_16_BIT (quint16 number) {
|
||||
QByteArray data;
|
||||
data.append ((number & 0xff00) >> 8);
|
||||
data.append ((number & 0xff));
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the 32-bit \a number as four 8-bit numbers
|
||||
*/
|
||||
QByteArray ENCODE_32_BIT (quint32 number) {
|
||||
QByteArray data;
|
||||
data.append ((number & 0xff000000UL) >> 24);
|
||||
data.append ((number & 0x00ff0000UL) >> 16);
|
||||
data.append ((number & 0x0000ff00UL) >> 8);
|
||||
data.append ((number & 0x000000ffUL));
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the 16-bit number stored in the \a upper and \a lower 8-bit numbers
|
||||
*/
|
||||
quint16 DECODE_16_BIT (quint8 upper, quint8 lower) {
|
||||
return (quint16) ((upper << 8) | lower);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the given \a socket to the given \a address and \a port.
|
||||
* Under GNU/Linux, this function implements a workaround of QTBUG-33419.
|
||||
*/
|
||||
bool BIND (QUdpSocket* socket, const QHostAddress& address, const int port) {
|
||||
if (!socket)
|
||||
return false;
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
int reuse = 1;
|
||||
int domain = PF_UNSPEC;
|
||||
|
||||
if (address.protocol() == QAbstractSocket::IPv4Protocol)
|
||||
domain = PF_INET;
|
||||
else if (address.protocol() == QAbstractSocket::IPv6Protocol)
|
||||
domain = PF_INET6;
|
||||
|
||||
socket->setSocketDescriptor (::socket (domain, SOCK_DGRAM, 0),
|
||||
QUdpSocket::UnconnectedState);
|
||||
|
||||
setsockopt (socket->socketDescriptor(), SOL_SOCKET, SO_REUSEADDR,
|
||||
&reuse, sizeof (reuse));
|
||||
#endif
|
||||
|
||||
return socket->bind (address, port,
|
||||
QUdpSocket::ShareAddress |
|
||||
QUdpSocket::ReuseAddressHint);
|
||||
}
|
||||
|
||||
qMDNS::qMDNS() {
|
||||
/* Set default TTL to 4500 seconds */
|
||||
m_ttl = 4500;
|
||||
|
||||
/* Initialize sockets */
|
||||
m_IPv4Socket = new QUdpSocket (this);
|
||||
m_IPv6Socket = new QUdpSocket (this);
|
||||
|
||||
/* Read and interpret data received from mDNS group */
|
||||
connect (m_IPv4Socket, &QUdpSocket::readyRead, this, &qMDNS::onReadyRead);
|
||||
connect (m_IPv6Socket, &QUdpSocket::readyRead, this, &qMDNS::onReadyRead);
|
||||
|
||||
/* Bind the sockets to the mDNS multicast group */
|
||||
if (BIND (m_IPv4Socket, QHostAddress::AnyIPv4, MDNS_PORT))
|
||||
m_IPv4Socket->joinMulticastGroup (IPV4_ADDRESS);
|
||||
if (BIND (m_IPv6Socket, QHostAddress::AnyIPv6, MDNS_PORT))
|
||||
m_IPv6Socket->joinMulticastGroup (IPV6_ADDRESS);
|
||||
|
||||
cacheCleanTimer = new QTimer(this);
|
||||
cacheCleanTimer->setInterval(60000); //1 minute checks
|
||||
connect(cacheCleanTimer, SIGNAL(timeout()), this, SLOT(cleanCache()) );
|
||||
|
||||
knownHash = new QHash<QDateTime, QHostInfo>();
|
||||
|
||||
//connect the internal signal/slot for the cache
|
||||
connect(this, SIGNAL(hostFound(const QHostInfo&)), this, SLOT(NewHostFound(QHostInfo)) );
|
||||
}
|
||||
|
||||
qMDNS::~qMDNS() {
|
||||
delete m_IPv4Socket;
|
||||
delete m_IPv6Socket;
|
||||
delete knownHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the only running instance of this class
|
||||
*/
|
||||
qMDNS* qMDNS::getInstance() {
|
||||
static qMDNS instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mDNS name assigned to the client computer
|
||||
*/
|
||||
QString qMDNS::hostName() const {
|
||||
return m_hostName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the given \a string is a valid mDNS/DNS address.
|
||||
*/
|
||||
QString qMDNS::getAddress (const QString& string) {
|
||||
QString address = string;
|
||||
|
||||
if (!string.endsWith (".local") && !string.contains ("."))
|
||||
address = string + ".local";
|
||||
|
||||
if (string.endsWith ("."))
|
||||
return "";
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
QList<QHostInfo> qMDNS::knownHosts(){
|
||||
QList<QDateTime> keys = knownHash->keys();
|
||||
QList<QHostInfo> addrs;
|
||||
for(int i=0; i<keys.length(); i++){ addrs << knownHash->value(keys[i]); }
|
||||
//Go through and remove any duplicate addresses
|
||||
/*QStringList chk;
|
||||
for(int i=0; i<addrs.length(); i++){
|
||||
QString tmp = addrs[i]->toString();
|
||||
if(chk.contains(tmp)){
|
||||
//duplicate address - remove it
|
||||
addrs.removeAt(i); i--;
|
||||
}else{
|
||||
chk << tmp; //unique address - add it to the check list
|
||||
}
|
||||
}*/
|
||||
return addrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the TTL send to other computers in the mDNS network
|
||||
*/
|
||||
void qMDNS::setTTL (const quint32 ttl) {
|
||||
m_ttl = ttl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a mDNS lookup to find the given host \a name.
|
||||
* If \a preferIPv6 is set to \c true, then this function will generate a
|
||||
* packet that requests an AAAA-type Resource Record instead of an A-type
|
||||
* Resource Record.
|
||||
*/
|
||||
void qMDNS::lookup (const QString& name) {
|
||||
/* The host name is empty, abort lookup */
|
||||
if (name.isEmpty()) {
|
||||
qWarning() << Q_FUNC_INFO << "Empty host name specified";
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ensure that we host name is a valid DNS address */
|
||||
QString address = getAddress (name);
|
||||
if (address.isEmpty())
|
||||
return;
|
||||
|
||||
/* Check if we are dealing with a normal DNS address */
|
||||
if (!address.endsWith (".local", Qt::CaseInsensitive)) {
|
||||
QHostInfo::lookupHost (address, this, SIGNAL (hostFound (QHostInfo)));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Perform a mDNS lookup */
|
||||
else {
|
||||
QByteArray data;
|
||||
|
||||
/* Get the host name and domain */
|
||||
QString host = address.split (".").first();
|
||||
QString domain = address.split (".").last();
|
||||
|
||||
/* Check that domain length is valid */
|
||||
if (host.length() > 255) {
|
||||
qWarning() << Q_FUNC_INFO << host << "is too long!";
|
||||
return;
|
||||
}
|
||||
|
||||
/* Create header & flags */
|
||||
data.append (ENCODE_16_BIT (0));
|
||||
data.append (ENCODE_16_BIT (kQR_Query));
|
||||
data.append (ENCODE_16_BIT (kQuery_QDCOUNT));
|
||||
data.append (ENCODE_16_BIT (kQuery_ANCOUNT));
|
||||
data.append (ENCODE_16_BIT (kQuery_NSCOUNT));
|
||||
data.append (ENCODE_16_BIT (kQuery_ARCOUNT));
|
||||
|
||||
/* Add name data */
|
||||
data.append (host.length());
|
||||
data.append (host.toUtf8());
|
||||
|
||||
/* Add domain data */
|
||||
data.append (domain.length());
|
||||
data.append (domain.toUtf8());
|
||||
|
||||
/* Add FQDN/TLD separator */
|
||||
data.append ((char) kFQDN_Separator);
|
||||
|
||||
/* Add IPv4 record type */
|
||||
data.append (ENCODE_16_BIT (kRecordA));
|
||||
data.append (ENCODE_16_BIT (kIN_Normal));
|
||||
|
||||
/* Add FQDN length */
|
||||
data.append (ENCODE_16_BIT (kFQDN_Length));
|
||||
|
||||
/* Add IPv6 record type */
|
||||
data.append (ENCODE_16_BIT (kRecordAAAA));
|
||||
data.append (ENCODE_16_BIT (kIN_Normal));
|
||||
|
||||
/* Send the datagram */
|
||||
sendPacket (data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the host name of the client computer
|
||||
*/
|
||||
void qMDNS::setHostName (const QString& name) {
|
||||
if (name.contains (".") && !name.endsWith (".local")) {
|
||||
qWarning() << "Invalid domain name";
|
||||
return;
|
||||
}
|
||||
|
||||
m_hostName = getAddress (name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when we receive data from a mDNS client on the network.
|
||||
*/
|
||||
void qMDNS::onReadyRead() {
|
||||
QByteArray data;
|
||||
QUdpSocket* socket = qobject_cast<QUdpSocket*> (sender());
|
||||
|
||||
/* Read data from the socket */
|
||||
if (socket) {
|
||||
while (socket->hasPendingDatagrams()) {
|
||||
data.resize (socket->pendingDatagramSize());
|
||||
socket->readDatagram (data.data(), data.size());
|
||||
}
|
||||
}
|
||||
|
||||
/* Packet is a valid mDNS datagram */
|
||||
if (data.length() > MIN_LENGTH) {
|
||||
quint16 flag = DECODE_16_BIT (data.at (2), data.at (3));
|
||||
|
||||
if (flag == kQR_Query)
|
||||
readQuery (data);
|
||||
|
||||
else if (flag >= kQR_Response)
|
||||
readResponse (data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the given query \a data and instructs the class to send a response
|
||||
* packet if the query is looking for the host name assigned to this computer.
|
||||
*/
|
||||
void qMDNS::readQuery (const QByteArray& data) {
|
||||
/* Query packet is invalid */
|
||||
if (data.length() < MIN_LENGTH)
|
||||
return;
|
||||
|
||||
/* Get the lengths of the host name and domain */
|
||||
int n = 12;
|
||||
int hostLength = data.at (n);
|
||||
int domainLength = data.at (n + hostLength + 1);
|
||||
|
||||
/* Read the host name until we stumble with the domain length character */
|
||||
QString name;
|
||||
int h = n + 1;
|
||||
while (data.at (h) != (char) domainLength) {
|
||||
name.append (data.at (h));
|
||||
++h;
|
||||
}
|
||||
|
||||
/* Read domain length until we stumble with the FQDN/TLD separator */
|
||||
QString domain;
|
||||
int d = n + hostLength + 2;
|
||||
while (data.at (d) != kFQDN_Separator) {
|
||||
domain.append (data.at (d));
|
||||
++d;
|
||||
}
|
||||
|
||||
/* Construct the full host name (name + domain) */
|
||||
QString host = getAddress (name + "." + domain);
|
||||
|
||||
/* The query packet wants to know more about us */
|
||||
if (host.toLower() == hostName().toLower())
|
||||
sendResponse (DECODE_16_BIT (data.at (0), data.at (1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the given \a data to both the IPv4 and IPv6 mDNS multicast groups
|
||||
*/
|
||||
void qMDNS::sendPacket (const QByteArray& data) {
|
||||
if (!data.isEmpty()) {
|
||||
m_IPv4Socket->writeDatagram (data, IPV4_ADDRESS, MDNS_PORT);
|
||||
m_IPv6Socket->writeDatagram (data, IPV6_ADDRESS, MDNS_PORT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the given \a data of a response packet and obtains:
|
||||
* - The remote host name
|
||||
* - The remote IPv4
|
||||
* - The remote IPv6
|
||||
*/
|
||||
void qMDNS::readResponse (const QByteArray& data) {
|
||||
if (data.length() < MIN_LENGTH)
|
||||
return;
|
||||
|
||||
QString host = getHostNameFromResponse (data);
|
||||
QList<QHostAddress> addresses = getAddressesFromResponse (data, host);
|
||||
|
||||
if (!host.isEmpty() && !addresses.isEmpty()) {
|
||||
QHostInfo info;
|
||||
info.setHostName (host);
|
||||
info.setAddresses (addresses);
|
||||
info.setError (QHostInfo::NoError);
|
||||
|
||||
emit hostFound (info);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a response packet with:
|
||||
* - Our mDNS host name
|
||||
* - Our IPv4 address
|
||||
* - Our IPv6 address
|
||||
*/
|
||||
void qMDNS::sendResponse (const quint16 query_id) {
|
||||
if (!hostName().isEmpty() && hostName().endsWith (".local")) {
|
||||
QByteArray data;
|
||||
|
||||
/* Get the host name and domain */
|
||||
QString host = hostName().split (".").first();
|
||||
QString domain = hostName().split (".").last();
|
||||
|
||||
/* Get local IPs */
|
||||
quint32 ipv4 = 0;
|
||||
QList<QIPv6Address> ipv6;
|
||||
foreach (QHostAddress address, QNetworkInterface::allAddresses()) {
|
||||
if (!address.isLoopback()) {
|
||||
if (address.protocol() == QAbstractSocket::IPv4Protocol)
|
||||
ipv4 = (ipv4 == 0 ? address.toIPv4Address() : ipv4);
|
||||
|
||||
if (address.protocol() == QAbstractSocket::IPv6Protocol)
|
||||
ipv6.append (address.toIPv6Address());
|
||||
}
|
||||
}
|
||||
|
||||
/* Check that domain length is valid */
|
||||
if (host.length() > 255) {
|
||||
qWarning() << Q_FUNC_INFO << host << "is too long!";
|
||||
return;
|
||||
}
|
||||
|
||||
/* Create header and flags */
|
||||
data.append (ENCODE_16_BIT (query_id));
|
||||
data.append (ENCODE_16_BIT (kQR_Response));
|
||||
data.append (ENCODE_16_BIT (kResponse_QDCOUNT));
|
||||
data.append (ENCODE_16_BIT (kResponse_ANCOUNT));
|
||||
data.append (ENCODE_16_BIT (kResponse_NSCOUNT));
|
||||
data.append (ENCODE_16_BIT (kResponse_ARCOUNT));
|
||||
|
||||
/* Add name data */
|
||||
data.append (host.length());
|
||||
data.append (host.toUtf8());
|
||||
|
||||
/* Add domain data and FQDN/TLD separator */
|
||||
data.append (domain.length());
|
||||
data.append (domain.toUtf8());
|
||||
data.append ((char) kFQDN_Separator);
|
||||
|
||||
/* Add IPv4 address header */
|
||||
data.append (ENCODE_16_BIT (kRecordA));
|
||||
data.append (ENCODE_16_BIT (kIN_BitFlush));
|
||||
data.append (ENCODE_32_BIT (m_ttl));
|
||||
data.append (ENCODE_16_BIT (sizeof (ipv4)));
|
||||
|
||||
/* Add IPv4 bytes */
|
||||
data.append (ENCODE_32_BIT (ipv4));
|
||||
|
||||
/* Add FQDN offset */
|
||||
data.append (ENCODE_16_BIT (kFQDN_Length));
|
||||
|
||||
/* Add IPv6 addresses */
|
||||
foreach (QIPv6Address ip, ipv6) {
|
||||
data.append (ENCODE_16_BIT (kRecordAAAA));
|
||||
data.append (ENCODE_16_BIT (kIN_BitFlush));
|
||||
data.append (ENCODE_32_BIT (m_ttl));
|
||||
data.append (ENCODE_16_BIT (sizeof (ip.c)));
|
||||
|
||||
/* Add IPv6 bytes */
|
||||
for (unsigned long i = 0; i < sizeof (ip.c); ++i)
|
||||
data.append (ip.c [i]);
|
||||
|
||||
/* Add FQDN offset */
|
||||
data.append (ENCODE_16_BIT (kFQDN_Length));
|
||||
}
|
||||
|
||||
/* TODO: Generate NSEC code block */
|
||||
int nsec_length = 0;
|
||||
|
||||
/* Add NSEC data */
|
||||
data.append (ENCODE_16_BIT (kNsecType));
|
||||
data.append (ENCODE_16_BIT (kIN_BitFlush));
|
||||
data.append (ENCODE_32_BIT (m_ttl));
|
||||
data.append (ENCODE_16_BIT (nsec_length));
|
||||
|
||||
/* Send the response */
|
||||
sendPacket (data);
|
||||
}
|
||||
}
|
||||
|
||||
/* Internal slot for saving the host information into the cache */
|
||||
void qMDNS::NewHostFound(QHostInfo info){
|
||||
if(info.addresses().isEmpty()){ return; } //no address associated with this host - skip it
|
||||
knownHash->insert(QDateTime::currentDateTime(), info);
|
||||
if(!cacheCleanTimer->isActive()){ cacheCleanTimer->start(); }
|
||||
}
|
||||
|
||||
void qMDNS::cleanCache(){
|
||||
QList<QDateTime> keys = knownHash->keys();
|
||||
QDateTime cdt = QDateTime::currentDateTime().addSecs( 0-(60*15)); //15 minutes in the past
|
||||
for(int i=0; i<keys.length(); i++){
|
||||
if(keys[i] < cdt){ knownHash->remove(keys[i]); }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the host name from the \a data received from the mDNS network.
|
||||
* The host name begins at byte #12 (when the header and flags end) and ends
|
||||
* with a mandatory NUL character after the domain.
|
||||
*
|
||||
* The host name is constructed in the following way (without spaces):
|
||||
* \c NAME_LENGTH + \c NAME + \c DOMAIN_LENGTH + \c DOMAIN + \c NUL
|
||||
*
|
||||
* For example, appletv.local would be formatted as:
|
||||
* \c 0x07 + \c appletv + \c 0x05 + \c local + \c 0x00
|
||||
*
|
||||
* Or, if you prefer hex data:
|
||||
* \c { 07 61 70 70 6c 65 74 76 05 6c 6f 63 61 6c 00 }
|
||||
* \c { 7 a p p l e t v 5 l o c a l 0 }
|
||||
*
|
||||
* In order to obtain the full host name (and its mDNS domain), we construct
|
||||
* the string backwards. When the code notices that the current character is
|
||||
* the same as the domain length, we know that the domain name has been
|
||||
* extracted, and thus we can replace the domain length with a dot (.) and
|
||||
* begin extracting the host name.
|
||||
*/
|
||||
QString qMDNS::getHostNameFromResponse (const QByteArray& data) {
|
||||
QList<char> list;
|
||||
QString address = "";
|
||||
|
||||
/* Begin reading host name at byte 13 (byte 12 is the host name length) */
|
||||
int n = 13;
|
||||
|
||||
/* Read the host name until we stumble with the FQDN/TLD separator */
|
||||
while (data.at (n) != kFQDN_Separator) {
|
||||
list.append (data.at (n));
|
||||
++n;
|
||||
}
|
||||
|
||||
/* Construct the string backwards (to replace domain length with a dot) */
|
||||
for (int i = 0; i < list.count(); ++i) {
|
||||
char character = list.at (list.count() - i - 1);
|
||||
|
||||
if (character == (char) address.length())
|
||||
address.prepend (".");
|
||||
else
|
||||
address.prepend (character);
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the IPv4 from the \a data received from the mDNS network.
|
||||
* The IPv4 data begins when the host name data ends.
|
||||
*
|
||||
* For the packet to contain IPv4 information, the DNS Record Type code must
|
||||
* be "A" (IPv4) and the DNS Class code should correspond to "IN" (Internet).
|
||||
*
|
||||
* Here is the layout of the IPv4 section of the packet:
|
||||
*
|
||||
* - DNS Record Type
|
||||
* - DNS Class Code
|
||||
* - TTL
|
||||
* - IP length
|
||||
* - IP address bytes
|
||||
*
|
||||
* This is an example IPv4 section:
|
||||
* \c {00 01 80 01 00 00 78 00 00 04 99 6d 07 5a}
|
||||
*
|
||||
* Data in example section:
|
||||
* - \c {00 01} Type Codes
|
||||
* - \c {80 01} Class Codes
|
||||
* - \c {00 00 78 00} IP TTL
|
||||
* - \c {00 04} Number of address bytes (length in layman's terms)
|
||||
* - \c {99 6d 07 5a} IPv4 Address bytes (153, 109, 7, 90)
|
||||
*/
|
||||
QString qMDNS::getIPv4FromResponse (const QByteArray& data,
|
||||
const QString& host) {
|
||||
QString ip = "";
|
||||
|
||||
/* n stands for the byte index in which the host name data ends */
|
||||
int n = MIN_LENGTH + host.length();
|
||||
|
||||
/* Packet is too small */
|
||||
if (data.length() < n + IP4_LENGTH)
|
||||
return ip;
|
||||
|
||||
/* Get the IP type and class codes */
|
||||
quint16 typeCode = DECODE_16_BIT (data.at (n + 1), data.at (n + 2));
|
||||
quint16 classCode = DECODE_16_BIT (data.at (n + 3), data.at (n + 4));
|
||||
|
||||
/* Check if type and class codes are good */
|
||||
if (typeCode != kRecordA || classCode != kIN_BitFlush)
|
||||
return ip;
|
||||
|
||||
/* Skip TTL indicator and obtain the number of address bytes */
|
||||
quint8 length = data.at (n + IPI_LENGTH);
|
||||
|
||||
/* Append each IPv4 address byte (and decimal dots) to the IP string */
|
||||
for (int i = 1; i < length + 1; ++i) {
|
||||
ip += QString::number ((quint8) data.at (n + IPI_LENGTH + i));
|
||||
ip += (i < length) ? "." : "";
|
||||
}
|
||||
|
||||
return ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the IPv6 from the \a data received from the mDNS network.
|
||||
* The IPv6 data begins when the host name data ends.
|
||||
*
|
||||
* For the packet to contain IPv6 information, the DNS Record Type code must
|
||||
* be "AAAA" (IPv6) and the DNS Class code should correspond to "IN" (Internet).
|
||||
*
|
||||
* Here is the layout of the IPv4 section of the packet:
|
||||
*
|
||||
* - DNS Record Type
|
||||
* - DNS Class Code
|
||||
* - TTL
|
||||
* - IP length
|
||||
* - IP address bytes
|
||||
*
|
||||
* This is an example IPv6 section:
|
||||
* \c { 00 1c 80 01 00 00 78 00 00 10 fe 80 00 00 00 00 00 00 02 23 32 ff fe b1 21 52 }
|
||||
*
|
||||
* Data in example section:
|
||||
* - \c {00 1c} Type Codes
|
||||
* - \c {80 01} Class Codes
|
||||
* - \c {00 00 78 00} IP TTL
|
||||
* - \c {00 10} Number of address bytes (length in layman's terms)
|
||||
* - \c {fe 80 00 00 ... 52} IPv6 Address bytes (there are 16 of them)
|
||||
*/
|
||||
QStringList qMDNS::getIPv6FromResponse (const QByteArray& data,
|
||||
const QString& host) {
|
||||
QStringList list;
|
||||
|
||||
/* Skip the FQDN and IPv4 section */
|
||||
int n = MIN_LENGTH + IP4_LENGTH + host.length();
|
||||
|
||||
/* Get the IPv6 list */
|
||||
bool isIPv6 = true;
|
||||
while (isIPv6) {
|
||||
/* Skip FQDN bytes */
|
||||
n += 2;
|
||||
|
||||
/* Packet is invalid */
|
||||
if (data.length() < n + IP6_LENGTH)
|
||||
break;
|
||||
|
||||
/* Get the IP type and class codes */
|
||||
quint16 typeCode = DECODE_16_BIT (data.at (n + 1), data.at (n + 2));
|
||||
quint16 classCode = DECODE_16_BIT (data.at (n + 3), data.at (n + 4));
|
||||
isIPv6 = (typeCode == kRecordAAAA && classCode == kIN_BitFlush);
|
||||
|
||||
/* IP type and class codes are OK, extract IP */
|
||||
if (isIPv6) {
|
||||
/* Skip TTL indicator and obtain the number of address bytes */
|
||||
quint8 length = data.at (n + IPI_LENGTH);
|
||||
|
||||
/* Append each IPv6 address byte (encoded as hex) to the IP string */
|
||||
QString ip = "";
|
||||
for (int i = 1; i < length + 1; ++i) {
|
||||
/* Get the hexadecimal representation of the byte */
|
||||
QString byte;
|
||||
byte.setNum ((quint8) data.at (n + i + IPI_LENGTH), 16);
|
||||
|
||||
/* Add the obtained string */
|
||||
ip += byte;
|
||||
|
||||
/* Append colons after even indexes (except in the last byte) */
|
||||
if ((i & 1) == 0 && (i < length))
|
||||
ip += ":";
|
||||
}
|
||||
|
||||
/* Increase the counter to 'jump' to the next section */
|
||||
n += 26;
|
||||
|
||||
/* Append the obtained IP to the list */
|
||||
if (!list.contains (ip))
|
||||
list.append (ip);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the IPv4 and IPv6 addresses from the received data.
|
||||
* \note This function will only generate a list with the valid IP addresses.
|
||||
*/
|
||||
QList<QHostAddress> qMDNS::getAddressesFromResponse (const QByteArray& data,
|
||||
const QString& host) {
|
||||
QList<QHostAddress> list;
|
||||
|
||||
/* Add IPv4 address */
|
||||
QHostAddress IPv4Address = QHostAddress (getIPv4FromResponse (data, host));
|
||||
if (!IPv4Address.isNull())
|
||||
list.append (IPv4Address);
|
||||
|
||||
/* Add IPv6 addresses */
|
||||
foreach (QString ip, getIPv6FromResponse (data, host)) {
|
||||
QHostAddress address = QHostAddress (ip);
|
||||
if (!address.isNull())
|
||||
list.append (address);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
84
src/qMDNS/qMDNS.h
Normal file
84
src/qMDNS/qMDNS.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Alex Spataru <alex_spataru@outlook.com>
|
||||
*
|
||||
* This file is part of qMDNS, which is released under the MIT license.
|
||||
* For more information, please read the LICENSE file in the root directory
|
||||
* of this project.
|
||||
*/
|
||||
|
||||
// Modified May 2017 By Ken Moore <ken@ixsystems.com>
|
||||
// to provide caching/results of the network at any point in time
|
||||
// available under the 3-clause BSD license (or MIT license if preferred)
|
||||
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
#include <QHostInfo>
|
||||
#include <QHash>
|
||||
#include <QDateTime>
|
||||
|
||||
class QHostInfo;
|
||||
class QUdpSocket;
|
||||
class QHostAddress;
|
||||
|
||||
/**
|
||||
* \brief Implements a simple mDNS responder using Qt
|
||||
*
|
||||
* This implementation is able perform mDNS queries and mDNS responses in any
|
||||
* operating system supported by the Qt network module.
|
||||
*
|
||||
* You can obtain the IP of an mDNS device using the \c lookup() function,
|
||||
* the \c hostFound() signal will be emitted whenever this class interprets
|
||||
* a mDNS response packet and obtains valid information about a remote host.
|
||||
*
|
||||
* You can change the name that the local computer uses to identify itself
|
||||
* in the mDNS network using the \c setHostName() function.
|
||||
*
|
||||
* \todo Implement NSEC block code generation in the \c sendResponse() packet
|
||||
*/
|
||||
class qMDNS : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
signals:
|
||||
void hostFound (const QHostInfo& info);
|
||||
|
||||
public:
|
||||
static qMDNS* getInstance();
|
||||
|
||||
QString hostName() const;
|
||||
QString getAddress (const QString& string);
|
||||
|
||||
QList<QHostInfo> knownHosts(); // KM: return list of known/available hosts (those active within the last 15 minutes)
|
||||
|
||||
protected:
|
||||
explicit qMDNS();
|
||||
~qMDNS();
|
||||
|
||||
public slots:
|
||||
void setTTL (const quint32 ttl);
|
||||
void lookup (const QString& name);
|
||||
void setHostName (const QString& name);
|
||||
|
||||
private slots:
|
||||
void onReadyRead();
|
||||
void readQuery (const QByteArray& data);
|
||||
void sendPacket (const QByteArray& data);
|
||||
void readResponse (const QByteArray& data);
|
||||
void sendResponse (const quint16 query_id);
|
||||
void NewHostFound(QHostInfo info); // KM: internal slot for caching the new host information
|
||||
void cleanCache(); //KM: internal slot for cleaning out old cached info on a regular basis
|
||||
|
||||
private:
|
||||
QString getHostNameFromResponse (const QByteArray& data);
|
||||
QString getIPv4FromResponse (const QByteArray& data, const QString& host);
|
||||
QStringList getIPv6FromResponse (const QByteArray& data, const QString& host);
|
||||
QList<QHostAddress> getAddressesFromResponse (const QByteArray& data,
|
||||
const QString& host);
|
||||
|
||||
private:
|
||||
quint32 m_ttl;
|
||||
QString m_hostName;
|
||||
QUdpSocket* m_IPv4Socket;
|
||||
QUdpSocket* m_IPv6Socket;
|
||||
QHash<QDateTime, QHostInfo> *knownHash; //KM: internal cache for the host addresses that were found
|
||||
QTimer *cacheCleanTimer; //KM: Timer for cleaning up old entries in the cache
|
||||
};
|
||||
7
src/qMDNS/qMDNS.pri
Normal file
7
src/qMDNS/qMDNS.pri
Normal file
@@ -0,0 +1,7 @@
|
||||
QT *= core network
|
||||
|
||||
INCLUDEPATH *= $$PWD
|
||||
|
||||
HEADERS *= $$PWD/qMDNS.h
|
||||
|
||||
SOURCES *= $$PWD/qMDNS.cpp
|
||||
@@ -33,7 +33,7 @@ void DProcess::procReady(){
|
||||
rawcmds = cmds;
|
||||
proclog.insert("cmd_list",QJsonArray::fromStringList(cmds));
|
||||
proclog.insert("process_id",ID);
|
||||
proclog.insert("state","pending");
|
||||
proclog.insert("state","pending");
|
||||
this->emit ProcUpdate(ID, proclog);
|
||||
uptimer->setSingleShot(false);
|
||||
uptimer->setInterval(2000); //2 second intervals for "pending" pings
|
||||
@@ -42,12 +42,12 @@ void DProcess::procReady(){
|
||||
|
||||
void DProcess::startProc(){
|
||||
cmds.removeAll(""); //make sure no empty commands
|
||||
if(cmds.isEmpty()){
|
||||
if(cmds.isEmpty()){
|
||||
proclog.insert("state","finished");
|
||||
proclog.insert("time_finished", QDateTime::currentDateTime().toString(Qt::ISODate));
|
||||
proclog.remove("current_cmd");
|
||||
emit ProcFinished(ID, proclog);
|
||||
return;
|
||||
return;
|
||||
}
|
||||
if(proclog.value("state").toString()=="pending"){
|
||||
//first cmd started
|
||||
@@ -66,7 +66,7 @@ void DProcess::startProc(){
|
||||
}
|
||||
|
||||
bool DProcess::isRunning(){
|
||||
return (this->state()!=QProcess::NotRunning);
|
||||
return (this->state()!=QProcess::NotRunning);
|
||||
}
|
||||
|
||||
bool DProcess::isDone(){
|
||||
@@ -90,10 +90,10 @@ void DProcess::cmdFinished(int ret, QProcess::ExitStatus status){
|
||||
//update the log before starting another command
|
||||
proclog.insert(cCmd, proclog.value(cCmd).toString().append(this->readAllStandardOutput()) );
|
||||
proclog.insert("return_codes/"+cCmd, QString::number(ret));
|
||||
|
||||
|
||||
//Now run any additional commands
|
||||
//qDebug() << "Proc Finished:" << ID << success << proclog;
|
||||
if(success && !cmds.isEmpty()){
|
||||
if(success && !cmds.isEmpty()){
|
||||
emit ProcUpdate(ID, proclog);
|
||||
startProc();
|
||||
}else{
|
||||
@@ -105,12 +105,18 @@ void DProcess::cmdFinished(int ret, QProcess::ExitStatus status){
|
||||
}
|
||||
|
||||
void DProcess::updateLog(){
|
||||
proclog.insert(cCmd, proclog.value(cCmd).toString().append(this->readAllStandardOutput()) );
|
||||
QString tmp = this->readAllStandardOutput();
|
||||
lognew.append(tmp);
|
||||
proclog.insert(cCmd, proclog.value(cCmd).toString().append(tmp) );
|
||||
if(!uptimer->isActive()){ uptimer->start(); }
|
||||
}
|
||||
|
||||
void DProcess::emitUpdate(){
|
||||
emit ProcUpdate(ID, proclog);
|
||||
QJsonObject tmp = proclog;
|
||||
//only emit the latest changes to the log - not the full thing
|
||||
tmp.insert(cCmd, lognew );
|
||||
emit ProcUpdate(ID, tmp);
|
||||
lognew.clear();
|
||||
}
|
||||
|
||||
// ================================
|
||||
@@ -123,7 +129,7 @@ Dispatcher::Dispatcher(){
|
||||
}
|
||||
|
||||
Dispatcher::~Dispatcher(){
|
||||
|
||||
|
||||
}
|
||||
|
||||
QJsonObject Dispatcher::listJobs(){
|
||||
@@ -153,7 +159,7 @@ QJsonObject Dispatcher::listJobs(){
|
||||
}
|
||||
out.insert(qname,obj);
|
||||
}
|
||||
} //end loop over queue types
|
||||
} //end loop over queue types
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -176,6 +182,24 @@ QJsonObject Dispatcher::killJobs(QStringList ids){
|
||||
return obj;
|
||||
}
|
||||
|
||||
bool Dispatcher::isJobActive(QString ID){
|
||||
//qDebug() << " - Is Job Active:" << ID;
|
||||
for(int i=0; i<enum_length; i++){
|
||||
PROC_QUEUE queue = static_cast<PROC_QUEUE>(i);
|
||||
if(HASH.contains(queue)){
|
||||
QList<DProcess*> list = HASH[queue];
|
||||
for(int j=0; j<list.length(); j++){
|
||||
if(ID == list[j]->ID){
|
||||
//qDebug() << " -- " << !list[j]->isDone();
|
||||
return !(list[j]->isDone());
|
||||
}
|
||||
} //end loop over list
|
||||
}
|
||||
}
|
||||
//qDebug() << " -- NO";
|
||||
return false; //could not find process with this ID
|
||||
}
|
||||
|
||||
void Dispatcher::start(QString queuefile){
|
||||
//Setup connections here (in case it was moved to different thread after creation)
|
||||
//connect(this, SIGNAL(mkprocs(Dispatcher::PROC_QUEUE, DProcess*)), this, SLOT(mkProcs(Dispatcher::PROC_QUEUE, DProcess*)) );
|
||||
@@ -190,41 +214,42 @@ void Dispatcher::stop(){
|
||||
}
|
||||
|
||||
//Overloaded Main Calling Functions (single command, or multiple in-order commands)
|
||||
DProcess* Dispatcher::queueProcess(QString ID, QString cmd){
|
||||
return queueProcess(NO_QUEUE, ID, QStringList() << cmd);
|
||||
DProcess* Dispatcher::queueProcess(QString ID, QString cmd, QString workdir){
|
||||
return queueProcess(NO_QUEUE, ID, QStringList() << cmd, workdir);
|
||||
}
|
||||
DProcess* Dispatcher::queueProcess(QString ID, QStringList cmds){
|
||||
return queueProcess(NO_QUEUE, ID, cmds);
|
||||
DProcess* Dispatcher::queueProcess(QString ID, QStringList cmds, QString workdir){
|
||||
return queueProcess(NO_QUEUE, ID, cmds, workdir);
|
||||
}
|
||||
DProcess* Dispatcher::queueProcess(Dispatcher::PROC_QUEUE queue, QString ID, QString cmd){
|
||||
return queueProcess(queue, ID, QStringList() << cmd);
|
||||
DProcess* Dispatcher::queueProcess(Dispatcher::PROC_QUEUE queue, QString ID, QString cmd, QString workdir){
|
||||
return queueProcess(queue, ID, QStringList() << cmd, workdir);
|
||||
}
|
||||
DProcess* Dispatcher::queueProcess(Dispatcher::PROC_QUEUE queue, QString ID, QStringList cmds){
|
||||
DProcess* Dispatcher::queueProcess(Dispatcher::PROC_QUEUE queue, QString ID, QStringList cmds, QString workdir){
|
||||
//This is the primary queueProcess() function - all the overloads end up here to do the actual work
|
||||
//For multi-threading, need to emit a signal/slot for this action (object creations need to be in same thread as parent)
|
||||
//qDebug() << "Queue Process:" << queue << ID << cmds;
|
||||
DProcess *P = createProcess(ID, cmds);
|
||||
DProcess *P = createProcess(ID, cmds, workdir);
|
||||
this->emit mkprocs(queue, P);
|
||||
return P;
|
||||
}
|
||||
|
||||
// === PRIVATE ===
|
||||
//Simplification routine for setting up a process
|
||||
DProcess* Dispatcher::createProcess(QString ID, QStringList cmds){
|
||||
DProcess* Dispatcher::createProcess(QString ID, QStringList cmds, QString workdir){
|
||||
DProcess *P = new DProcess();
|
||||
P->moveToThread(this->thread());
|
||||
P->cmds = cmds;
|
||||
P->ID = ID;
|
||||
if(!workdir.isEmpty()){ P->setWorkingDirectory(workdir); }
|
||||
return P;
|
||||
}
|
||||
|
||||
// === PRIVATE SLOTS ===
|
||||
void Dispatcher::mkProcs(Dispatcher::PROC_QUEUE queue, DProcess *P){
|
||||
//qDebug() << "mkProcs()";
|
||||
//qDebug() << "mkProcs()";
|
||||
QList<DProcess*> list = HASH.value(queue);
|
||||
list << P;
|
||||
//qDebug() << " - add to queue:" << queue;
|
||||
HASH.insert(queue,list);
|
||||
HASH.insert(queue,list);
|
||||
connect(P, SIGNAL(ProcFinished(QString, QJsonObject)), this, SLOT(ProcFinished(QString, QJsonObject)) );
|
||||
connect(P, SIGNAL(ProcUpdate(QString, QJsonObject)), this, SLOT(ProcUpdated(QString, QJsonObject)) );
|
||||
P->procReady();
|
||||
@@ -236,7 +261,7 @@ void Dispatcher::ProcFinished(QString ID, QJsonObject log){
|
||||
//qDebug() << " - Got Proc Finished Signal:" << ID;
|
||||
LogManager::log(LogManager::DISPATCH, log);
|
||||
//First emit any subsystem-specific event, falling back on the raw log
|
||||
QJsonObject ev = CreateDispatcherEventNotification(ID,log);
|
||||
QJsonObject ev = CreateDispatcherEventNotification(ID,log, true);
|
||||
if(!ev.isEmpty()){
|
||||
emit DispatchEvent(ev);
|
||||
}else{
|
||||
@@ -247,7 +272,7 @@ void Dispatcher::ProcFinished(QString ID, QJsonObject log){
|
||||
|
||||
void Dispatcher::ProcUpdated(QString ID, QJsonObject log){
|
||||
//See if this needs to generate an event
|
||||
QJsonObject ev = CreateDispatcherEventNotification(ID,log);
|
||||
QJsonObject ev = CreateDispatcherEventNotification(ID,log, false);
|
||||
if(!ev.isEmpty()){
|
||||
emit DispatchEvent(ev);
|
||||
}
|
||||
@@ -280,6 +305,6 @@ for(int i=0; i<enum_length; i++){
|
||||
}
|
||||
} //end loop over list
|
||||
}
|
||||
|
||||
} //end loop over queue types
|
||||
|
||||
} //end loop over queue types
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ class DProcess : public QProcess{
|
||||
public:
|
||||
DProcess(QObject *parent = 0);
|
||||
~DProcess();
|
||||
|
||||
|
||||
QString ID;
|
||||
QStringList cmds;
|
||||
|
||||
@@ -35,7 +35,7 @@ public slots:
|
||||
void startProc();
|
||||
|
||||
private:
|
||||
QString cCmd;
|
||||
QString cCmd, lognew;
|
||||
QJsonObject proclog;
|
||||
QTimer *uptimer;
|
||||
|
||||
@@ -50,19 +50,20 @@ signals:
|
||||
//Generic signals for subsystem usage (no direct proc access later)
|
||||
void ProcUpdate(QString, QJsonObject); // ID/log
|
||||
};
|
||||
|
||||
|
||||
|
||||
class Dispatcher : public QObject{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum PROC_QUEUE { NO_QUEUE = 0, PKG_QUEUE, IOCAGE_QUEUE };
|
||||
#define enum_length 3 //This needs to be the number of items in the enum above
|
||||
|
||||
|
||||
Dispatcher();
|
||||
~Dispatcher();
|
||||
|
||||
QJsonObject listJobs();
|
||||
QJsonObject killJobs(QStringList ids);
|
||||
bool isJobActive(QString ID); //returns true if a job with this ID is running/pending
|
||||
|
||||
public slots:
|
||||
//Main start/stop
|
||||
@@ -70,21 +71,21 @@ public slots:
|
||||
void stop(); //save any currently-unrun processes for next time
|
||||
|
||||
//Main Calling Functions (single command, or multiple in-order commands)
|
||||
DProcess* queueProcess(QString ID, QString cmd); //uses NO_QUEUE
|
||||
DProcess* queueProcess(QString ID, QStringList cmds); //uses NO_QUEUE
|
||||
DProcess* queueProcess(Dispatcher::PROC_QUEUE, QString ID, QString cmd);
|
||||
DProcess* queueProcess(Dispatcher::PROC_QUEUE, QString ID, QStringList cmds);
|
||||
DProcess* queueProcess(QString ID, QString cmd, QString workdir = ""); //uses NO_QUEUE
|
||||
DProcess* queueProcess(QString ID, QStringList cmds, QString workdir = ""); //uses NO_QUEUE
|
||||
DProcess* queueProcess(Dispatcher::PROC_QUEUE, QString ID, QString cmd, QString workdir = "");
|
||||
DProcess* queueProcess(Dispatcher::PROC_QUEUE, QString ID, QStringList cmds, QString workdir = "");
|
||||
|
||||
private:
|
||||
// Queue file
|
||||
QString queue_file;
|
||||
|
||||
|
||||
//Internal lists
|
||||
QHash<PROC_QUEUE, QList<DProcess*> > HASH;
|
||||
|
||||
//Simplification routine for setting up a process
|
||||
DProcess* createProcess(QString ID, QStringList cmds);
|
||||
QJsonObject CreateDispatcherEventNotification(QString, QJsonObject);
|
||||
DProcess* createProcess(QString ID, QStringList cmds, QString workdir = "");
|
||||
QJsonObject CreateDispatcherEventNotification(QString, QJsonObject, bool);
|
||||
|
||||
// Functions to do parsing out dispatcher queued tasks
|
||||
// Please keep these sorted
|
||||
@@ -104,7 +105,7 @@ signals:
|
||||
//Signals for private usage
|
||||
void mkprocs(Dispatcher::PROC_QUEUE, DProcess*);
|
||||
void checkProcs();
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -9,8 +9,13 @@
|
||||
#include "globals-qt.h"
|
||||
#include "EventWatcher.h"
|
||||
#include "Dispatcher.h"
|
||||
#include "library/sysadm-update.h"
|
||||
#include "library/sysadm-sourcectl.h"
|
||||
|
||||
|
||||
QJsonObject Dispatcher::CreateDispatcherEventNotification(QString ID, QJsonObject log, bool full_log){
|
||||
//NOTE: full_log = true when the process has finished. If it is false, the process is still running and you are probably getting an incremental update of the process log
|
||||
|
||||
QJsonObject Dispatcher::CreateDispatcherEventNotification(QString ID, QJsonObject log){
|
||||
//key outputs - need to set these if an event is going to be sent out
|
||||
QJsonObject args; //any arguments to send out
|
||||
QString namesp, name; //the namespace/name of the subsystem used
|
||||
@@ -26,7 +31,7 @@ QJsonObject Dispatcher::CreateDispatcherEventNotification(QString ID, QJsonObjec
|
||||
//Add the generic process values
|
||||
args.insert("state",log.value("state").toString());
|
||||
args.insert("process_details", log); //full process log array here
|
||||
|
||||
|
||||
//Now parse the notification based on the dispatch ID or current command
|
||||
//NOTE: There might be a random string on the end of the ID (to accomodate similar process calls)
|
||||
// == sysadm/iohyve ==
|
||||
@@ -37,14 +42,18 @@ QJsonObject Dispatcher::CreateDispatcherEventNotification(QString ID, QJsonObjec
|
||||
//Do some parsing of the log
|
||||
parseIohyveFetchOutput(cLog,&args);
|
||||
}
|
||||
|
||||
|
||||
// == sysadm/update ==
|
||||
}else if(ID.startsWith("sysadm_update")){
|
||||
namesp = "sysadm"; name="update";
|
||||
//No special parsing here: the pc-updatemanager output should be available as-is
|
||||
args.insert("update_log",cLog);
|
||||
|
||||
|
||||
if(ID.section("::",0,0)=="sysadm_update_runupdates"){
|
||||
namesp = "sysadm"; name="update";
|
||||
//No special parsing here: the pc-updatemanager output should be available as-is
|
||||
args.insert("update_log",cLog);
|
||||
}else if(isFinished && full_log && ID.section("::",0,0)=="sysadm_update_checkupdates"){
|
||||
//qDebug() << "Got update check process finished";
|
||||
sysadm::Update::saveCheckUpdateLog(cLog); //save this for use later
|
||||
}
|
||||
|
||||
// == sysadm/pkg ==
|
||||
}else if(ID.startsWith("sysadm_pkg")){
|
||||
namesp = "sysadm"; name="pkg";
|
||||
@@ -56,7 +65,7 @@ QJsonObject Dispatcher::CreateDispatcherEventNotification(QString ID, QJsonObjec
|
||||
bool hasupdates = !cLog.contains("Your packages are up to date.");
|
||||
args.insert("updates_available", hasupdates ? "true" : "false");
|
||||
}
|
||||
|
||||
|
||||
}else if(ID.section("-",0,0)=="sysadm_pkg_audit" && isFinished){
|
||||
QStringList info = cLog.split("\n");
|
||||
QStringList vuln, effects;
|
||||
@@ -71,11 +80,21 @@ QJsonObject Dispatcher::CreateDispatcherEventNotification(QString ID, QJsonObjec
|
||||
args.insert("vulnerable_pkgs",QJsonArray::fromStringList(vuln));
|
||||
args.insert("impacts_pkgs",QJsonArray::fromStringList(effects));
|
||||
}
|
||||
|
||||
|
||||
// == sysadm/sourcecrl ==
|
||||
}else if(ID.startsWith("sysadm_sourcectl_")){
|
||||
QString type = ID.section("::",0,0).section("_",2,-1); //type of sourcectl process
|
||||
namesp = "sysadm"; name="sourcectl";
|
||||
//No special parsing here: the git output should be available as-is
|
||||
args.insert("update_log",cLog);
|
||||
if(full_log){
|
||||
//qDebug() << "Got update check process finished";
|
||||
sysadm::sourcectl::savePortsLog(cLog); //save this for use later
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Now assemble the output as needed
|
||||
if(namesp.isEmpty() || name.isEmpty()){ return QJsonObject(); } //no event
|
||||
if(namesp.isEmpty() || name.isEmpty() || args.isEmpty()){ return QJsonObject(); } //no event
|
||||
args.insert("event_system",namesp+"/"+name);
|
||||
return args;
|
||||
}
|
||||
@@ -93,3 +112,7 @@ void Dispatcher::parseIohyveFetchOutput(QString outputLog, QJsonObject *out){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*void Dispatcher::parseUpdateCheckOutput(QString outputLog, QJsonObject *out){
|
||||
|
||||
}*/
|
||||
|
||||
@@ -40,7 +40,7 @@ void EventWatcher::start(){
|
||||
// - Life Preserver Events
|
||||
WatcherUpdate(LPLOG); //load it initially (will also add it to the watcher);
|
||||
WatcherUpdate(LPERRLOG); //load it initially (will also add it to the watcher);
|
||||
|
||||
|
||||
filechecktimer->start();
|
||||
syschecktimer->start();
|
||||
QTimer::singleShot(60000, this, SLOT(CheckSystemState()) ); //wait 1 minute for networking to settle down first
|
||||
@@ -64,7 +64,7 @@ QString EventWatcher::typeToString(EventWatcher::EVENT_TYPE typ){
|
||||
QJsonValue EventWatcher::lastEvent(EVENT_TYPE type){
|
||||
CheckLogFiles();
|
||||
if(HASH.contains(type)){ return HASH.value(type); }
|
||||
else{ qDebug() << "No saved event:" << type; return QJsonValue(); }
|
||||
else{ return QJsonValue(); }
|
||||
}
|
||||
|
||||
// === PRIVATE ===
|
||||
@@ -88,11 +88,11 @@ QString EventWatcher::readFile(QString path){
|
||||
QString contents = in.readAll();
|
||||
file.close();
|
||||
if(contents.endsWith("\n")){ contents.chop(1); }
|
||||
return contents;
|
||||
return contents;
|
||||
}
|
||||
|
||||
double EventWatcher::displayToDoubleK(QString displayNumber){
|
||||
QStringList labels;
|
||||
QStringList labels;
|
||||
labels << "K" << "M" << "G" << "T" << "P" << "E";
|
||||
QString clab = displayNumber.right(1); //last character is the size label
|
||||
displayNumber.chop(1); //remove the label from the number
|
||||
@@ -122,12 +122,12 @@ void EventWatcher::DispatchStarting(QString ID){
|
||||
void EventWatcher::DispatchEvent(QJsonObject obj){
|
||||
LogManager::log(LogManager::EV_DISPATCH, obj);
|
||||
//qDebug() << "Got Dispatch Finished: sending event...";
|
||||
emit NewEvent(DISPATCHER, obj);
|
||||
emit NewEvent(DISPATCHER, obj);
|
||||
}
|
||||
|
||||
// === PRIVATE SLOTS ===
|
||||
void EventWatcher::WatcherUpdate(const QString &path){
|
||||
if(!starting){ qDebug() << "Event Watcher Update:" << path; }
|
||||
//if(!starting){ qDebug() << "Event Watcher Update:" << path; }
|
||||
if(path==LPLOG){
|
||||
//Main Life Preserver Log File
|
||||
ReadLPLogFile();
|
||||
@@ -162,8 +162,8 @@ void EventWatcher::ReadLPLogFile(){
|
||||
if( !LPlogfile.open(QIODevice::ReadOnly) ){ return; } //could not open file
|
||||
QTextStream STREAM(&LPlogfile);
|
||||
qint64 LPlog_pos = CONFIG->value("internal/"+QString(WS_MODE ? "ws" : "tcp")+"/lp-log-pos",0).toLongLong();
|
||||
if(LPlog_pos>0 && QFileInfo(LPlogfile).created() < CONFIG->value("internal/"+QString(WS_MODE ? "ws" : "tcp")+"/lp-log-lastread").toDateTime() ){
|
||||
STREAM.seek(LPlog_pos);
|
||||
if(LPlog_pos>0 && QFileInfo(LPlogfile).created() < CONFIG->value("internal/"+QString(WS_MODE ? "ws" : "tcp")+"/lp-log-lastread").toDateTime() ){
|
||||
STREAM.seek(LPlog_pos);
|
||||
}
|
||||
QStringList info = STREAM.readAll().split("\n");
|
||||
//Now save the file pointer for later
|
||||
@@ -174,7 +174,7 @@ void EventWatcher::ReadLPLogFile(){
|
||||
for(int i=0; i<info.length(); i++){
|
||||
if(info[i].isEmpty()){ continue; }
|
||||
QString log = info[i];
|
||||
if(!starting){ qDebug() << "Read LP Log File Line:" << log; }
|
||||
// if(!starting){ qDebug() << "Read LP Log File Line:" << log; }
|
||||
//Divide up the log into it's sections
|
||||
QString timestamp = log.section(":",0,2).simplified();
|
||||
QString time = timestamp.section(" ",3,3).simplified();
|
||||
@@ -243,16 +243,15 @@ void EventWatcher::ReadLPLogFile(){
|
||||
HASH.insert(122, tr("Replication Failed") ); //summary
|
||||
HASH.insert(123, tt );
|
||||
HASH.insert(124, timestamp); //full timestamp
|
||||
HASH.insert(125, time); // time only
|
||||
HASH.insert(125, time); // time only
|
||||
HASH.insert(126, tr("Replication Error Log")+" <"+file+">" );
|
||||
sendLPEvent("replication", 7, timestamp+": "+tt);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void EventWatcher::ReadLPErrFile(){
|
||||
|
||||
|
||||
}
|
||||
|
||||
void EventWatcher::ReadLPRepFile(){
|
||||
@@ -268,7 +267,7 @@ void EventWatcher::ReadLPRepFile(){
|
||||
//New file location
|
||||
stat.clear();
|
||||
repTotK.clear();
|
||||
lastSize.clear();
|
||||
lastSize.clear();
|
||||
}
|
||||
QStringList info = STREAM.readAll().split("\n");
|
||||
CONFIG->setValue("internal/"+QString(WS_MODE ? "ws" : "tcp")+"/lp-rep-pos",STREAM.pos());
|
||||
@@ -283,7 +282,7 @@ void EventWatcher::ReadLPRepFile(){
|
||||
else{ stat = line; } //only save the relevant/latest status line
|
||||
}
|
||||
if(!stat.isEmpty()){
|
||||
//qDebug() << "New Status Message:" << stat;
|
||||
//qDebug() << "New Status Message:" << stat;
|
||||
//Divide up the status message into sections
|
||||
stat.replace("\t"," ");
|
||||
QString dataset = stat.section(" ",2,2,QString::SectionSkipEmpty).section("/",0,0).simplified();
|
||||
@@ -343,7 +342,7 @@ void EventWatcher::CheckSystemState(){
|
||||
}
|
||||
obj.insert("hostname",oldhostname);
|
||||
|
||||
//Next Check zpools
|
||||
//Next Check zpools
|
||||
QJsonObject zpools = sysadm::ZFS::zpool_list();
|
||||
if(!zpools.isEmpty()){
|
||||
//Scan each pool for any bad indicators
|
||||
@@ -369,10 +368,11 @@ void EventWatcher::CheckSystemState(){
|
||||
|
||||
//Next Check for Updates
|
||||
QJsonObject updates = sysadm::Update::checkUpdates(true); //do the "fast" version of updates
|
||||
//qDebug() << "Health check - got updates status:" << updates;
|
||||
if(!updates.isEmpty()){
|
||||
if(updates.value("status").toString()!="noupdates"){
|
||||
int tmp = 2;
|
||||
if(updates.value("status").toString()=="rebootrequired"){
|
||||
if(updates.value("status").toString()=="rebootrequired"){
|
||||
tmp = 9; //user input required
|
||||
//Check if the auto_update_reboot flag is set, and reboot as needed
|
||||
QJsonObject upset = sysadm::Update::readSettings();
|
||||
@@ -384,10 +384,12 @@ void EventWatcher::CheckSystemState(){
|
||||
QDateTime finished = sysadm::Update::rebootRequiredSince();
|
||||
QDateTime cdt = QDateTime::currentDateTime();
|
||||
if( (finished.addSecs(60*60*24)<cdt) || cdt.time().hour() == hour){ //more than 24 hours have passed, or time has come
|
||||
sysadm::SysMgmt::systemReboot();
|
||||
sysadm::Update::applyUpdates();
|
||||
}
|
||||
}
|
||||
}
|
||||
}else if(updates.value("status").toString()=="checkingforupdates"){
|
||||
//do nothing more here - still checking for updates
|
||||
}else if(updates.value("status").toString()!="updaterunning"){
|
||||
//updates are available - see if the auto-update flag is set, and start the updates as needed
|
||||
QJsonObject upset = sysadm::Update::readSettings();
|
||||
|
||||
@@ -18,7 +18,7 @@ void LogManager::checkLogDir(){
|
||||
QDir dir(logd);
|
||||
dir.mkpath(logd);
|
||||
}
|
||||
int daysold = CONFIG->value("prune_log_days_old",90).toInt(); //90 days by default
|
||||
int daysold = CONFIG->value("prune_log_days_old",30).toInt(); //90 days by default
|
||||
if(daysold>0){
|
||||
LogManager::pruneLogs(QDate::currentDate().addDays(0-daysold));
|
||||
}
|
||||
@@ -35,7 +35,7 @@ void LogManager::pruneLogs(QDate olderthan){
|
||||
for(int i=0; i<files.length(); i++){
|
||||
QDate fdate = QDate::fromString( files[i].section(".log",0,0).section("-",-3,-1), Qt::ISODate);
|
||||
//qDebug() << "Check File Date:" << fdate << olderthan;
|
||||
if( fdate < olderthan && fdate.isValid()){
|
||||
if( fdate < olderthan && fdate.isValid()){
|
||||
dir.remove(files[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "library/sysadm-firewall.h"
|
||||
#include "library/sysadm-moused.h"
|
||||
#include "library/sysadm-powerd.h"
|
||||
#include "library/sysadm-sourcectl.h"
|
||||
|
||||
#define DEBUG 0
|
||||
//#define SCLISTDELIM QString("::::") //SysCache List Delimiter
|
||||
@@ -41,11 +42,10 @@ RestOutputStruct::ExitCode WebSocket::AvailableSubsystems(bool allaccess, QJsonO
|
||||
out->insert("rpc/logs", allaccess ? "read/write" : "read");
|
||||
|
||||
// - beadm
|
||||
if(QFile::exists("/usr/local/sbin/beadm")){
|
||||
if(QFile::exists("/usr/local/sbin/beadm") || QFile::exists("/sbin/beadm") ){
|
||||
out->insert("sysadm/beadm", "read/write");
|
||||
}
|
||||
|
||||
|
||||
// - dispatcher (Internal to server - always available)
|
||||
//"read" is the event notifications, "write" is the ability to queue up jobs
|
||||
out->insert("rpc/dispatcher", allaccess ? "read/write" : "read");
|
||||
@@ -70,22 +70,22 @@ RestOutputStruct::ExitCode WebSocket::AvailableSubsystems(bool allaccess, QJsonO
|
||||
if(QFile::exists("/usr/local/sbin/iohyve")){
|
||||
out->insert("sysadm/iohyve", "read/write");
|
||||
}
|
||||
|
||||
|
||||
// - zfs
|
||||
if(QFile::exists("/sbin/zfs") && QFile::exists("/sbin/zpool")){
|
||||
out->insert("sysadm/zfs", allaccess ? "read/write" : "read");
|
||||
}
|
||||
|
||||
|
||||
// - pkg
|
||||
if(QFile::exists("/usr/local/sbin/pkg")){
|
||||
if(QFile::exists("/usr/local/sbin/pkg") || QFile::exists("/usr/sbin/pkg")){
|
||||
out->insert("sysadm/pkg", "read/write");
|
||||
}
|
||||
|
||||
|
||||
// - Generic system information
|
||||
out->insert("sysadm/systemmanager","read/write");
|
||||
|
||||
// - PC-BSD/TrueOS Updater
|
||||
if(QFile::exists("/usr/local/bin/pc-updatemanager")){
|
||||
// - Legacy PC-BSD/TrueOS Updater
|
||||
if( QFile::exists("/usr/local/bin/pc-updatemanager") ){
|
||||
out->insert("sysadm/update", "read/write");
|
||||
}
|
||||
|
||||
@@ -104,6 +104,10 @@ RestOutputStruct::ExitCode WebSocket::AvailableSubsystems(bool allaccess, QJsonO
|
||||
if(QFile::exists("/usr/sbin/powerd")){
|
||||
out->insert("sysadm/powerd", "read/write");
|
||||
}
|
||||
// - sourcectl
|
||||
if(QFile::exists("/usr/local/bin/git")){
|
||||
out->insert("sysadm/sourcectl", "read/write");
|
||||
}
|
||||
|
||||
return RestOutputStruct::OK;
|
||||
}
|
||||
@@ -164,6 +168,8 @@ RestOutputStruct::ExitCode WebSocket::EvaluateBackendRequest(const RestInputStru
|
||||
return EvaluateSysadmMousedRequest(IN.args, out);
|
||||
}else if(namesp=="sysadm" && name=="powerd"){
|
||||
return EvaluateSysadmPowerdRequest(IN.args, out);
|
||||
}else if(namesp=="sysadm" && name=="sourcectl"){
|
||||
return EvaluateSysadmSourceCTLRequest(IN.args, out);
|
||||
}else{
|
||||
return RestOutputStruct::BADREQUEST;
|
||||
}
|
||||
@@ -187,7 +193,7 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmSettingsRequest(const QJsonV
|
||||
pub_key = argsO.value("pub_key").toString();
|
||||
if(keys.contains("nickname")){ nickname = argsO.value("nickname").toString(); }
|
||||
if(keys.contains("email")){ email = argsO.value("email").toString(); }
|
||||
|
||||
|
||||
if(!pub_key.isEmpty()){
|
||||
ok = AUTHSYSTEM->RegisterCertificate(SockAuthToken, pub_key, nickname, email);
|
||||
if(!ok){ return RestOutputStruct::FORBIDDEN; }
|
||||
@@ -264,7 +270,7 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmLogsRequest(bool allaccess,
|
||||
else if(logs[i]=="events-dispatcher"){ log = 2; }
|
||||
else if(logs[i]=="events-lifepreserver"){ log = 3; }
|
||||
else if(logs[i]=="events-state"){ log = 4; }
|
||||
|
||||
|
||||
if(log>=0){
|
||||
QStringList info = LogManager::readLog( (LogManager::LOG_FILE)(log), starttime, endtime);
|
||||
//REMINDER of format: "[datetime]<message>"
|
||||
@@ -366,7 +372,7 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmBEADMRequest(const QJsonValu
|
||||
}else if(act=="umountbe"){
|
||||
ok = true;
|
||||
out->insert("umountbe", sysadm::BEADM::umountBE(in_args.toObject()));
|
||||
}
|
||||
}
|
||||
} //end of "action" key usage
|
||||
|
||||
//If nothing done - return the proper code
|
||||
@@ -409,9 +415,9 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmNetworkRequest(const QJsonVa
|
||||
bool ok = false;
|
||||
if(keys.contains("action")){
|
||||
QString act = JsonValueToString(in_args.toObject().value("action"));
|
||||
QStringList devs = sysadm::NetDevice::listNetDevices();
|
||||
if(act=="list-devices"){
|
||||
ok = true;
|
||||
QStringList devs = sysadm::NetDevice::listNetDevices();
|
||||
for(int i=0; i<devs.length(); i++){
|
||||
sysadm::NetDevice D(devs[i]);
|
||||
QJsonObject obj;
|
||||
@@ -428,6 +434,31 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmNetworkRequest(const QJsonVa
|
||||
//Add this device info to the main output structure
|
||||
out->insert(devs[i], obj);
|
||||
}
|
||||
|
||||
}else if(act=="list-settings"){
|
||||
ok = true;
|
||||
for(int i=0; i<devs.length(); i++){
|
||||
sysadm::NetDevSettings D = sysadm::Network::deviceRCSettings(devs[i]);
|
||||
if(D.device.isEmpty()){ continue; } //nothing for this device
|
||||
QJsonObject obj;
|
||||
//assemble the information about this device into an output object
|
||||
obj.insert("device", D.device);
|
||||
obj.insert("associated_device", D.asDevice);
|
||||
obj.insert("use_dhcp", D.useDHCP ? "true" : "false" );
|
||||
obj.insert("static_ipv4", D.staticIPv4);
|
||||
obj.insert("static_ipv6", D.staticIPv6);
|
||||
obj.insert("static_netmask", D.staticNetmask);
|
||||
obj.insert("static_gateway", D.staticGateway);
|
||||
if(D.wifihost){
|
||||
obj.insert("wifi_country", D.wifiCountry);
|
||||
obj.insert("wifi_ssid", D.wifiSSID);
|
||||
obj.insert("wifi_bssid", D.wifiBSSID);
|
||||
obj.insert("wifi_channel", D.wifiChannel);
|
||||
obj.insert("wifi_use_wpa", D.wifisecurity ? "true" : "false");
|
||||
}
|
||||
//Add this device info to the main output structure
|
||||
out->insert(devs[i], obj);
|
||||
}
|
||||
}
|
||||
|
||||
} //end of "action" key usage
|
||||
@@ -529,50 +560,58 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmSystemMgmtRequest(const QJso
|
||||
ok = true;
|
||||
out->insert("batteryinfo", sysadm::SysMgmt::batteryInfo());
|
||||
}
|
||||
if(act=="cpupercentage"){
|
||||
else if(act=="cpupercentage"){
|
||||
ok = true;
|
||||
out->insert("cpupercentage", sysadm::SysMgmt::cpuPercentage());
|
||||
}
|
||||
if(act=="cputemps"){
|
||||
else if(act=="cputemps"){
|
||||
ok = true;
|
||||
out->insert("cputemps", sysadm::SysMgmt::cpuTemps());
|
||||
}
|
||||
if(act=="externalmounts"){
|
||||
else if(act=="externalmounts"){
|
||||
ok = true;
|
||||
out->insert("externalmounts", sysadm::SysMgmt::externalDevicePaths());
|
||||
}
|
||||
if(act=="halt"){
|
||||
else if(act=="halt"){
|
||||
ok = true;
|
||||
out->insert("halt", sysadm::SysMgmt::systemHalt());
|
||||
}
|
||||
if(act=="killproc"){
|
||||
else if(act=="killproc"){
|
||||
ok = true;
|
||||
out->insert("killproc", sysadm::SysMgmt::killProc(in_args.toObject()));
|
||||
}
|
||||
if(act=="memorystats"){
|
||||
else if(act=="memorystats"){
|
||||
ok = true;
|
||||
out->insert("memorystats", sysadm::SysMgmt::memoryStats());
|
||||
}
|
||||
if(act=="procinfo"){
|
||||
else if(act=="procinfo"){
|
||||
ok = true;
|
||||
out->insert("procinfo", sysadm::SysMgmt::procInfo());
|
||||
}
|
||||
if(act=="reboot"){
|
||||
else if(act=="reboot"){
|
||||
ok = true;
|
||||
out->insert("reboot", sysadm::SysMgmt::systemReboot());
|
||||
}
|
||||
if(act=="setsysctl"){
|
||||
else if(act=="getsysctl"){
|
||||
ok = true;
|
||||
out->insert("getsysctl", sysadm::SysMgmt::getSysctl(in_args.toObject()));
|
||||
}
|
||||
else if(act=="setsysctl"){
|
||||
ok = true;
|
||||
out->insert("setsysctl", sysadm::SysMgmt::setSysctl(in_args.toObject()));
|
||||
}
|
||||
if(act=="sysctllist"){
|
||||
else if(act=="sysctllist"){
|
||||
ok = true;
|
||||
out->insert("sysctllist", sysadm::SysMgmt::sysctlList());
|
||||
}
|
||||
if(act=="systeminfo"){
|
||||
else if(act=="systeminfo"){
|
||||
ok = true;
|
||||
out->insert("systeminfo", sysadm::SysMgmt::systemInfo());
|
||||
}
|
||||
else if(act=="deviceinfo"){
|
||||
ok = true;
|
||||
out->insert("deviceinfo", sysadm::SysMgmt::systemDevices());
|
||||
}
|
||||
|
||||
} //end of "action" key usage
|
||||
|
||||
@@ -598,11 +637,11 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmUpdateRequest(const QJsonVal
|
||||
bool fastcheck = true;
|
||||
fastcheck = in_args.toObject().value("force").toString().toLower()!="true";
|
||||
out->insert("checkupdates", sysadm::Update::checkUpdates(fastcheck));
|
||||
|
||||
|
||||
}else if(act=="listbranches"){
|
||||
ok = true;
|
||||
out->insert("listbranches", sysadm::Update::listBranches());
|
||||
|
||||
|
||||
}else if(act=="startupdate"){
|
||||
ok = true;
|
||||
out->insert("startupdate", sysadm::Update::startUpdate(in_args.toObject()) );
|
||||
@@ -611,6 +650,10 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmUpdateRequest(const QJsonVal
|
||||
ok = true;
|
||||
out->insert("stopupdate", sysadm::Update::stopUpdate() );
|
||||
|
||||
}else if(act=="applyupdate"){
|
||||
ok = true;
|
||||
out->insert("applyupdate", sysadm::Update::applyUpdates() );
|
||||
|
||||
}else if(act=="listsettings"){
|
||||
ok = true;
|
||||
out->insert("listsettings", sysadm::Update::readSettings() );
|
||||
@@ -650,7 +693,7 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmIocageRequest(const QJsonVal
|
||||
QJsonObject retObj;
|
||||
if(act=="activatepool"){ retObj = sysadm::Iocage::activatePool(in_args.toObject()); }
|
||||
else if(act=="deactivatepool"){retObj = sysadm::Iocage::deactivatePool(in_args.toObject()); }
|
||||
|
||||
else if(act=="activatestatus"){ retObj = sysadm::Iocage::activateStatus(); }
|
||||
/*if(act=="execjail"){
|
||||
ok = true;
|
||||
out->insert("execjail", sysadm::Iocage::execJail(in_args.toObject()));
|
||||
@@ -675,14 +718,6 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmIocageRequest(const QJsonVal
|
||||
ok = true;
|
||||
out->insert("cleanall", sysadm::Iocage::cleanAll());
|
||||
}
|
||||
else if(act=="cleantemplates"){
|
||||
ok = true;
|
||||
out->insert("cleantemplates", sysadm::Iocage::cleanTemplates());
|
||||
}
|
||||
else if(act=="cleanreleases"){
|
||||
ok = true;
|
||||
out->insert("cleanreleases", sysadm::Iocage::cleanReleases());
|
||||
}
|
||||
else if(act=="cleanjails"){
|
||||
ok = true;
|
||||
out->insert("cleanjails", sysadm::Iocage::cleanJails());
|
||||
@@ -702,19 +737,21 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmIocageRequest(const QJsonVal
|
||||
else if(act=="getjailsettings"){
|
||||
ok = true;
|
||||
out->insert("getjailsettings", sysadm::Iocage::getJailSettings(in_args.toObject()));
|
||||
}
|
||||
else if(act=="listjails"){
|
||||
ok = true;
|
||||
out->insert("listjails", sysadm::Iocage::listJails());
|
||||
}
|
||||
else if(act=="listtemplates"){
|
||||
ok = true;
|
||||
out->insert("listtemplates", sysadm::Iocage::listTemplates());
|
||||
}
|
||||
else if(act=="listreleases"){
|
||||
ok = true;
|
||||
out->insert("listjails", sysadm::Iocage::listReleases());
|
||||
}*/
|
||||
|
||||
//JAILS (GENERIC)
|
||||
else if(act=="listjails"){ retObj = sysadm::Iocage::listJails(); }
|
||||
//TEMPLATES
|
||||
else if(act=="listtemplates"){ retObj = sysadm::Iocage::listTemplates(); }
|
||||
else if(act=="cleantemplates"){ retObj = sysadm::Iocage::cleanTemplates(); }
|
||||
//RELEASES
|
||||
else if(act=="listreleases"){ retObj = sysadm::Iocage::listReleases(); }
|
||||
else if(act=="fetchreleases"){ retObj = sysadm::Iocage::fetchReleases(in_args.toObject()); }
|
||||
else if(act=="cleanreleases"){ retObj = sysadm::Iocage::cleanReleases(); }
|
||||
//PLUGINS
|
||||
else if(act=="listplugins"){ retObj = sysadm::Iocage::listPlugins(); }
|
||||
else if(act=="createplugin"){ retObj = sysadm::Iocage::fetchPlugin(in_args.toObject()); }
|
||||
|
||||
ok = !retObj.keys().isEmpty();
|
||||
if(ok){ out->insert(act,retObj); }
|
||||
} //end of "action" key usage
|
||||
@@ -863,20 +900,20 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmPkgRequest(const QJsonValue
|
||||
if(in_args.toObject().value("pkg_origins").isString()){ pkgs << in_args.toObject().value("pkg_origins").toString(); }
|
||||
else if(in_args.toObject().value("pkg_origins").isArray()){ pkgs = JsonArrayToStringList(in_args.toObject().value("pkg_origins").toArray()); }
|
||||
}
|
||||
|
||||
|
||||
//Parse the action and perform accordingly
|
||||
if(act=="pkg_info"){
|
||||
//OPTIONAL: "pkg_origins" OR "category"
|
||||
//OPTIONAL: "repo"
|
||||
//OPTIONAL: "result" = "full" or "simple" (Default: "simple")
|
||||
bool fullresults = false;
|
||||
bool fullresults = false;
|
||||
if(in_args.toObject().contains("result")){ fullresults = (in_args.toObject().value("result").toString()=="full"); }
|
||||
|
||||
|
||||
//Now run the info fetch routine
|
||||
QJsonObject info = sysadm::PKG::pkg_info(pkgs, repo, cat, fullresults);
|
||||
if(!info.isEmpty()){ out->insert("pkg_info",info); }
|
||||
else{ return RestOutputStruct::NOCONTENT; }
|
||||
|
||||
|
||||
}else if(act=="pkg_search" && in_args.toObject().contains("search_term")){
|
||||
//REQUIRED: "search_term" (string to search for)
|
||||
//OPTIONAL: "repo"
|
||||
@@ -897,22 +934,26 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmPkgRequest(const QJsonValue
|
||||
}else{
|
||||
return RestOutputStruct::NOCONTENT;
|
||||
}
|
||||
|
||||
|
||||
}else if(act=="list_categories"){
|
||||
//OPTIONAL: "repo"
|
||||
QJsonArray cats = sysadm::PKG::list_categories(repo);
|
||||
if(!cats.isEmpty()){ out->insert("list_categories", cats); }
|
||||
else{ return RestOutputStruct::NOCONTENT; }
|
||||
|
||||
|
||||
}else if(act=="list_repos"){
|
||||
QJsonArray repos = sysadm::PKG::list_repos();
|
||||
if(!repos.isEmpty()){ out->insert("list_repos", repos); }
|
||||
else{ return RestOutputStruct::NOCONTENT; }
|
||||
|
||||
|
||||
}else if(act=="pkg_install" && !pkgs.isEmpty() ){
|
||||
//REQUIRED: "pkg_origins"
|
||||
//OPTIONAL: "repo" (pkg will determine the best repo to use if not supplied)
|
||||
out->insert("pkg_install", sysadm::PKG::pkg_install(pkgs,repo));
|
||||
|
||||
}else if(act=="pkg_install_verify"){
|
||||
//REQUIRED: "pkg_origins", "repo"
|
||||
out->insert("pkg_install_verify", sysadm::PKG::evaluateInstall(pkgs,repo) );
|
||||
}else if(act=="pkg_remove" && !pkgs.isEmpty() ){
|
||||
//REQUIRED: "pkg_origins"
|
||||
//OPTIONAL: "recursive"="true" or "false" (default: "true")
|
||||
@@ -921,10 +962,10 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmPkgRequest(const QJsonValue
|
||||
out->insert("pkg_remove", sysadm::PKG::pkg_remove(pkgs, recursive));
|
||||
}else if(act=="pkg_lock" && !pkgs.isEmpty() ){
|
||||
//REQUIRED: "pkg_origins"
|
||||
out->insert("pkg_lock", sysadm::PKG::pkg_lock(pkgs));
|
||||
out->insert("pkg_lock", sysadm::PKG::pkg_lock(pkgs));
|
||||
}else if(act=="pkg_unlock" && !pkgs.isEmpty() ){
|
||||
//REQUIRED: "pkg_origins"
|
||||
out->insert("pkg_unlock", sysadm::PKG::pkg_unlock(pkgs));
|
||||
out->insert("pkg_unlock", sysadm::PKG::pkg_unlock(pkgs));
|
||||
}else if(act=="pkg_update"){
|
||||
//OPTIONAL: "force" = ["true"/"false"] (default: "false")
|
||||
bool force = false;
|
||||
@@ -942,7 +983,7 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmPkgRequest(const QJsonValue
|
||||
//unknown action
|
||||
return RestOutputStruct::BADREQUEST;
|
||||
}
|
||||
|
||||
|
||||
return RestOutputStruct::OK;
|
||||
}
|
||||
|
||||
@@ -1245,6 +1286,12 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmMousedRequest(const QJsonVal
|
||||
outobj = sysadm::moused::enableDevice(in_args.toObject());
|
||||
}else if(action == "set_device_inactive"){
|
||||
outobj = sysadm::moused::disableDevice(in_args.toObject());
|
||||
}else if(action == "get_tap_to_click"){
|
||||
outobj = sysadm::moused::tapToClick();
|
||||
}else if(action == "set_tap_to_click"){
|
||||
outobj = sysadm::moused::setTapToClick(in_args.toObject());
|
||||
}else if(action == "get_synaptics_options"){
|
||||
outobj = sysadm::moused::synapticsSettings();
|
||||
}
|
||||
|
||||
//check return structure for validity
|
||||
@@ -1286,3 +1333,33 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmPowerdRequest(const QJsonVal
|
||||
return RestOutputStruct::BADREQUEST;
|
||||
}
|
||||
}
|
||||
|
||||
// ==== SYSADM SOURCECTL API ====
|
||||
RestOutputStruct::ExitCode WebSocket::EvaluateSysadmSourceCTLRequest(const QJsonValue in_args, QJsonObject *out){
|
||||
QString action = in_args.toObject().value("action").toString();
|
||||
QJsonObject outobj;
|
||||
if(action == "downloadports"){
|
||||
outobj = sysadm::sourcectl::downloadports();
|
||||
}else if(action == "updateports"){
|
||||
outobj = sysadm::sourcectl::updateports();
|
||||
}else if(action == "deleteports"){
|
||||
outobj = sysadm::sourcectl::deleteports();
|
||||
}else if(action == "stopports"){
|
||||
outobj = sysadm::sourcectl::stopports();
|
||||
}else if(action == "downloadsource"){
|
||||
outobj = sysadm::sourcectl::downloadsource();
|
||||
}else if(action == "updatesource"){
|
||||
outobj = sysadm::sourcectl::updatesource();
|
||||
}else if(action == "deletesource"){
|
||||
outobj = sysadm::sourcectl::deletesource();
|
||||
}else if(action == "stopsource"){
|
||||
outobj = sysadm::sourcectl::stopsource();
|
||||
//check return structure for validity
|
||||
if(!outobj.keys().isEmpty()){
|
||||
out->insert(action, outobj);
|
||||
return RestOutputStruct::OK;
|
||||
}else{
|
||||
return RestOutputStruct::BADREQUEST;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,7 +255,7 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){
|
||||
QString user, pass;
|
||||
if(out.in_struct.args.toObject().contains("username")){ user = JsonValueToString(out.in_struct.args.toObject().value("username")); }
|
||||
if(out.in_struct.args.toObject().contains("password")){ pass = JsonValueToString(out.in_struct.args.toObject().value("password")); }
|
||||
|
||||
|
||||
//Use the given password
|
||||
cur_auth_tok = AUTHSYSTEM->LoginUP(host, user, pass);
|
||||
}else if(out.in_struct.name=="auth_ssl"){
|
||||
@@ -294,7 +294,7 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){
|
||||
out.CODE = RestOutputStruct::OK;
|
||||
QString msg = out.assembleMessage();
|
||||
if(SOCKET!=0 && !REQ.bridgeID.isEmpty()){
|
||||
//BRIDGE RELAY - alternate format
|
||||
//BRIDGE RELAY - alternate format
|
||||
//Note that the Stage 1 SSL auth reply is only partially encrypted (specific variables only, not bulk message encryption)
|
||||
//Now add the destination ID
|
||||
msg.prepend( REQ.bridgeID+"\n");
|
||||
@@ -307,10 +307,10 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){
|
||||
}else if(out.in_struct.name == "auth_clear"){
|
||||
return; //don't send a return message after clearing an auth (already done)
|
||||
}
|
||||
|
||||
|
||||
//Now check the auth and respond appropriately
|
||||
if(AUTHSYSTEM->checkAuth(cur_auth_tok)){
|
||||
//Good Authentication - return the new token
|
||||
//Good Authentication - return the new token
|
||||
QJsonArray array;
|
||||
array.append(cur_auth_tok);
|
||||
array.append(AUTHSYSTEM->checkAuthTimeoutSecs(cur_auth_tok));
|
||||
@@ -325,25 +325,25 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){
|
||||
//Bad Authentication - return error
|
||||
out.CODE = RestOutputStruct::UNAUTHORIZED;
|
||||
}
|
||||
|
||||
}else if( AUTHSYSTEM->checkAuth(cur_auth_tok) ){ //validate current Authentication token
|
||||
|
||||
}else if( AUTHSYSTEM->checkAuth(cur_auth_tok) ){ //validate current Authentication token
|
||||
//Now provide access to the various subsystems
|
||||
// First get/set the permissions flag into the input structure
|
||||
out.in_struct.fullaccess = AUTHSYSTEM->hasFullAccess(cur_auth_tok);
|
||||
//Pre-set any output fields
|
||||
QJsonObject outargs;
|
||||
QJsonObject outargs;
|
||||
out.CODE = EvaluateBackendRequest(out.in_struct, &outargs);
|
||||
out.out_args = outargs;
|
||||
out.out_args = outargs;
|
||||
}else{
|
||||
//Bad/No authentication
|
||||
out.CODE = RestOutputStruct::UNAUTHORIZED;
|
||||
}
|
||||
|
||||
|
||||
}else if(out.in_struct.namesp.toLower() == "events"){
|
||||
//qDebug() << "Got Event subsytem request" << out.in_struct.args;
|
||||
if( AUTHSYSTEM->checkAuth(cur_auth_tok) ){ //validate current Authentication token
|
||||
if( AUTHSYSTEM->checkAuth(cur_auth_tok) ){ //validate current Authentication token
|
||||
//Pre-set any output fields
|
||||
QJsonObject outargs;
|
||||
QJsonObject outargs;
|
||||
//Assemble the list of input events
|
||||
QStringList evlist;
|
||||
if(out.in_struct.args.isString()){ evlist << JsonValueToString(out.in_struct.args); }
|
||||
@@ -357,14 +357,14 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){
|
||||
for(int i=0; i<evlist.length(); i++){
|
||||
EventWatcher::EVENT_TYPE type = EventWatcher::typeFromString(evlist[i]);
|
||||
//qDebug() << " - type:" << type;
|
||||
if(isBridge){
|
||||
if(isBridge){
|
||||
ForwardEvents.clear();
|
||||
if(!REQ.bridgeID.isEmpty()){ ForwardEvents = BRIDGE[REQ.bridgeID].sendEvents; }
|
||||
}
|
||||
if(type==EventWatcher::BADEVENT){ continue; }
|
||||
outargs.insert(out.in_struct.name,QJsonValue(evlist[i]));
|
||||
if(sub==1){
|
||||
ForwardEvents << type;
|
||||
if(sub==1){
|
||||
ForwardEvents << type;
|
||||
EventUpdate(type);
|
||||
}else{
|
||||
ForwardEvents.removeAll(type);
|
||||
@@ -375,7 +375,7 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){
|
||||
out.CODE = RestOutputStruct::OK;
|
||||
}else{
|
||||
//Bad/No authentication
|
||||
out.CODE = RestOutputStruct::BADREQUEST;
|
||||
out.CODE = RestOutputStruct::BADREQUEST;
|
||||
}
|
||||
}else{
|
||||
//Bad/No authentication
|
||||
@@ -386,7 +386,7 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){
|
||||
//qDebug() << "Within special bridge section";
|
||||
out.in_struct.fullaccess = false;
|
||||
//Pre-set any output fields
|
||||
QJsonObject outargs;
|
||||
QJsonObject outargs;
|
||||
out.CODE = EvaluateBackendRequest(out.in_struct, &outargs);
|
||||
out.out_args = outargs;
|
||||
}else if( AUTHSYSTEM->checkAuth(cur_auth_tok) ){ //validate current Authentication token
|
||||
@@ -395,7 +395,7 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){
|
||||
// First get/set the permissions flag into the input structure
|
||||
out.in_struct.fullaccess = AUTHSYSTEM->hasFullAccess(cur_auth_tok);
|
||||
//Pre-set any output fields
|
||||
QJsonObject outargs;
|
||||
QJsonObject outargs;
|
||||
out.CODE = EvaluateBackendRequest(out.in_struct, &outargs);
|
||||
out.out_args = outargs;
|
||||
}else{
|
||||
@@ -413,7 +413,7 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){
|
||||
QString msg = out.assembleMessage();
|
||||
if(SOCKET!=0 && !REQ.bridgeID.isEmpty()){
|
||||
//BRIDGE RELAY - alternate format
|
||||
msg = AUTHSYSTEM->encryptString(msg, BRIDGE[REQ.bridgeID].enc_key);
|
||||
msg = AUTHSYSTEM->encryptString(msg, BRIDGE[REQ.bridgeID].enc_key);
|
||||
//Now add the destination ID
|
||||
msg.prepend( REQ.bridgeID+"\n");
|
||||
}
|
||||
@@ -421,7 +421,7 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){
|
||||
this->sendReply(msg);
|
||||
SOCKET->close(QWebSocketProtocol::CloseCodeNormal, "Too Many Authorization Failures - Try again later");
|
||||
}else{
|
||||
this->emit SendMessage(msg);
|
||||
this->emit SendMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -489,7 +489,7 @@ QStringList WebSocket::JsonArrayToStringList(QJsonArray array){
|
||||
for(int i=0; i<array.count(); i++){
|
||||
out << JsonValueToString(array.at(i));
|
||||
}
|
||||
return out;
|
||||
return out;
|
||||
}
|
||||
|
||||
// =====================
|
||||
@@ -532,10 +532,10 @@ void WebSocket::checkAuth(){
|
||||
|
||||
void WebSocket::SocketClosing(){
|
||||
LogManager::log(LogManager::HOST,"Connection Closing: "+SockPeerIP);
|
||||
if(idletimer->isActive()){
|
||||
if(idletimer->isActive()){
|
||||
//This means the client deliberately closed the connection - not the idle timer
|
||||
//qDebug() << " - Client Closed Connection";
|
||||
idletimer->stop();
|
||||
idletimer->stop();
|
||||
}else{
|
||||
//qDebug() << "idleTimer not running";
|
||||
}
|
||||
@@ -544,23 +544,23 @@ void WebSocket::SocketClosing(){
|
||||
//Reset the pointer
|
||||
if(SOCKET!=0){ SOCKET = 0; }
|
||||
if(TSOCKET!=0){ TSOCKET = 0; }
|
||||
|
||||
|
||||
emit SocketClosed(SockID);
|
||||
}
|
||||
|
||||
void WebSocket::EvaluateMessage(const QByteArray &msg){
|
||||
//qDebug() << "New Binary Message:";
|
||||
if(idletimer->isActive()){ idletimer->stop(); }
|
||||
idletimer->start();
|
||||
idletimer->start();
|
||||
EvaluateREST( QString(msg) );
|
||||
//qDebug() << " - Done with Binary Message";
|
||||
}
|
||||
|
||||
void WebSocket::EvaluateMessage(const QString &msg){
|
||||
void WebSocket::EvaluateMessage(const QString &msg){
|
||||
//qDebug() << "New Text Message:" << msg;
|
||||
if(idletimer->isActive()){ idletimer->stop(); }
|
||||
idletimer->start();
|
||||
EvaluateREST(msg);
|
||||
idletimer->start();
|
||||
EvaluateREST(msg);
|
||||
//qDebug() << " - Done with Text Message";
|
||||
}
|
||||
|
||||
@@ -609,8 +609,8 @@ void WebSocket::EvaluateTcpMessage(){
|
||||
// Check for JSON in this incoming data
|
||||
ParseIncoming();
|
||||
|
||||
idletimer->start();
|
||||
//qDebug() << " - Done with TCP Message";
|
||||
idletimer->start();
|
||||
//qDebug() << " - Done with TCP Message";
|
||||
}
|
||||
|
||||
//SSL signal handling
|
||||
|
||||
@@ -100,6 +100,8 @@ private:
|
||||
RestOutputStruct::ExitCode EvaluateSysadmMousedRequest(const QJsonValue in_args, QJsonObject *out);
|
||||
// -- sysadm powerd API
|
||||
RestOutputStruct::ExitCode EvaluateSysadmPowerdRequest(const QJsonValue in_args, QJsonObject *out);
|
||||
// -- sysadm sourcectl API
|
||||
RestOutputStruct::ExitCode EvaluateSysadmSourceCTLRequest(const QJsonValue in_args, QJsonObject *out);
|
||||
|
||||
private slots:
|
||||
void sendReply(QString msg);
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
// Available under the 3-clause BSD license
|
||||
// See the LICENSE file for full details
|
||||
//===========================================
|
||||
// SysAdm source code for TrueOS
|
||||
// Copyright (c) 2016-2017 TrueOS/iXsystems
|
||||
// Available under the 3-clause BSD license
|
||||
// See the LICENSE file for full details
|
||||
//===========================================
|
||||
// Note: This was almost entirely written by Tim McCormick in 2009 for
|
||||
// the first PC-BSD library, and copied here by Ken Moore in 2015
|
||||
//===========================================
|
||||
@@ -15,7 +20,7 @@
|
||||
using namespace sysadm;
|
||||
|
||||
//====================
|
||||
// STATIC LISTING FUNCTION
|
||||
// STATIC LISTING FUNCTION
|
||||
//====================
|
||||
QStringList NetDevice::listNetDevices(){
|
||||
QStringList result;
|
||||
@@ -28,7 +33,7 @@ QStringList NetDevice::listNetDevices(){
|
||||
if (result.contains(ifName) == 0) result += ifName;
|
||||
ifap = ifap->ifa_next;
|
||||
}
|
||||
//Close the
|
||||
//Close the structure
|
||||
freeifaddrs(ifap);
|
||||
return result;
|
||||
}
|
||||
@@ -57,10 +62,10 @@ QString NetDevice::ipAsString(){
|
||||
|
||||
strncpy(ifr.ifr_name, name.toLocal8Bit(), IFNAMSIZ);
|
||||
int s = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
|
||||
|
||||
ioctl(s, SIOCGIFADDR, &ifr);
|
||||
struct in_addr in = ((sockaddr_in *) &ifr.ifr_addr)->sin_addr;
|
||||
|
||||
close(s); //close the file descriptor
|
||||
return QString(inet_ntoa(in));
|
||||
}
|
||||
|
||||
@@ -86,7 +91,7 @@ QString NetDevice::ipv6AsString(){
|
||||
//Now get the IPv6 address in string form
|
||||
char straddr[INET6_ADDRSTRLEN];
|
||||
int err = getnameinfo(sadd, sadd->sa_len, straddr, sizeof(straddr),NULL, 0, NI_NUMERICHOST);
|
||||
if(err!=0){
|
||||
if(err!=0){
|
||||
qDebug() << "getnameinfo error:" << gai_strerror(err);
|
||||
return "";
|
||||
}else{
|
||||
@@ -102,16 +107,28 @@ QString NetDevice::netmaskAsString(){
|
||||
|
||||
strncpy(ifr.ifr_name, name.toLocal8Bit(), IFNAMSIZ);
|
||||
int s = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
|
||||
|
||||
ioctl(s, SIOCGIFNETMASK, &ifr);
|
||||
struct in_addr in = ((sockaddr_in *) &ifr.ifr_addr)->sin_addr;
|
||||
|
||||
close(s); //close the file descriptor
|
||||
return QString(inet_ntoa(in));
|
||||
}
|
||||
|
||||
//Returns the description string for the device
|
||||
QString NetDevice::desc(){
|
||||
return General::sysctl("dev." + devName() + "." + QString::number(devNum()) + ".%desc");
|
||||
QString name, num, parent;
|
||||
if( isWireless() ){ parent = getWifiParent(); }
|
||||
|
||||
if(!parent.isEmpty()){
|
||||
name = num = parent;
|
||||
uint pos = name.indexOf(QRegExp("[0-9]+$"));
|
||||
name.truncate(pos);
|
||||
num.remove(0,pos);
|
||||
}else{
|
||||
name = devName();
|
||||
num = QString::number(devNum());
|
||||
}
|
||||
return General::sysctl("dev." + name + "." + num + ".%desc");
|
||||
}
|
||||
|
||||
//Fetch the mac address as a QString
|
||||
@@ -136,7 +153,7 @@ QString NetDevice::macAsString(){
|
||||
|
||||
sdl = (sockaddr_dl *)(((if_msghdr *)buf)+1);
|
||||
ptr = (char *) LLADDR(sdl);
|
||||
|
||||
|
||||
QString mac;
|
||||
for (uint i=0; i < 6; i++){
|
||||
mac += QString::number(*(ptr+i), 16).right(2).rightJustified(2, '0');
|
||||
@@ -157,11 +174,6 @@ QString NetDevice::mediaStatusAsString(){
|
||||
QString status;
|
||||
|
||||
switch (IFM_TYPE(ifm.ifm_active)){
|
||||
case IFM_FDDI:
|
||||
case IFM_TOKEN:
|
||||
if (ifm.ifm_status & IFM_ACTIVE) status = "inserted";
|
||||
else status = "no ring";
|
||||
break;
|
||||
case IFM_IEEE80211:
|
||||
if (ifm.ifm_status & IFM_ACTIVE) status = "associated";
|
||||
else status = "no carrier";
|
||||
@@ -170,6 +182,7 @@ QString NetDevice::mediaStatusAsString(){
|
||||
if (ifm.ifm_status & IFM_ACTIVE) status = "active";
|
||||
else status = "no carrier";
|
||||
}
|
||||
close(s); //close the file descriptor
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -190,8 +203,9 @@ bool NetDevice::isWireless(){
|
||||
int s = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
|
||||
ioctl(s, SIOCGIFMEDIA, &ifm);
|
||||
|
||||
return IFM_TYPE(ifm.ifm_active) == IFM_IEEE80211;
|
||||
bool iswifi = (IFM_TYPE(ifm.ifm_active) == IFM_IEEE80211);
|
||||
close(s); //close the file descriptor
|
||||
return iswifi;
|
||||
}
|
||||
|
||||
//Get the parent device (if this is a wireless wlan)
|
||||
@@ -203,7 +217,7 @@ QString NetDevice::getWifiParent(){
|
||||
//See if the device is setup to use DHCP
|
||||
bool NetDevice::usesDHCP(){
|
||||
//The system does not keep track of how the device's address was assigned
|
||||
// so the closest we can get to this is to see if the system is setup to use
|
||||
// so the closest we can get to this is to see if the system is setup to use
|
||||
// DHCP on startup (in /etc/rc.conf) (Ken Moore - 6/24/15)
|
||||
return !Network::readRcConf().filter(name).filter("DHCP").isEmpty();
|
||||
}
|
||||
@@ -217,8 +231,9 @@ bool NetDevice::isUp(){
|
||||
int s = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
|
||||
ioctl(s, SIOCGIFFLAGS, &ifr);
|
||||
|
||||
return (ifr.ifr_flags & IFF_UP) ? 1 : 0;
|
||||
bool isup = (ifr.ifr_flags & IFF_UP);
|
||||
close(s); //close the file descriptor
|
||||
return isup;
|
||||
}
|
||||
|
||||
//Determine the number of packets received by the device
|
||||
|
||||
@@ -17,7 +17,9 @@ HEADERS += $${PWD}/sysadm-global.h \
|
||||
$${PWD}/sysadm-zfs.h \
|
||||
$${PWD}/sysadm-pkg.h \
|
||||
$${PWD}/sysadm-moused.h \
|
||||
$${PWD}/sysadm-powerd.h
|
||||
$${PWD}/sysadm-powerd.h \
|
||||
$${PWD}/sysadm-sourcectl.h
|
||||
|
||||
|
||||
SOURCES += $${PWD}/NetDevice.cpp \
|
||||
$${PWD}/sysadm-general.cpp \
|
||||
@@ -35,4 +37,5 @@ SOURCES += $${PWD}/NetDevice.cpp \
|
||||
$${PWD}/sysadm-zfs.cpp \
|
||||
$${PWD}/sysadm-pkg.cpp \
|
||||
$${PWD}/sysadm-moused.cpp \
|
||||
$${PWD}/sysadm-powerd.cpp
|
||||
$${PWD}/sysadm-powerd.cpp \
|
||||
$${PWD}/sysadm-sourcectl.cpp
|
||||
|
||||
@@ -52,7 +52,7 @@ QJsonObject BEADM::listBEs() {
|
||||
|
||||
QJsonObject BEADM::renameBE(QJsonObject jsin) {
|
||||
QJsonObject retObject;
|
||||
|
||||
|
||||
QStringList keys = jsin.keys();
|
||||
if (! keys.contains("source") || ! keys.contains("target") ) {
|
||||
retObject.insert("error", "Missing required key(s) 'source / target'");
|
||||
@@ -71,7 +71,7 @@ QJsonObject BEADM::listBEs() {
|
||||
return retObject;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
retObject.insert("source", source);
|
||||
retObject.insert("target", target);
|
||||
return retObject;
|
||||
@@ -191,7 +191,7 @@ QJsonObject BEADM::listBEs() {
|
||||
QString mountpoint;
|
||||
if (keys.contains("mountpoint") ) {
|
||||
mountpoint = jsin.value("mountpoint").toString();
|
||||
}
|
||||
}
|
||||
|
||||
QStringList output = General::RunCommand("beadm mount "+ be + " " + mountpoint).split("\n");
|
||||
|
||||
@@ -209,7 +209,7 @@ QJsonObject BEADM::listBEs() {
|
||||
|
||||
return retObject;
|
||||
}
|
||||
|
||||
|
||||
// Unmount the given boot environment immediately. Confirmation should be done through the client.
|
||||
|
||||
QJsonObject BEADM::umountBE(QJsonObject jsin) {
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
#include "sysadm-global.h"
|
||||
|
||||
using namespace sysadm;
|
||||
|
||||
#define PREFIX QString("/usr/local")
|
||||
QString TRUEOS_ETCCONF(PREFIX + "/etc/trueos.conf"); // The default trueos.conf file
|
||||
|
||||
//=================
|
||||
// RunCommand() variations
|
||||
//=================
|
||||
@@ -36,7 +40,10 @@ QString General::RunCommand(bool &success, QString command, QStringList argument
|
||||
if(arguments.isEmpty()){ proc.start(command); }
|
||||
else{ proc.start(command, arguments); }
|
||||
//Wait for the process to finish (but don't block the event loop)
|
||||
while( !proc.waitForFinished(500) ){ QCoreApplication::processEvents(); }
|
||||
while( !proc.waitForFinished(500) ){
|
||||
if(proc.state() != QProcess::Running){ break; } //somehow missed the finished signal
|
||||
QCoreApplication::processEvents();
|
||||
}
|
||||
success = (proc.exitCode()==0); //return success/failure
|
||||
return QString(proc.readAllStandardOutput());
|
||||
}
|
||||
@@ -54,6 +61,21 @@ bool General::RunQuickCommand(QString command, QStringList arguments,QString wor
|
||||
return success;
|
||||
}
|
||||
|
||||
QStringList General::gitCMD(QString dir, QString cmd, QStringList args){
|
||||
//Run a quick command in the proper dir and return the output
|
||||
QProcess proc;
|
||||
proc.setProcessChannelMode(QProcess::MergedChannels);
|
||||
if( !dir.isEmpty() && QFile::exists(dir) ){ proc.setWorkingDirectory(dir); }
|
||||
if(args.isEmpty()){ proc.start(cmd); }
|
||||
else{ proc.start(cmd, args); }
|
||||
while(!proc.waitForFinished(300)){ QCoreApplication::processEvents(); }
|
||||
QStringList out;
|
||||
while(proc.canReadLine()){
|
||||
out << QString( proc.readLine() );
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
//=================
|
||||
// TEXT FILE INTERACTION
|
||||
//=================
|
||||
@@ -170,12 +192,10 @@ bool General::setConfFileValue(QString fileName, QString oldKey, QString newKey,
|
||||
|
||||
// Load the old file, find the oldKey, remove it and replace with newKey
|
||||
QFile file( oFileTmp );
|
||||
if ( ! file.open( QIODevice::ReadOnly ) )
|
||||
return false;
|
||||
|
||||
QTextStream stream( &file );
|
||||
QString line;
|
||||
while ( !stream.atEnd() ) {
|
||||
if ( file.open( QIODevice::ReadOnly ) ){
|
||||
QTextStream stream( &file );
|
||||
QString line;
|
||||
while ( !stream.atEnd() ) {
|
||||
line = stream.readLine(); // line of text excluding '\n'
|
||||
|
||||
// Key is not found at all
|
||||
@@ -214,10 +234,12 @@ bool General::setConfFileValue(QString fileName, QString oldKey, QString newKey,
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
file.close();
|
||||
}else if(file.exists()){
|
||||
return false; //could not read an existing file - permissions issue?
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
// Didn't find the key? Write it!
|
||||
if ( ! newKey.isEmpty() )
|
||||
SavedFile << newKey;
|
||||
@@ -241,6 +263,30 @@ bool General::setConfFileValue(QString fileName, QString oldKey, QString newKey,
|
||||
return true;
|
||||
}
|
||||
|
||||
QString General::getValFromTrueOSConf(QString key) {
|
||||
return getValFromTOConf(TRUEOS_ETCCONF, key);
|
||||
}
|
||||
|
||||
QString General::getValFromTOConf(QString conf, QString key) {
|
||||
|
||||
// Load from conf the requested key
|
||||
QFile confFile(conf);
|
||||
if ( confFile.open( QIODevice::ReadOnly ) ) {
|
||||
QTextStream stream( &confFile );
|
||||
stream.setCodec("UTF-8");
|
||||
QString line;
|
||||
while ( !stream.atEnd() ) {
|
||||
line = stream.readLine().simplified();
|
||||
if ( line.indexOf(key + ": ") == 0 ) {
|
||||
confFile.close();
|
||||
return line.replace(key + ": ", "");
|
||||
}
|
||||
|
||||
}
|
||||
confFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
//===========================
|
||||
// SYSCTL ACCESS (might require root)
|
||||
//===========================
|
||||
@@ -261,3 +307,40 @@ long long General::sysctlAsInt(QString var){
|
||||
if(0!=sysctlbyname(var.toLocal8Bit(), &result, &len, NULL, 0) ){ return 0; }
|
||||
return result;
|
||||
}
|
||||
|
||||
//===========================
|
||||
// Misc
|
||||
//===========================
|
||||
|
||||
QString General::bytesToHumanReadable(long long bytes)
|
||||
{
|
||||
float num = bytes;
|
||||
QStringList list;
|
||||
list << "KB" << "MB" << "GB" << "TB";
|
||||
|
||||
QStringListIterator i(list);
|
||||
QString unit("bytes");
|
||||
|
||||
while(num >= 1024.0 && i.hasNext())
|
||||
{
|
||||
unit = i.next();
|
||||
num /= 1024.0;
|
||||
}
|
||||
return QString().setNum(num,'f',2)+" "+unit;
|
||||
}
|
||||
|
||||
void General::emptyDir(QString dir){
|
||||
QDir d(dir);
|
||||
if(!d.exists()){ return; } //quick check to make sure directory exists first
|
||||
//Remove all the files in this directory
|
||||
QStringList tmp = d.entryList(QDir::Files | QDir::NoDotAndDotDot);
|
||||
for(int i=0; i<tmp.length(); i++){
|
||||
d.remove(tmp[i]);
|
||||
}
|
||||
//Now remove all the directories in this directory (recursive)
|
||||
tmp = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
for(int i=0; i<tmp.length(); i++){
|
||||
General::emptyDir(d.absoluteFilePath(tmp[i])); //Empty this directory first
|
||||
d.rmdir(tmp[i]); //Now try to remove it
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@ public:
|
||||
static QString RunCommand(QString command, QStringList arguments = QStringList(), QString workdir = "", QStringList env = QStringList() );
|
||||
// - success output only
|
||||
static bool RunQuickCommand(QString command, QStringList arguments = QStringList(), QString workdir = "", QStringList env = QStringList() );
|
||||
|
||||
static QStringList gitCMD(QString dir, QString cmd, QStringList args = QStringList());
|
||||
static void emptyDir(QString dir);
|
||||
//File Access Functions
|
||||
static QStringList readTextFile(QString filename);
|
||||
static bool writeTextFile(QString filename, QStringList contents, bool overwrite = true);
|
||||
@@ -55,6 +56,10 @@ public:
|
||||
//Retrieve a number-based sysctl
|
||||
static long long sysctlAsInt(QString var);
|
||||
|
||||
static QString bytesToHumanReadable(long long bytes);
|
||||
static QString getValFromTOConf(QString conf, QString key);
|
||||
static QString getValFromTrueOSConf(QString key);
|
||||
|
||||
};
|
||||
|
||||
} //end of pcbsd namespace
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include "sysadm-iocage.h"
|
||||
#include "sysadm-global.h"
|
||||
//need access to the global DISPATCHER object
|
||||
#include "globals.h"
|
||||
#include "globals.h"
|
||||
|
||||
using namespace sysadm;
|
||||
|
||||
@@ -17,11 +17,11 @@ using namespace sysadm;
|
||||
QJsonObject Iocage::activateStatus(){
|
||||
QJsonObject retObject;
|
||||
bool success = false;
|
||||
QString output = General::RunCommand(success, "iocage activate --status");
|
||||
QStringList info = General::RunCommand(success, "iocage get -p").split("\n");
|
||||
retObject.insert("activated", success ? "true" : "false");
|
||||
if(success){
|
||||
//Grab the currently activated pool out of the return, and list that
|
||||
QString pool = output.simplified();
|
||||
QString pool = info.last().simplified();
|
||||
retObject.insert("pool", pool);
|
||||
}
|
||||
return retObject;
|
||||
@@ -87,59 +87,104 @@ QJsonObject Iocage::cleanAll() {
|
||||
//================TEMPLATE MANAGEMENT===================
|
||||
QJsonObject Iocage::listTemplates(){
|
||||
QJsonObject retObject;
|
||||
QStringList local = General::RunCommand("iocage list -tlh ").split("\n");
|
||||
for(int i=0; i<local.length(); i++){
|
||||
QStringList info = local[i].split("\t"); //the -h flag is for scripting use (tabs as separators)
|
||||
//NOTE ABOUT FORMAT:
|
||||
// [JID, UUID, BOOT, STATE, TAG, TYPE, IP4, RELEASE, TEMPLATE]
|
||||
if(info.length()!=9){ continue; } //invalid line
|
||||
QJsonObject obj;
|
||||
obj.insert("jid",info[0]);
|
||||
obj.insert("uuid",info[1]);
|
||||
obj.insert("boot",info[2]);
|
||||
obj.insert("state",info[3]);
|
||||
obj.insert("tag",info[4]);
|
||||
obj.insert("type",info[5]);
|
||||
obj.insert("ip4",info[6]);
|
||||
obj.insert("release",info[7]);
|
||||
obj.insert("template",info[8]);
|
||||
retObject.insert(info[8], obj);
|
||||
bool ok = false;
|
||||
QStringList local = General::RunCommand(ok, "iocage list -tlh ").split("\n");
|
||||
if(ok){
|
||||
QJsonObject temp;
|
||||
for(int i=0; i<local.length(); i++){
|
||||
QStringList info = local[i].split("\t"); //the -h flag is for scripting use (tabs as separators)
|
||||
//NOTE ABOUT FORMAT:
|
||||
// [JID, UUID, BOOT, STATE, TAG, TYPE, RELEASE, IP4, IP6, TEMPLATE]
|
||||
if(info.length()!=10){ continue; } //invalid line
|
||||
QJsonObject obj;
|
||||
obj.insert("jid",info[0]);
|
||||
obj.insert("uuid",info[1]);
|
||||
obj.insert("boot",info[2]);
|
||||
obj.insert("state",info[3]);
|
||||
obj.insert("tag",info[4]);
|
||||
obj.insert("type",info[5]);
|
||||
obj.insert("release",info[6]);
|
||||
obj.insert("ip4",info[7]);
|
||||
obj.insert("ip6",info[8]);
|
||||
obj.insert("template",info[9]);
|
||||
temp.insert(info[9], obj);
|
||||
}
|
||||
retObject.insert("templates", temp);
|
||||
}else{
|
||||
retObject.insert("error",local.join("\n"));
|
||||
}
|
||||
|
||||
return retObject;
|
||||
}
|
||||
|
||||
QJsonObject Iocage::listReleases(){
|
||||
QJsonObject retObject;
|
||||
// Locally-available releases
|
||||
QStringList local = General::RunCommand("iocage list -rh").split("\n");
|
||||
retObject.insert("local", QJsonArray::fromStringList(local) );
|
||||
bool ok = false;
|
||||
QStringList local = General::RunCommand(ok, "iocage list -rh").split("\n");
|
||||
if(ok){ retObject.insert("local", QJsonArray::fromStringList(local) ); }
|
||||
//Remote releases available for download
|
||||
QStringList remote = General::RunCommand("iocage list -Rh").split("\n");
|
||||
for(int i=0; i<remote.length(); i++){
|
||||
if(remote[i].startsWith("[")){ remote[i] = remote[i].section("]",1,-1); }
|
||||
else{ remote.removeAt(i); i--; }
|
||||
QStringList remote = General::RunCommand(ok, "iocage list -rRh").split("\n");
|
||||
if(ok){
|
||||
for(int i=0; i<remote.length(); i++){
|
||||
if(remote[i].startsWith("[")){ remote[i] = remote[i].section("]",1,-1).simplified(); }
|
||||
else{ remote.removeAt(i); i--; }
|
||||
}
|
||||
retObject.insert("remote", QJsonArray::fromStringList(remote));
|
||||
}
|
||||
retObject.insert("remote", QJsonArray::fromStringList(remote));
|
||||
return retObject;
|
||||
}
|
||||
|
||||
QJsonObject Iocage::listPlugins(){
|
||||
QJsonObject retObject;
|
||||
//Not sure about format of this yet (just commited upstream) - just treat it as line-delimited for now. (2/16/17)
|
||||
//locally downloaded plugins
|
||||
QStringList local = General::RunCommand("iocage list -ph ").split("\n");
|
||||
retObject.insert("local", QJsonArray::fromStringList(local) );
|
||||
//Remote plugins available for download/use
|
||||
QStringList remote = General::RunCommand("iocage list -Ph").split("\n");
|
||||
retObject.insert("remote", QJsonArray::fromStringList(remote));
|
||||
bool ok = false;
|
||||
QStringList remote = General::RunCommand(ok,"iocage list -PhR").split("\n");
|
||||
QStringList local = General::RunCommand("iocage list -Ph").split("\n");
|
||||
if(!ok && remote.first().startsWith("Traceback")){
|
||||
//older version of iocage - no local browsing (remote uses the local syntax)
|
||||
remote = local;
|
||||
local.clear();
|
||||
}
|
||||
QJsonObject plugins;
|
||||
for(int i=0; i<remote.length(); i++){
|
||||
if(remote[i].startsWith("[")){ remote[i] = remote[i].section("]",1,-1); }
|
||||
else{ remote.removeAt(i); i--; continue; }
|
||||
//Now parse the line and put it into the plugins object
|
||||
QJsonObject obj;
|
||||
obj.insert("name", remote[i].section(" - ",0,0).simplified());
|
||||
obj.insert("description", remote[i].section(" - ",1,-1).section("(",0,-2).simplified());
|
||||
obj.insert("id", remote[i].section("(",-1).section(")",0,0).simplified());
|
||||
plugins.insert(obj.value("id").toString(), obj);
|
||||
}
|
||||
retObject.insert("remote", plugins);
|
||||
//Now do the local plugins
|
||||
plugins = QJsonObject(); //clear it
|
||||
for(int i=0; i<local.length(); i++){
|
||||
QStringList info = local[i].split("\t"); //the -h flag is for scripting use (tabs as separators)
|
||||
//NOTE ABOUT FORMAT:
|
||||
// [JID, UUID, BOOT, STATE, TAG, TYPE, RELEASE, IP4, IP6, TEMPLATE]
|
||||
if(info.length()!=10){ continue; } //invalid line
|
||||
QJsonObject obj;
|
||||
obj.insert("jid",info[0]);
|
||||
obj.insert("uuid",info[1]);
|
||||
obj.insert("boot",info[2]);
|
||||
obj.insert("state",info[3]);
|
||||
obj.insert("tag",info[4]); //name of the plugin used (non-unique)
|
||||
obj.insert("type",info[5]);
|
||||
obj.insert("release",info[6]);
|
||||
obj.insert("ip4",info[7]);
|
||||
obj.insert("ip6",info[8]);
|
||||
obj.insert("template",info[9]);
|
||||
plugins.insert(info[4]+"_"+info[0], obj);
|
||||
}
|
||||
retObject.insert("local",plugins);
|
||||
return retObject;
|
||||
}
|
||||
|
||||
QJsonObject Iocage::fetchReleases(QJsonObject inobj){
|
||||
QJsonObject retObject;
|
||||
if(!inobj.contains("releases")){ return retObject; } //nothing to do
|
||||
QStringList releases;
|
||||
QStringList releases;
|
||||
if(inobj.value("releases").isArray()){ releases = General::JsonArrayToStringList(inobj.value("releases").toArray()); }
|
||||
else if(inobj.value("releases").isString()){ releases << inobj.value("releases").toString(); }
|
||||
//Now start up each of these downloads as appropriate
|
||||
@@ -147,7 +192,7 @@ QJsonObject Iocage::fetchReleases(QJsonObject inobj){
|
||||
QString jobprefix = "sysadm_iocage_fetch_release_";
|
||||
QJsonArray started;
|
||||
for(int i=0; i<releases.length(); i++){
|
||||
releases[i] = releases[i].section(" ",0,0, QString::SectionSkipEmpty); //all valid releases are a single word - do not allow injection of other commands
|
||||
releases[i] = releases[i].section(" ",0,0, QString::SectionSkipEmpty); //all valid releases are a single word - do not allow injection of other commands (or "(EOL)" tags on end)
|
||||
if(cids.contains(jobprefix+releases[i]) ){ continue; } //this fetch job is already running - skip it for now
|
||||
DISPATCHER->queueProcess(jobprefix+releases[i], "iocage fetch --verify -r "+releases[i]);
|
||||
started << jobprefix+releases[i];
|
||||
@@ -156,25 +201,28 @@ QJsonObject Iocage::fetchReleases(QJsonObject inobj){
|
||||
return retObject;
|
||||
}
|
||||
|
||||
QJsonObject Iocage::fetchPlugins(QJsonObject inobj){
|
||||
QJsonObject Iocage::fetchPlugin(QJsonObject inobj){
|
||||
QJsonObject retObject;
|
||||
if(!inobj.contains("plugins")){ return retObject; } //nothing to do
|
||||
QStringList plugins;
|
||||
if(inobj.value("plugins").isArray()){ plugins = General::JsonArrayToStringList(inobj.value("plugins").toArray()); }
|
||||
else if(inobj.value("plugins").isString()){ plugins << inobj.value("plugins").toString(); }
|
||||
if(!inobj.contains("plugin") || !inobj.contains("net_device") || ! (inobj.contains("ip4") || inobj.contains("ip6")) ){ return retObject; } //nothing to do
|
||||
QString plugin = inobj.value("plugin").toString();
|
||||
QString dev = inobj.value("net_device").toString();
|
||||
QString inet;
|
||||
if(inobj.contains("ip6")){
|
||||
inet = "ip6_addr=\""+dev+"|"+inobj.value("ip6").toString()+"\"";
|
||||
}else{
|
||||
inet = "ip4_addr=\""+dev+"|"+inobj.value("ip4").toString()+"\"";
|
||||
}
|
||||
|
||||
//Now start up each of these downloads as appropriate
|
||||
QStringList cids = DISPATCHER->listJobs().value("no_queue").toObject().keys(); //all currently running/pending jobs
|
||||
QString jobprefix = "sysadm_iocage_fetch_plugin_";
|
||||
QJsonArray started;
|
||||
for(int i=0; i<plugins.length(); i++){
|
||||
plugins[i] = plugins[i].section(" ",0,0, QString::SectionSkipEmpty); //all valid releases are a single word - do not allow injection of other commands
|
||||
if(cids.contains(jobprefix+plugins[i]) ){ continue; } //this fetch job is already running - skip it for now
|
||||
DISPATCHER->queueProcess(jobprefix+plugins[i], "iocage fetch --verify -P "+plugins[i]);
|
||||
started << jobprefix+plugins[i];
|
||||
}
|
||||
if(started.count()>0){ retObject.insert("started_dispatcher_id", started); }
|
||||
plugin = plugin.section(" ",0,0, QString::SectionSkipEmpty); //all valid releases are a single word - do not allow injection of other commands
|
||||
if(cids.contains(jobprefix+plugin) ){ return QJsonObject(); } //this fetch job is already running
|
||||
DISPATCHER->queueProcess(jobprefix+plugin, "iocage fetch -P --name "+plugin+" "+inet);
|
||||
retObject.insert("started_dispatcher_id", jobprefix+plugin);
|
||||
return retObject;
|
||||
}
|
||||
|
||||
// Clean all templates on a box
|
||||
QJsonObject Iocage::cleanTemplates() {
|
||||
QJsonObject retObject;
|
||||
@@ -205,23 +253,31 @@ QJsonObject Iocage::cleanReleases() {
|
||||
// List the jails on the box
|
||||
QJsonObject Iocage::listJails() {
|
||||
QJsonObject retObject;
|
||||
QStringList output = General::RunCommand("iocage list -lh").split("\n");
|
||||
for(int i=0; i<output.length(); i++){
|
||||
QStringList info = output[i].split("\t");
|
||||
//FORMAT NOTE: (long output: "-l" flag)
|
||||
// [JID, UUID, BOOT, STATE, TAG, TYPE, IP4, RELEASE, TEMPLATE]
|
||||
if(info.length()!=9){ continue; } //invalid line
|
||||
QJsonObject obj;
|
||||
obj.insert("jid",info[0]);
|
||||
obj.insert("uuid",info[1]);
|
||||
obj.insert("boot",info[2]);
|
||||
obj.insert("state",info[3]);
|
||||
obj.insert("tag",info[4]);
|
||||
obj.insert("type",info[5]);
|
||||
obj.insert("ip4",info[6]);
|
||||
obj.insert("release",info[7]);
|
||||
obj.insert("template",info[8]);
|
||||
retObject.insert(info[1], obj); //use uuid as main id tag
|
||||
bool ok = false;
|
||||
QStringList local = General::RunCommand(ok, "iocage list -lh").split("\n");
|
||||
if(ok){
|
||||
QJsonObject temp;
|
||||
for(int i=0; i<local.length(); i++){
|
||||
QStringList info = local[i].split("\t"); //the -h flag is for scripting use (tabs as separators)
|
||||
//NOTE ABOUT FORMAT:
|
||||
// [JID, UUID, BOOT, STATE, TAG, TYPE, RELEASE, IP4, IP6, TEMPLATE]
|
||||
if(info.length()!=10){ continue; } //invalid line
|
||||
QJsonObject obj;
|
||||
obj.insert("jid",info[0]);
|
||||
obj.insert("uuid",info[1]);
|
||||
obj.insert("boot",info[2]);
|
||||
obj.insert("state",info[3]);
|
||||
obj.insert("tag",info[4]);
|
||||
obj.insert("type",info[5]);
|
||||
obj.insert("release",info[6]);
|
||||
obj.insert("ip4",info[7]);
|
||||
obj.insert("ip6",info[8]);
|
||||
obj.insert("template",info[9]);
|
||||
temp.insert(info[0], obj);
|
||||
}
|
||||
retObject.insert("jails", temp);
|
||||
}else{
|
||||
retObject.insert("error",local.join("\n"));
|
||||
}
|
||||
return retObject;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
static QJsonObject listReleases();
|
||||
static QJsonObject listPlugins();
|
||||
static QJsonObject fetchReleases(QJsonObject);
|
||||
static QJsonObject fetchPlugins(QJsonObject);
|
||||
static QJsonObject fetchPlugin(QJsonObject);
|
||||
static QJsonObject cleanTemplates();
|
||||
static QJsonObject cleanReleases();
|
||||
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
static QJsonObject capJail(QJsonObject);
|
||||
|
||||
static QJsonObject getJailSettings(QJsonObject);
|
||||
|
||||
|
||||
};
|
||||
|
||||
} //end of namespace
|
||||
|
||||
@@ -7,12 +7,23 @@
|
||||
#include "sysadm-general.h"
|
||||
#include "sysadm-moused.h"
|
||||
#include "sysadm-global.h"
|
||||
#include "sysadm-systemmanager.h"
|
||||
#include "globals.h"
|
||||
|
||||
#define _MOUSED_CONF QString("/etc/conf.d/moused")
|
||||
#define _MOUSED_SYS_CONF QString("/etc/rc.conf")
|
||||
#define _MOUSED_DEFAULT_CONF QString("/etc/defaults/rc.conf")
|
||||
|
||||
//Setup the Tap To Click (TTC) sysctl definitions
|
||||
#define _MOUSED_TTC_ENABLED QString("hw.psm.tap_enabled")
|
||||
#define _MOUSED_TTC_NORMAL QString("hw.psm.tap_timeout")
|
||||
#define _MOUSED_TTC_SYNAPTICS QString("hw.psm.synaptics.taphold_timeout")
|
||||
#define _MOUSED_TTC_ENABLE_SYNAPTICS QString("hw.psm.synaptics_support")
|
||||
|
||||
//Additional Synaptics controls
|
||||
#define _MOUSED_SYNAPTICS_TOUCHPAD_OFF QString("hw.psm.synaptics.touchpad_off")
|
||||
#define _MOUSED_SYNAPTICS_TWOFINGER_SCROLL QString("hw.psm.synaptics.two_finger_scroll")
|
||||
|
||||
using namespace sysadm;
|
||||
|
||||
QJsonObject moused::listDevices(){
|
||||
@@ -46,6 +57,7 @@ QJsonObject moused::listOptions(){
|
||||
QJsonObject out;
|
||||
out.insert("emulate_button_3", QJsonArray() << "true" << "false");
|
||||
out.insert("hand_mode", QJsonArray() << "left" << "right");
|
||||
out.insert("mouse_scroll_invert", QJsonArray() << "true" << "false");
|
||||
out.insert("virtual_scrolling", QJsonArray() << "true" << "false");
|
||||
out.insert("accel_exponential", "float min=1.0 max=2.0");
|
||||
out.insert("accel_linear", "float min=0.01 max=100.00");
|
||||
@@ -53,12 +65,12 @@ QJsonObject moused::listOptions(){
|
||||
out.insert("terminate_drift_threshold_pixels", "int min=0 max=1000");
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
QJsonObject moused::readOptions(QJsonObject obj){
|
||||
QString device = obj.value("device").toString();
|
||||
//qDebug() << "Read Options for Device:" << device;
|
||||
if(device.isEmpty()){ return QJsonObject(); } //invalid inputs
|
||||
|
||||
|
||||
QString val = General::getConfFileValue(_MOUSED_CONF, "moused_args_"+device+"=" );
|
||||
if(val.isEmpty()){ General::getConfFileValue(_MOUSED_SYS_CONF, "moused_flags=" ); }
|
||||
if(val.isEmpty()){ General::getConfFileValue(_MOUSED_DEFAULT_CONF, "moused_flags=" ); }
|
||||
@@ -72,10 +84,13 @@ QJsonObject moused::readOptions(QJsonObject obj){
|
||||
out.insert("emulate_button_3", args.contains("-3") ? "true" : "false");
|
||||
int index = args.indexOf("-m");
|
||||
bool righthand = true;
|
||||
bool scrollinvert = false;
|
||||
while(index>=0 && args.length() > (index+1) ){
|
||||
if(args[index+1].startsWith("1=")){ righthand = (args[index+1] == "1=1"); }
|
||||
else if(args[index+1].startsWith("4=")){ scrollinvert = (args[index+1] == "4=5"); }
|
||||
index = args.indexOf("-m", index+1);
|
||||
}
|
||||
out.insert("mouse_scroll_invert", scrollinvert ? "true" : "false" );
|
||||
out.insert("hand_mode", righthand ? "right" : "left");
|
||||
out.insert("virtual_scrolling", args.contains("-V") ? "true" : "false" );
|
||||
|
||||
@@ -122,6 +137,7 @@ QJsonObject moused::setOptions(QJsonObject obj){
|
||||
QString val = Cobj.value(keys[i]).toString();
|
||||
if(keys[i]=="emulate_button_3" && val=="true"){ args << "-3"; }
|
||||
else if(keys[i]=="hand_mode" && val=="left"){ args << "-m" << "1=3" << "-m" << "3=1"; }
|
||||
else if(keys[i]=="mouse_scroll_invert" && val=="true"){ args << "-m" << "4=5" << "-m" << "5=4"; }
|
||||
else if(keys[i]=="virtual_scrolling" && val=="true"){ args << "-V" << "-H"; } //Enable both horizontal and vertical virtual scrolling
|
||||
else if(keys[i]=="accel_exponential" && val!="1.0"){ args << "-A" << val; }
|
||||
else if(keys[i]=="accel_linear" && val!="1.0"){ args << "-a" << val; } //both X and Y linear acceleration
|
||||
@@ -141,8 +157,8 @@ QJsonObject moused::listActiveDevices(){
|
||||
QDir dir("/var/run");
|
||||
QJsonObject out;
|
||||
QStringList devsactive = dir.entryList(QStringList() << "moused-*.pid", QDir::Files, QDir::Name);
|
||||
for(int i=0; i<devsactive.length(); i++){
|
||||
devsactive[i] = devsactive[i].section("-",1,-1).section(".pid",0,0);
|
||||
for(int i=0; i<devsactive.length(); i++){
|
||||
devsactive[i] = devsactive[i].section("-",1,-1).section(".pid",0,0);
|
||||
}
|
||||
out.insert("active_devices", QJsonArray::fromStringList(devsactive));
|
||||
return out;
|
||||
@@ -167,3 +183,96 @@ QJsonObject moused::disableDevice(QJsonObject obj){
|
||||
out.insert("stopped", device);
|
||||
return out;
|
||||
}
|
||||
|
||||
QJsonObject moused::tapToClick(){
|
||||
QJsonObject out;
|
||||
QJsonObject tmp;
|
||||
tmp.insert("sysctl", QJsonArray() << _MOUSED_TTC_NORMAL << _MOUSED_TTC_SYNAPTICS << _MOUSED_TTC_ENABLED << _MOUSED_TTC_ENABLE_SYNAPTICS);
|
||||
tmp = SysMgmt::getSysctl(tmp);
|
||||
bool usesynaptics = false;
|
||||
int timeout = -1;
|
||||
if(tmp.contains(_MOUSED_TTC_ENABLE_SYNAPTICS)){
|
||||
usesynaptics = (tmp.value(_MOUSED_TTC_ENABLE_SYNAPTICS).toString().toInt()==1) && tmp.contains(_MOUSED_TTC_SYNAPTICS);
|
||||
}
|
||||
|
||||
if(usesynaptics){
|
||||
QString level = tmp.value(_MOUSED_TTC_SYNAPTICS).toString();
|
||||
if(level.isEmpty()){ level = tmp.value(_MOUSED_TTC_NORMAL).toString(); }
|
||||
out.insert("enabled", level.toInt()>0 ? "true" : "false" );
|
||||
out.insert("timeout", level);
|
||||
}else{
|
||||
int enabled = tmp.value(_MOUSED_TTC_ENABLED).toString().toInt();
|
||||
if(enabled<0){ out.insert("enabled", "unavailable"); }
|
||||
else{ out.insert("enabled", (enabled==1) ? "true" : "false" ); }
|
||||
out.insert("timeout", tmp.value(_MOUSED_TTC_NORMAL).toString() );
|
||||
}
|
||||
out.insert("using_synaptics", usesynaptics ? "true" : "false");
|
||||
return out;
|
||||
}
|
||||
|
||||
QJsonObject moused::setTapToClick(QJsonObject jsin){
|
||||
QJsonObject out;
|
||||
//Check the inputs first
|
||||
if(!jsin.contains("enable") && !jsin.contains("timeout")){
|
||||
return out;
|
||||
}
|
||||
//Find out which sysctls need to be set (only some systems have the synaptics option)
|
||||
QJsonObject tmp;
|
||||
tmp.insert("sysctl", QJsonArray() << _MOUSED_TTC_NORMAL << _MOUSED_TTC_SYNAPTICS << _MOUSED_TTC_ENABLED << _MOUSED_TTC_ENABLE_SYNAPTICS);
|
||||
tmp = SysMgmt::getSysctl(tmp); //this will only return valid sysctls - can use it for quick filtering/detection
|
||||
QStringList sysctls = tmp.keys();
|
||||
bool usesynaptics = false;
|
||||
if(tmp.contains(_MOUSED_TTC_ENABLE_SYNAPTICS)){ usesynaptics = (tmp.value(_MOUSED_TTC_ENABLE_SYNAPTICS).toString().toInt()==1) && tmp.contains(_MOUSED_TTC_SYNAPTICS); }
|
||||
bool canenable = (tmp.value(_MOUSED_TTC_ENABLED).toString()!="-1" );
|
||||
//Update the timeout as needed
|
||||
if(jsin.contains("timeout")){
|
||||
int ms = jsin.value("timeout").toInt(-1); //-1 is the default non-valid number
|
||||
if(ms<0){ ms = jsin.value("timeout").toString().toInt(); } //try a string->integer instead
|
||||
if(ms>=0){
|
||||
tmp = QJsonObject(); //clear it for re-use
|
||||
tmp.insert("value",QString::number(ms) );
|
||||
if(sysctls.contains(_MOUSED_TTC_NORMAL)){ tmp.insert("sysctl",_MOUSED_TTC_NORMAL); SysMgmt::setSysctl(tmp); }
|
||||
if(sysctls.contains(_MOUSED_TTC_SYNAPTICS)){ tmp.insert("sysctl",_MOUSED_TTC_SYNAPTICS); SysMgmt::setSysctl(tmp); }
|
||||
out.insert("timeout", QString::number(ms) );
|
||||
}
|
||||
}
|
||||
//Enable/Disable as needed
|
||||
if(jsin.contains("enable") && canenable ){
|
||||
bool enable = (jsin.value("enable").toString().toLower()=="true");
|
||||
tmp = QJsonObject(); //clear it for re-use
|
||||
if(usesynaptics){
|
||||
if(!sysctls.contains("timeout")){ //if we just set this, don't overwrite it
|
||||
tmp.insert("value", enable ? "125000" : "0"); //default values for enable/disable
|
||||
tmp.insert("sysctl", _MOUSED_TTC_SYNAPTICS);
|
||||
}
|
||||
}else{
|
||||
tmp.insert("value", enable ? "1" : "0");
|
||||
tmp.insert("sysctl", _MOUSED_TTC_ENABLED);
|
||||
}
|
||||
//Now make the actual change
|
||||
if(!tmp.isEmpty()){
|
||||
SysMgmt::setSysctl(tmp);
|
||||
out.insert("enabled", enable ? "true" : "false");
|
||||
}
|
||||
}
|
||||
//Restart the moused daemon if we made any changes
|
||||
if(!out.isEmpty()){ General::RunQuickCommand("service moused restart"); }
|
||||
return out;
|
||||
}
|
||||
|
||||
QJsonObject moused::synapticsSettings(){
|
||||
QJsonObject tmp;
|
||||
tmp.insert("sysctl", QJsonArray() << _MOUSED_SYNAPTICS_TOUCHPAD_OFF << _MOUSED_SYNAPTICS_TWOFINGER_SCROLL << _MOUSED_TTC_ENABLE_SYNAPTICS);
|
||||
SysMgmt::getSysctl(tmp);
|
||||
bool touch_off = false;
|
||||
bool two_finger_scroll = false;
|
||||
bool enabled = false;
|
||||
if(tmp.contains(_MOUSED_SYNAPTICS_TOUCHPAD_OFF)){ touch_off = (tmp.value(_MOUSED_SYNAPTICS_TOUCHPAD_OFF).toString().toInt()==1); }
|
||||
if(tmp.contains(_MOUSED_SYNAPTICS_TWOFINGER_SCROLL)){ two_finger_scroll = (tmp.value(_MOUSED_SYNAPTICS_TWOFINGER_SCROLL).toString().toInt()==1); }
|
||||
if(tmp.contains(_MOUSED_TTC_ENABLE_SYNAPTICS)){ enabled = (tmp.value(_MOUSED_TTC_ENABLE_SYNAPTICS).toString().toInt()==1); }
|
||||
QJsonObject out;
|
||||
out.insert("disable_touchpad", touch_off ? "true" : "false");
|
||||
out.insert("enable_two_finger_scroll", two_finger_scroll ? "true" : "false");
|
||||
out.insert("enable_synaptics", enabled ? "true" : "false");
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -26,8 +26,15 @@ public:
|
||||
static QJsonObject enableDevice(QJsonObject);
|
||||
static QJsonObject disableDevice(QJsonObject);
|
||||
|
||||
//General system input options
|
||||
static QJsonObject tapToClick();
|
||||
static QJsonObject setTapToClick(QJsonObject);
|
||||
|
||||
//Synaptics options
|
||||
static QJsonObject synapticsSettings();
|
||||
|
||||
};
|
||||
|
||||
|
||||
} //end of namespace
|
||||
|
||||
#endif
|
||||
|
||||
@@ -19,7 +19,7 @@ using namespace sysadm;
|
||||
//Copy over this data into the output structure
|
||||
NetworkEntry tmp;
|
||||
tmp.name = QString::fromLocal8Bit(entry->n_name);
|
||||
for(int i=0; entry->n_aliases[i] != 0; i++){
|
||||
for(int i=0; entry->n_aliases[i] != 0; i++){
|
||||
tmp.aliases << QString::fromLocal8Bit(entry->n_aliases[i]);
|
||||
}
|
||||
tmp.netnum = entry->n_net;
|
||||
@@ -57,8 +57,9 @@ NetDevSettings Network::deviceRCSettings(QString dev){
|
||||
if(val.startsWith("\"")){ val = val.remove(1); }
|
||||
if(val.endsWith("\"")){ val.chop(1); }
|
||||
val.prepend(" "); val.append(" "); //just to make additional parsing easier later - each "variable" should have whitespace on both sides
|
||||
if( (var=="vlans_"+dev) || (var=="wlans_"+dev) ){
|
||||
set.asDevice = val;
|
||||
if( val.simplified()==dev && (var.startsWith("vlans_") || var.startsWith("wlans_") )){
|
||||
set.asDevice = var.section("_",1,-1);
|
||||
if(var.startsWith("wlans_")){ set.wifihost = true; }
|
||||
}else if(var==("ifconfig_"+dev)){
|
||||
QStringList vals = val.split(" ",QString::SkipEmptyParts);
|
||||
//This is the main settings line: lots of things to look for:
|
||||
@@ -73,13 +74,13 @@ NetDevSettings Network::deviceRCSettings(QString dev){
|
||||
//IPv4/6 address can sometimes not have the "inet(6)" identifier - look through the first few values as well
|
||||
QStringList vals = val.split(" ",QString::SkipEmptyParts);
|
||||
for(int v=0; v<vals.length(); v++){
|
||||
|
||||
|
||||
}
|
||||
}*/
|
||||
}else{ set.useDHCP=true; } //end of DHCP check
|
||||
//Wifi Checks
|
||||
if(vals.contains("WPA")){ set.wifisecurity=true; }
|
||||
|
||||
|
||||
} //end variable checks
|
||||
} //end loop over rc.conf lines
|
||||
return set;
|
||||
@@ -119,11 +120,53 @@ NetDevSettings Network::deviceRCSettings(QString dev){
|
||||
}*/
|
||||
|
||||
//--------------------------------------
|
||||
bool NetworkRoot::saveRCSettings(NetDevSettings){
|
||||
return false;
|
||||
bool NetworkRoot::saveRCSettings(NetDevSettings set){
|
||||
if(!QFile::exists("/dev/"+set.device)){ return false; } //invalid device
|
||||
//Create the lines which need to be put info /etc/rc.conf based on the settings
|
||||
QStringList tags, lines, found;
|
||||
//Related device lines
|
||||
if(!set.asDevice.isEmpty()){
|
||||
if(set.wifihost){ tags << "wlans_"+set.asDevice; lines << "wlans_"+set.asDevice+"=\""+set.device+"\""; }
|
||||
else{ tags << "vlans_"+set.asDevice; lines << "vlans_"+set.asDevice+"=\""+set.device+"\""; }
|
||||
}
|
||||
//Main device line
|
||||
QString tmp = "ifconfig_"+set.device+"=\"";
|
||||
if(set.useDHCP){ tmp.append("DHCP"); }
|
||||
else{
|
||||
if(!set.staticIPv4.isEmpty()){ tmp.append("inet "+set.staticIPv4+" "); }
|
||||
if(!set.staticIPv6.isEmpty()){ tmp.append("inet6 "+set.staticIPv6+" "); }
|
||||
if(!set.staticNetmask.isEmpty()){ tmp.append("netmask "+set.staticNetmask+" "); }
|
||||
if(!set.staticGateway.isEmpty()){ tmp.append("gateway "+set.staticGateway+" "); }
|
||||
tmp = tmp.simplified(); //remove any excess whitespace on end
|
||||
}
|
||||
QString var = "ifconfig_"+set.device;
|
||||
tags << var;
|
||||
if(!tmp.isEmpty()){ lines << var+"=\""+tmp+"\""; }
|
||||
else{ lines << ""; } //delete the line
|
||||
|
||||
//Now read rc.conf and adjust contents as needed
|
||||
QStringList info = Network::readRcConf();
|
||||
for(int i=0; i<info.length(); i++){
|
||||
if(info[i].simplified().isEmpty()){ continue; }
|
||||
QString var = info[i].section("=",0,0).simplified();
|
||||
if( tags.contains(var) ){
|
||||
int index = tags.indexOf(var);
|
||||
info[i] = lines[index];
|
||||
lines.removeAt(index);
|
||||
found << tags.takeAt(index);
|
||||
}else if(found.contains(var)){
|
||||
//Duplicate line - remove this since it was already handled
|
||||
info.removeAt(i); i--;
|
||||
}
|
||||
}
|
||||
//Now add any lines which were not already found to the end of the file
|
||||
for(int i=0; i<lines.length(); i++){
|
||||
info << lines[i];
|
||||
}
|
||||
return General::writeTextFile("/etc/rc.conf", info, true);
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
bool NetworkRoot::setIfconfigSettings(NetDevSettings){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ struct NetDevSettings{
|
||||
//General data class for network devices
|
||||
// Note: Sources in NetDevice.cpp
|
||||
class NetDevice{
|
||||
private:
|
||||
private:
|
||||
QString name;
|
||||
public:
|
||||
NetDevice(QString devName){ name = devName; }
|
||||
@@ -61,7 +61,7 @@ public:
|
||||
long packetsTx();
|
||||
long errorsRx();
|
||||
long errorsTx();
|
||||
|
||||
|
||||
//Setting Functions (to make changes - requires root)
|
||||
void setUp(bool up); //Turn device on/off (temporary - not saved globally)
|
||||
|
||||
@@ -75,7 +75,6 @@ struct NetWifi{
|
||||
QString BSSID, SSID;
|
||||
};
|
||||
|
||||
|
||||
//The general-purpose class that any user/app can utilitize
|
||||
class Network{
|
||||
public:
|
||||
@@ -88,11 +87,11 @@ public:
|
||||
//The class that requires overarching root permissions (usually for changes to system)
|
||||
class NetworkRoot{
|
||||
public:
|
||||
//static bool saveNetworkEntry(NetworkEntry); //**Not implemented yet**
|
||||
//static bool saveNetworkEntry(NetworkEntry); // **Not implemented yet**
|
||||
static bool saveRCSettings(NetDevSettings); //rc.conf settings (bootup)
|
||||
static bool setIfconfigSettings(NetDevSettings); //ifconfig settings (temporary session)
|
||||
};
|
||||
|
||||
|
||||
} //end of pcbsd namespace
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -14,6 +14,20 @@ using namespace sysadm;
|
||||
// ==================
|
||||
// INLINE FUNCTIONS
|
||||
// ==================
|
||||
inline QStringList ids_from_origins(QStringList origins, QSqlDatabase DB){
|
||||
QSqlQuery q("SELECT id FROM packages WHERE origin IN ('"+origins.join("', '")+"')",DB);
|
||||
QStringList out;
|
||||
while(q.next()){ out << q.value("id").toString(); }
|
||||
return out;
|
||||
}
|
||||
|
||||
inline QStringList ids_from_names(QStringList names, QSqlDatabase DB){
|
||||
QSqlQuery q("SELECT id FROM packages WHERE name IN ('"+names.join("', '")+"')",DB);
|
||||
QStringList out;
|
||||
while(q.next()){ out << q.value("id").toString(); }
|
||||
return out;
|
||||
}
|
||||
|
||||
//Get annotation variable/values
|
||||
inline void annotations_from_ids(QStringList var_ids, QStringList val_ids, QJsonObject *out, QSqlDatabase DB){
|
||||
//Note: Both input lists *must* be the same length (one variable for one value)
|
||||
@@ -21,16 +35,16 @@ inline void annotations_from_ids(QStringList var_ids, QStringList val_ids, QJson
|
||||
tot.removeDuplicates();
|
||||
int index = -1;
|
||||
QSqlQuery q("SELECT annotation, annotation_id FROM annotation WHERE annotation_id IN ('"+tot.join("', '")+"')",DB);
|
||||
while(q.next()){
|
||||
while(q.next()){
|
||||
//qDebug() << "Got query result:" << q.value("annotation_id").toString() << q.value("annotation").toString();
|
||||
index = var_ids.indexOf(q.value("annotation_id").toString());
|
||||
while(index>=0){
|
||||
var_ids.replace(index, q.value("annotation").toString());
|
||||
while(index>=0){
|
||||
var_ids.replace(index, q.value("annotation").toString());
|
||||
index = var_ids.indexOf(q.value("annotation_id").toString());
|
||||
}
|
||||
index = val_ids.indexOf(q.value("annotation_id").toString());
|
||||
while(index>=0){
|
||||
val_ids.replace(index, q.value("annotation").toString());
|
||||
while(index>=0){
|
||||
val_ids.replace(index, q.value("annotation").toString());
|
||||
index = val_ids.indexOf(q.value("annotation_id").toString());
|
||||
}
|
||||
}
|
||||
@@ -47,7 +61,7 @@ inline QStringList origins_from_package_ids(QStringList ids, QSqlDatabase DB){
|
||||
while(q.next()){ out << q.value("origin").toString(); }
|
||||
return out;
|
||||
}
|
||||
//Generic ID's -> Names function (known databases: users, groups, licenses, shlibs, categories )
|
||||
//Generic ID's -> Names function (known databases: users, groups, licenses, shlibs, categories, packages )
|
||||
inline QStringList names_from_ids(QStringList ids, QString db, QSqlDatabase DB){
|
||||
QSqlQuery q("SELECT name FROM "+db+" WHERE id IN ('"+ids.join("', '")+"')",DB);
|
||||
QStringList out;
|
||||
@@ -68,15 +82,35 @@ inline QStringList requires_from_ids(QStringList ids, QSqlDatabase DB){
|
||||
while(q.next()){ out << q.value("require").toString(); }
|
||||
return out;
|
||||
}
|
||||
|
||||
//conflict ID's from package ID's
|
||||
inline QStringList conflicts_from_ids(QStringList ids, QSqlDatabase DB){
|
||||
QSqlQuery q("SELECT conflict_id FROM pkg_conflicts WHERE package_id IN ('"+ids.join("', '")+"')", DB);
|
||||
QStringList out;
|
||||
while(q.next()){ out << q.value("conflict_id").toString(); }
|
||||
qDebug() << "Last Conflict detection Error:" << q.lastError().text();
|
||||
return out;
|
||||
}
|
||||
|
||||
//dependencies from package ID's
|
||||
inline QStringList depends_from_ids(QStringList ids, QSqlDatabase DB){
|
||||
//Note: This returns package names, not ID's
|
||||
QSqlQuery q("SELECT name FROM deps WHERE package_id IN ('"+ids.join("', '")+"')", DB);
|
||||
QStringList out;
|
||||
while(q.next()){ out << q.value("name").toString(); }
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
inline QString getRepoFile(QString repo){
|
||||
if(repo=="local"){ return "/var/db/pkg/local.sqlite"; }
|
||||
else{ return ("/var/db/pkg/repo-"+repo+".sqlite"); }
|
||||
else{ return ("/var/db/pkg/repo-"+repo+".sqlite"); }
|
||||
}
|
||||
inline QString openDB(QString repo){
|
||||
//This ensures that each request for a database gets its own unique connection
|
||||
//This ensures that each request for a database gets its own unique connection
|
||||
// (preventing conflict between concurrent calls)
|
||||
QSqlDatabase DB = QSqlDatabase::addDatabase("QSQLITE", repo+QUuid::createUuid().toString());
|
||||
DB.setConnectOptions("QSQLITE_OPEN_READONLY=1");
|
||||
DB.setConnectOptions("QSQLITE_OPEN_READONLY=1");
|
||||
DB.setHostName("localhost");
|
||||
QString path = getRepoFile(repo);
|
||||
DB.setDatabaseName(path); //path to the database file
|
||||
@@ -100,15 +134,15 @@ QJsonObject PKG::pkg_info(QStringList origins, QString repo, QString category, b
|
||||
QJsonObject retObj;
|
||||
//if(origins.contains("math/R")){ qDebug() << "pkg_info:" << repo << category; }
|
||||
QString dbconn = openDB(repo);
|
||||
if(!dbconn.isEmpty()){
|
||||
if(!dbconn.isEmpty()){
|
||||
QSqlDatabase DB = QSqlDatabase::database(dbconn);
|
||||
if(!DB.isOpen()){ return retObj; } //could not open DB (file missing?)
|
||||
//Now do all the pkg info, one pkg origin at a time
|
||||
origins.removeAll("");
|
||||
origins.removeDuplicates();
|
||||
QString q_string = "SELECT * FROM packages";
|
||||
if(!origins.isEmpty()){
|
||||
q_string.append(" WHERE origin IN ('"+origins.join("', '")+"')");
|
||||
if(!origins.isEmpty()){
|
||||
q_string.append(" WHERE name IN ('"+origins.join("', '")+"')");
|
||||
//Also keep the ordering of the origins preserved
|
||||
/*q_string.append(" ORDER BY CASE origins ");
|
||||
for(int i=0; i<origins.length(); i++){ q_string.append("WHEN '"+origins[i]+"' THEN '"+QString::number(i+1)+"' "); }
|
||||
@@ -119,13 +153,13 @@ QJsonObject PKG::pkg_info(QStringList origins, QString repo, QString category, b
|
||||
QSqlQuery query(q_string, DB);
|
||||
while(query.next()){
|
||||
QString id = query.value("id").toString(); //need this pkg id for later
|
||||
QString origin = query.value("origin").toString(); //need the origin for later
|
||||
QString name = query.value("name").toString(); //need the origin for later
|
||||
//if(origins.contains("math/R")){ qDebug() << "Found origin:" << origin << id; }
|
||||
if(id.isEmpty() || origin.isEmpty()){ continue; }
|
||||
if(id.isEmpty() || name.isEmpty()){ continue; }
|
||||
QJsonObject info;
|
||||
//General info
|
||||
for(int i=0; i<query.record().count(); i++){
|
||||
info.insert(query.record().fieldName(i), query.value(i).toString() );
|
||||
info.insert(query.record().fieldName(i), query.value(i).toString() );
|
||||
}
|
||||
//ANNOTATIONS
|
||||
QSqlQuery q2("SELECT tag_id, value_id FROM pkg_annotation WHERE package_id = '"+id+"'", DB);
|
||||
@@ -134,7 +168,7 @@ QJsonObject PKG::pkg_info(QStringList origins, QString repo, QString category, b
|
||||
tags << q2.value("tag_id").toString(); vals << q2.value("value_id").toString();
|
||||
}
|
||||
if(!tags.isEmpty()){ annotations_from_ids(tags, vals, &info, DB); }
|
||||
if(!fullresults){ retObj.insert(origin,info); continue; } //skip the rest of the info queries
|
||||
if(!fullresults){ retObj.insert(name,info); continue; } //skip the rest of the info queries
|
||||
//OPTIONS
|
||||
QSqlQuery q3("SELECT value, option FROM pkg_option INNER JOIN option ON pkg_option.option_id = option.option_id WHERE pkg_option.package_id = '"+id+"'", DB);
|
||||
QJsonObject options;
|
||||
@@ -144,22 +178,29 @@ QJsonObject PKG::pkg_info(QStringList origins, QString repo, QString category, b
|
||||
}
|
||||
if(!options.isEmpty()){ info.insert("options",options); }
|
||||
//DEPENDENCIES
|
||||
QSqlQuery q4("SELECT origin FROM deps WHERE package_id = '"+id+"'", DB);
|
||||
QStringList tmpList;
|
||||
while(q4.next()){
|
||||
QSqlQuery q4("SELECT origin, name FROM deps WHERE package_id = '"+id+"'", DB);
|
||||
QStringList tmpList, tmpListN;
|
||||
while(q4.next()){
|
||||
tmpList << q4.value("origin").toString();
|
||||
tmpListN << q4.value("name").toString();
|
||||
} //end deps query
|
||||
if(!tmpList.isEmpty()){ info.insert("dependencies", QJsonArray::fromStringList(tmpList) ); }
|
||||
if(!tmpList.isEmpty()){
|
||||
//info.insert("dependencies_origins", QJsonArray::fromStringList(tmpList) );
|
||||
info.insert("dependencies", QJsonArray::fromStringList(tmpListN) );
|
||||
}
|
||||
//FILES
|
||||
QSqlQuery q5("SELECT path FROM files WHERE package_id = '"+id+"'", DB);
|
||||
tmpList.clear();
|
||||
while(q5.next()){ tmpList << q5.value("path").toString(); }
|
||||
if(!tmpList.isEmpty()){ info.insert("files", QJsonArray::fromStringList(tmpList) ); }
|
||||
//REVERSE DEPENDENCIES
|
||||
QSqlQuery q6("SELECT package_id FROM deps WHERE origin = '"+origin+"'", DB);
|
||||
QSqlQuery q6("SELECT package_id FROM deps WHERE name = '"+name+"'", DB);
|
||||
tmpList.clear();
|
||||
while(q6.next()){ tmpList << q6.value("package_id").toString(); }
|
||||
if(!tmpList.isEmpty()){ info.insert("reverse_dependencies", QJsonArray::fromStringList(origins_from_package_ids(tmpList, DB)) ); }
|
||||
if(!tmpList.isEmpty()){
|
||||
//info.insert("reverse_dependencies_origins", QJsonArray::fromStringList(origins_from_package_ids(tmpList, DB)) );
|
||||
info.insert("reverse_dependencies", QJsonArray::fromStringList(names_from_ids(tmpList, "packages", DB)) );
|
||||
}
|
||||
//USERS
|
||||
QSqlQuery q7("SELECT user_id FROM pkg_users WHERE package_id = '"+id+"'", DB);
|
||||
tmpList.clear();
|
||||
@@ -204,9 +245,9 @@ QJsonObject PKG::pkg_info(QStringList origins, QString repo, QString category, b
|
||||
QSqlQuery q15("SELECT require_id FROM pkg_requires WHERE package_id = '"+id+"'", DB);
|
||||
tmpList.clear();
|
||||
while(q15.next()){ tmpList << q15.value("require_id").toString(); }
|
||||
if(!tmpList.isEmpty()){ info.insert("requires", QJsonArray::fromStringList(requires_from_ids(tmpList, DB)) ); }
|
||||
if(!tmpList.isEmpty()){ info.insert("requires", QJsonArray::fromStringList(requires_from_ids(tmpList, DB)) ); }\
|
||||
//Now insert this information into the main object
|
||||
retObj.insert(origin,info);
|
||||
retObj.insert(name,info);
|
||||
} //end loop over pkg matches
|
||||
DB.close();
|
||||
}//end if dbconn exists (force DB out of scope now)
|
||||
@@ -218,67 +259,67 @@ QJsonObject PKG::pkg_info(QStringList origins, QString repo, QString category, b
|
||||
QStringList PKG::pkg_search(QString repo, QString searchterm, QStringList searchexcludes, QString category){
|
||||
QString dbconn = openDB(repo);
|
||||
QStringList found;
|
||||
if(!dbconn.isEmpty()){
|
||||
if(!dbconn.isEmpty()){
|
||||
QSqlDatabase DB = QSqlDatabase::database(dbconn);
|
||||
if(!DB.isOpen()){ return QStringList(); } //could not open DB (file missing?)
|
||||
|
||||
|
||||
QStringList terms = searchterm.split(" ",QString::SkipEmptyParts);
|
||||
searchexcludes.removeAll("");
|
||||
QString q_string;
|
||||
int numtry = 0;
|
||||
while(found.isEmpty() && numtry<2){
|
||||
if(numtry<1 && !searchterm.contains(" ")){ //single-word-search (exact names never have multiple words)
|
||||
q_string = "SELECT origin FROM packages WHERE name = '"+searchterm+"' OR origin LIKE '%/"+searchterm+"'";
|
||||
q_string = "SELECT name FROM packages WHERE name = '"+searchterm+"' OR origin LIKE '%/"+searchterm+"'";
|
||||
if(!category.isEmpty()){ q_string.append(" AND origin LIKE '"+category+"/%'"); }
|
||||
if(!searchexcludes.isEmpty()){ q_string.append(" AND name NOT LIKE '%"+searchexcludes.join("%' AND name NOT LIKE '%")+"%'"); }
|
||||
q_string.append(" COLLATE NOCASE"); // Case insensitive
|
||||
QSqlQuery query(q_string, DB);
|
||||
while(query.next()){
|
||||
found << query.value("origin").toString(); //need the origin for later
|
||||
found << query.value("name").toString(); //need the origin for later
|
||||
}
|
||||
}
|
||||
if(found.length()<60 && numtry<1){
|
||||
//Expand the search to names containing the term
|
||||
q_string = "SELECT origin FROM packages WHERE name LIKE '"+searchterm+"%'";
|
||||
q_string = "SELECT name FROM packages WHERE name LIKE '"+searchterm+"%'";
|
||||
if(!category.isEmpty()){ q_string.append(" AND origin LIKE '"+category+"/%'"); }
|
||||
if(!searchexcludes.isEmpty()){ q_string.append(" AND name NOT LIKE '%"+searchexcludes.join("%' AND name NOT LIKE '%")+"%'"); }
|
||||
QSqlQuery q2(q_string, DB);
|
||||
while(q2.next()){
|
||||
found << q2.value("origin").toString(); //need the origin for later
|
||||
found << q2.value("name").toString(); //need the origin for later
|
||||
}
|
||||
}
|
||||
if(found.length()<60 && numtry<1){
|
||||
//Expand the search to names containing the term
|
||||
q_string = "SELECT origin FROM packages WHERE name LIKE '%"+searchterm+"%'";
|
||||
q_string = "SELECT name FROM packages WHERE name LIKE '%"+searchterm+"%'";
|
||||
if(!category.isEmpty()){ q_string.append(" AND origin LIKE '"+category+"/%'"); }
|
||||
if(!searchexcludes.isEmpty()){ q_string.append(" AND name NOT LIKE '%"+searchexcludes.join("%' AND name NOT LIKE '%")+"%'"); }
|
||||
QSqlQuery q2(q_string, DB);
|
||||
while(q2.next()){
|
||||
found << q2.value("origin").toString(); //need the origin for later
|
||||
found << q2.value("name").toString(); //need the origin for later
|
||||
}
|
||||
}
|
||||
if(found.length()<60){
|
||||
//Expand the search to comments
|
||||
if(terms.length()<2){ q_string = "SELECT origin FROM packages WHERE comment LIKE '%"+searchterm+"%'"; }
|
||||
else if(numtry==0){ q_string = "SELECT origin FROM packages WHERE comment LIKE '%"+terms.join("%' AND comment LIKE '%")+"%'"; }
|
||||
else if(numtry==1){ q_string = "SELECT origin FROM packages WHERE comment LIKE '%"+terms.join("%' OR comment LIKE '%")+"%'"; }
|
||||
if(terms.length()<2){ q_string = "SELECT nameFROM packages WHERE comment LIKE '%"+searchterm+"%'"; }
|
||||
else if(numtry==0){ q_string = "SELECT name FROM packages WHERE comment LIKE '%"+terms.join("%' AND comment LIKE '%")+"%'"; }
|
||||
else if(numtry==1){ q_string = "SELECT name FROM packages WHERE comment LIKE '%"+terms.join("%' OR comment LIKE '%")+"%'"; }
|
||||
if(!category.isEmpty()){ q_string.append(" AND origin LIKE '"+category+"/%'"); }
|
||||
if(!searchexcludes.isEmpty()){ q_string.append(" AND comment NOT LIKE '%"+searchexcludes.join("%' AND comment NOT LIKE '%")+"%'"); }
|
||||
QSqlQuery q2(q_string, DB);
|
||||
while(q2.next()){
|
||||
found << q2.value("origin").toString(); //need the origin for later
|
||||
found << q2.value("name").toString(); //need the origin for later
|
||||
}
|
||||
}
|
||||
if(found.length()<100){
|
||||
//Expand the search to full descriptions
|
||||
if(terms.length()<2){ q_string = "SELECT origin FROM packages WHERE desc LIKE '%"+searchterm+"%'"; }
|
||||
else if(numtry==0){ q_string = "SELECT origin FROM packages WHERE desc LIKE '%"+terms.join("%' AND desc LIKE '%")+"%'"; }
|
||||
else if(numtry==1){ q_string = "SELECT origin FROM packages WHERE desc LIKE '%"+terms.join("%' OR desc LIKE '%")+"%'"; }
|
||||
if(terms.length()<2){ q_string = "SELECT name FROM packages WHERE desc LIKE '%"+searchterm+"%'"; }
|
||||
else if(numtry==0){ q_string = "SELECT name FROM packages WHERE desc LIKE '%"+terms.join("%' AND desc LIKE '%")+"%'"; }
|
||||
else if(numtry==1){ q_string = "SELECT name FROM packages WHERE desc LIKE '%"+terms.join("%' OR desc LIKE '%")+"%'"; }
|
||||
if(!category.isEmpty()){ q_string.append(" AND origin LIKE '"+category+"/%'"); }
|
||||
if(!searchexcludes.isEmpty()){ q_string.append(" AND desc NOT LIKE '%"+searchexcludes.join("%' AND desc NOT LIKE '%")+"%'"); }
|
||||
QSqlQuery q2(q_string, DB);
|
||||
while(q2.next()){
|
||||
found << q2.value("origin").toString(); //need the origin for later
|
||||
found << q2.value("name").toString(); //need the origin for later
|
||||
}
|
||||
}
|
||||
//Now bump the try count
|
||||
@@ -297,7 +338,7 @@ while(found.isEmpty() && numtry<2){
|
||||
QJsonArray PKG::list_categories(QString repo){
|
||||
QString dbconn = openDB(repo);
|
||||
QStringList found;
|
||||
if(!dbconn.isEmpty()){
|
||||
if(!dbconn.isEmpty()){
|
||||
QSqlDatabase DB = QSqlDatabase::database(dbconn);
|
||||
if(!DB.isOpen()){ return QJsonArray(); } //could not open DB (file missing?)
|
||||
|
||||
@@ -313,7 +354,7 @@ QJsonArray PKG::list_categories(QString repo){
|
||||
while(query.next()){
|
||||
found << query.value("name").toString(); //need the origin for later
|
||||
}
|
||||
|
||||
|
||||
//Now check all the categories to ensure that pkgs exist within it
|
||||
for(int i=0; i<found.length(); i++){
|
||||
if(origins.filter(found[i]+"/").isEmpty()){ found.removeAt(i); i--; }
|
||||
@@ -330,17 +371,25 @@ QJsonArray PKG::list_categories(QString repo){
|
||||
|
||||
QJsonArray PKG::list_repos(bool updated){
|
||||
QString dbdir = "/var/db/pkg/repo-%1.sqlite";
|
||||
QDir confdir("/usr/local/etc/pkg/repos");
|
||||
QStringList confs = confdir.entryList(QStringList() << "*.conf", QDir::Files);
|
||||
QStringList repodirs; repodirs << "/etc/pkg" << "/etc/pkg/repos" << "/usr/local/etc/pkg" << "/usr/local/etc/pkg/repos";
|
||||
QStringList found;
|
||||
found << "local"; //There is always a local database (for installed pkgs)
|
||||
for(int i=0; i<confs.length(); i++){
|
||||
QStringList repoinfo = General::readTextFile(confdir.absoluteFilePath(confs[i])).join("\n").split("}");
|
||||
for(int j=0; j<repoinfo.length(); j++){
|
||||
QString repo = repoinfo[j].section(":",0,0).simplified();
|
||||
if(QFile::exists(dbdir.arg(repo)) && repoinfo[j].section("enabled:",1,-1).section(":",0,0).contains("true")){ found << repo; }
|
||||
}
|
||||
}
|
||||
for(int d=0; d<repodirs.length(); d++){
|
||||
if(!QFile::exists(repodirs[d])){ continue; }
|
||||
QDir confdir(repodirs[d]);
|
||||
QStringList confs = confdir.entryList(QStringList() << "*.conf", QDir::Files);
|
||||
for(int i=0; i<confs.length(); i++){
|
||||
QStringList repoinfo = General::readTextFile(confdir.absoluteFilePath(confs[i])).join("\n").split("\n}");
|
||||
for(int j=0; j<repoinfo.length(); j++){
|
||||
//qDebug() << "Repoinfo:" << repoinfo[j];
|
||||
QString repo = repoinfo[j].section(":",0,0).simplified();
|
||||
QString enabled = repoinfo[j].section("enabled:",1,-1).section(":",0,0).toLower();
|
||||
bool isEnabled = (enabled.contains("yes") || enabled.contains("true"));
|
||||
//qDebug() << "Checking Repo:" << repo << enabled << isEnabled;
|
||||
if(QFile::exists(dbdir.arg(repo)) && isEnabled){ found << repo; }
|
||||
} //loop over repos listed in conf
|
||||
} //loop over confs in repodir
|
||||
} //loop over repodirs
|
||||
if(found.length()<2 && !updated){
|
||||
//Only the local repo could be found - update the package repos and try again
|
||||
DProcess* proc = DISPATCHER->queueProcess(Dispatcher::PKG_QUEUE, "internal_sysadm_pkg_repo_update_sync", "pkg update");
|
||||
@@ -350,6 +399,79 @@ QJsonArray PKG::list_repos(bool updated){
|
||||
return QJsonArray::fromStringList(found);
|
||||
}
|
||||
|
||||
QJsonObject PKG::evaluateInstall(QStringList origins, QString repo){
|
||||
//qDebug() << "Verify Install:" << origins << repo;
|
||||
QJsonObject out;
|
||||
out.insert("install_origins", QJsonArray::fromStringList(origins) );
|
||||
out.insert("repo", repo);
|
||||
if(repo=="local" || origins.isEmpty()){ return out; } //nothing to do
|
||||
QString dbconn = openDB(repo);
|
||||
QString ldbconn = openDB("local");
|
||||
if(!dbconn.isEmpty()){
|
||||
QSqlDatabase DB = QSqlDatabase::database(dbconn);
|
||||
QSqlDatabase LDB = QSqlDatabase::database(ldbconn);
|
||||
if(!DB.isOpen() || !LDB.isOpen()){ return out; } //could not open DB (file missing?)
|
||||
|
||||
//First get the list of all packages which need to be installed (ID's) from the remote database
|
||||
QStringList toInstall_id;
|
||||
QStringList tmp;
|
||||
if(origins.first().contains("/")){ tmp = names_from_ids( ids_from_origins(origins, DB), "packages", DB); }
|
||||
else{ tmp = origins; } //already given names
|
||||
//qDebug() << " - Initial names:" << tmp;
|
||||
while(!tmp.isEmpty()){
|
||||
QStringList ids = ids_from_names(tmp, DB);
|
||||
for(int i=0; i<ids.length(); i++){
|
||||
if(toInstall_id.contains(ids[i])){ ids.removeAt(i); i--; } //remove any duplicate/evaluated ID's
|
||||
}
|
||||
if(ids.isEmpty()){ break; } //stop the loop - found the last round of dependencies
|
||||
toInstall_id << ids; //add these to the list which are going to get installed
|
||||
tmp = depends_from_ids(ids, DB); //now get the depdendencies of these packages
|
||||
//qDebug() << " - Iteration names:" << tmp;
|
||||
}
|
||||
|
||||
//Now go through and remove any packages from the list which are already installed locally
|
||||
QStringList names = names_from_ids(toInstall_id, "packages", DB); //same order
|
||||
//qDebug() << " - Total Names:" << names;
|
||||
QStringList local_names = names_from_ids( ids_from_names(names, LDB), "packages", LDB);
|
||||
//qDebug() << " - Local Names:" << local_names;
|
||||
for(int i=0; i<local_names.length(); i++){
|
||||
names.removeAll(local_names[i]);
|
||||
}
|
||||
//qDebug() << " - Filtered Names:" << names;
|
||||
toInstall_id = ids_from_names(names, DB); //now get the shorter/filtered list of ID's (remote)
|
||||
//qDebug() << " - Filtered ID's:" << toInstall_id;
|
||||
//Get the list of conflicting packages which are already installed
|
||||
QStringList conflict_ids = conflicts_from_ids(toInstall_id, DB); //also get the list of any conflicts for these packages
|
||||
conflict_ids.removeDuplicates();
|
||||
QStringList conflict_names = names_from_ids(conflict_ids, "packages", DB);
|
||||
//qDebug() << " - Conflicts (remote):" << conflict_ids << conflict_names;
|
||||
out.insert("conflicts", QJsonArray::fromStringList(names_from_ids( ids_from_names(conflict_names, LDB), "packages", LDB) ) );
|
||||
//Now assemble all the information about the packages (remote database)
|
||||
QJsonObject install;
|
||||
//qDebug() << "Perform Query";
|
||||
QSqlQuery qi("SELECT * FROM packages WHERE id IN ('"+toInstall_id.join("', '")+"')", DB);
|
||||
while(qi.next()){
|
||||
QJsonObject obj;
|
||||
obj.insert( "name", qi.value("name").toString());
|
||||
obj.insert( "origin", qi.value("origin").toString());
|
||||
obj.insert( "pkgsize", qi.value("pkgsize").toString());
|
||||
obj.insert( "flatsize", qi.value("flatsize").toString());
|
||||
obj.insert( "version", qi.value("version").toString());
|
||||
obj.insert( "comment", qi.value("comment").toString());
|
||||
install.insert(qi.value("name").toString(), obj);
|
||||
}
|
||||
//qDebug() << "Final Install Object:" << install;
|
||||
//qDebug() << "Last Query Error:" << qi.lastError().text();
|
||||
//Add the info to the output object and close the databases
|
||||
out.insert("install", install);
|
||||
DB.close();
|
||||
LDB.close();
|
||||
} //force DB out of scope
|
||||
QSqlDatabase::removeDatabase(dbconn);
|
||||
QSqlDatabase::removeDatabase(ldbconn);
|
||||
return out;
|
||||
}
|
||||
|
||||
//=================
|
||||
//pkg modification routines (dispatcher events for notifications)
|
||||
//=================
|
||||
@@ -382,7 +504,7 @@ QJsonObject PKG::pkg_remove(QStringList origins, bool recursive){
|
||||
obj.insert("status", "pending");
|
||||
obj.insert("proc_cmd",cmd);
|
||||
obj.insert("proc_id",ID);
|
||||
return obj;
|
||||
return obj;
|
||||
}
|
||||
|
||||
QJsonObject PKG::pkg_lock(QStringList origins){
|
||||
@@ -397,7 +519,7 @@ QJsonObject PKG::pkg_lock(QStringList origins){
|
||||
obj.insert("status", "pending");
|
||||
obj.insert("proc_cmd",cmd);
|
||||
obj.insert("proc_id",ID);
|
||||
return obj;
|
||||
return obj;
|
||||
}
|
||||
|
||||
QJsonObject PKG::pkg_unlock(QStringList origins){
|
||||
@@ -412,7 +534,7 @@ QJsonObject PKG::pkg_unlock(QStringList origins){
|
||||
obj.insert("status", "pending");
|
||||
obj.insert("proc_cmd",cmd);
|
||||
obj.insert("proc_id",ID);
|
||||
return obj;
|
||||
return obj;
|
||||
}
|
||||
|
||||
//==================
|
||||
@@ -430,7 +552,7 @@ QJsonObject PKG::pkg_update(bool force){
|
||||
obj.insert("status", "pending");
|
||||
obj.insert("proc_cmd",cmd);
|
||||
obj.insert("proc_id",ID);
|
||||
return obj;
|
||||
return obj;
|
||||
}
|
||||
|
||||
QJsonObject PKG::pkg_check_upgrade(){
|
||||
@@ -444,7 +566,7 @@ QJsonObject PKG::pkg_check_upgrade(){
|
||||
obj.insert("status", "pending");
|
||||
obj.insert("proc_cmd",cmd);
|
||||
obj.insert("proc_id",ID);
|
||||
return obj;
|
||||
return obj;
|
||||
}
|
||||
|
||||
QJsonObject PKG::pkg_upgrade(){
|
||||
@@ -472,7 +594,7 @@ QJsonObject PKG::pkg_audit(){
|
||||
obj.insert("status", "pending");
|
||||
obj.insert("proc_cmd",cmd);
|
||||
obj.insert("proc_id",ID);
|
||||
return obj;
|
||||
return obj;
|
||||
}
|
||||
|
||||
QJsonObject PKG::pkg_autoremove(){
|
||||
@@ -486,5 +608,5 @@ QJsonObject PKG::pkg_autoremove(){
|
||||
obj.insert("status", "pending");
|
||||
obj.insert("proc_cmd",cmd);
|
||||
obj.insert("proc_id",ID);
|
||||
return obj;
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ public:
|
||||
static QStringList pkg_search(QString repo, QString searchterm, QStringList searchexcludes, QString category = "");
|
||||
static QJsonArray list_categories(QString repo);
|
||||
static QJsonArray list_repos(bool updated = false);
|
||||
static QJsonObject evaluateInstall(QStringList origins, QString repo); //evaluate what will be done if these packages are installed
|
||||
|
||||
|
||||
//pkg modification routines (dispatcher events for notifications)
|
||||
static QJsonObject pkg_install(QStringList origins, QString repo);
|
||||
@@ -37,9 +39,9 @@ public:
|
||||
static QJsonObject pkg_upgrade(); //upgrade all pkgs (use sysadm/updates if possible instead)
|
||||
static QJsonObject pkg_audit(); //List details of vulnerable packages
|
||||
static QJsonObject pkg_autoremove(); //Autoremove orphaned packages
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
} //end of sysadm namespace
|
||||
|
||||
#endif
|
||||
|
||||
@@ -9,7 +9,8 @@ ServiceManager::ServiceManager(QString chroot, QString ip)
|
||||
{
|
||||
this->chroot = chroot;
|
||||
this->ip = ip;
|
||||
loadRCdata();
|
||||
usingOpenRC = QFile::exists(chroot+"/sbin/rc-update");
|
||||
loadRCdata();
|
||||
//loadServices();
|
||||
}
|
||||
|
||||
@@ -75,14 +76,14 @@ bool ServiceManager::Start(Service service)
|
||||
// Start the process
|
||||
QString prog;
|
||||
QStringList args;
|
||||
bool once = !isEnabled(service);
|
||||
bool once = !isEnabled(service) && !usingOpenRC;
|
||||
if ( chroot.isEmpty() ) {
|
||||
prog = "service";
|
||||
args << service.Directory;
|
||||
args << "start";
|
||||
args << (once ? "onestart" : "start") ;
|
||||
} else {
|
||||
prog = "warden";
|
||||
args << "chroot" << ip << "service" << service.Directory << (once ? "one" : "" )+QString("start");
|
||||
args << "chroot" << ip << "service" << service.Directory << (once ? "onestart" : "start" );
|
||||
}
|
||||
return General::RunQuickCommand(prog,args);
|
||||
}
|
||||
@@ -94,14 +95,14 @@ bool ServiceManager::Stop(Service service)
|
||||
// Start the process
|
||||
QString prog;
|
||||
QStringList args;
|
||||
bool once = !isEnabled(service);
|
||||
bool once = !isEnabled(service) && !usingOpenRC;
|
||||
if ( chroot.isEmpty() ) {
|
||||
prog = "service";
|
||||
args << service.Directory;
|
||||
args << "stop";
|
||||
args << (once ? "onestop" : "stop") ;
|
||||
} else {
|
||||
prog = "warden";
|
||||
args << "chroot" << ip << "service" << service.Directory << (once ? "one" : "" )+QString("stop");
|
||||
args << "chroot" << ip << "service" << service.Directory << (once ? "onestop" : "stop" );
|
||||
}
|
||||
return General::RunQuickCommand(prog,args);
|
||||
}
|
||||
@@ -111,7 +112,7 @@ bool ServiceManager::Restart(Service service)
|
||||
if(service.Directory.isEmpty()){ return false; }
|
||||
QString prog;
|
||||
QStringList args;
|
||||
bool once = !isEnabled(service);
|
||||
bool once = !isEnabled(service) && !usingOpenRC;
|
||||
if ( chroot.isEmpty() ) {
|
||||
prog = "service";
|
||||
args << service.Directory;
|
||||
@@ -152,7 +153,13 @@ Service ServiceManager::loadServices(QString name)
|
||||
|
||||
// OpenRC directories are /etc/init.d and /usr/local/etc/init.d
|
||||
QStringList stringDirs;
|
||||
stringDirs << chroot + "/etc/init.d" << chroot + "/usr/local/etc/init.d";
|
||||
if(usingOpenRC){
|
||||
//OpenRC
|
||||
stringDirs << chroot + "/etc/init.d" << chroot + "/usr/local/etc/init.d";
|
||||
}else{
|
||||
//FreeBSD rc.d
|
||||
stringDirs << chroot + "/etc/rc.d" << chroot + "/usr/local/etc/rc.d";
|
||||
}
|
||||
|
||||
for ( QString dir: stringDirs)
|
||||
{
|
||||
@@ -186,7 +193,7 @@ Service ServiceManager::loadServices(QString name)
|
||||
|
||||
if (line.simplified().startsWith("description=") ||
|
||||
line.simplified().startsWith("desc=") ||
|
||||
line.simplified().startsWith("name=")) {
|
||||
(line.simplified().startsWith("name=") && service.Description.isEmpty()) ) {
|
||||
service.Description = line.section("=\"",1,-1).section("\"",0,0);
|
||||
}
|
||||
}
|
||||
@@ -245,6 +252,7 @@ void ServiceManager::loadUnusedData() {
|
||||
|
||||
// get the services enabled for all runlevels so we can update services
|
||||
void ServiceManager::loadRunlevels() {
|
||||
if(!usingOpenRC){ return; }
|
||||
QStringList info = sysadm::General::RunCommand("rc-status --nocolor --all").split("\n");
|
||||
QString runlevel = "default";
|
||||
for (int i=0; i<info.length(); i++) {
|
||||
@@ -264,19 +272,17 @@ void ServiceManager::loadRunlevels() {
|
||||
}
|
||||
|
||||
bool ServiceManager::enableDisableService(QString name, bool enable) {
|
||||
QString runlevel = "default";
|
||||
// look in services for name, see if there is a runlevel set
|
||||
// if not, use default
|
||||
QString prog = "rc-update";
|
||||
QString prog = usingOpenRC ? "rc-update" : "sysrc";
|
||||
QStringList args;
|
||||
if (enable)
|
||||
args << "add";
|
||||
else
|
||||
args << "delete";
|
||||
|
||||
args << name;
|
||||
args << runlevel;
|
||||
qDebug() << prog << " " << args;
|
||||
if(usingOpenRC){
|
||||
if (enable){ args << "add"; }
|
||||
else{ args << "delete"; }
|
||||
args << name;
|
||||
}else{
|
||||
args << name+"_enable=\""+ (enable ? "YES" : "NO") + "\"";
|
||||
}
|
||||
//qDebug() << prog << " " << args;
|
||||
bool ret = sysadm::General::RunQuickCommand(prog,args);
|
||||
loadUnusedData();
|
||||
return ret;
|
||||
|
||||
@@ -92,9 +92,10 @@ private:
|
||||
void loadRunlevels();
|
||||
|
||||
bool enableDisableService(QString name, bool enable=false);
|
||||
|
||||
|
||||
QString chroot;
|
||||
QString ip;
|
||||
bool usingOpenRC;
|
||||
};
|
||||
}
|
||||
#endif // SERVICEMANAGER_H
|
||||
|
||||
209
src/server/library/sysadm-sourcectl.cpp
Normal file
209
src/server/library/sysadm-sourcectl.cpp
Normal file
@@ -0,0 +1,209 @@
|
||||
//===========================================
|
||||
// TrueOS source code
|
||||
// Copyright (c) 2017, JT (q5sys)
|
||||
// Available under the 3-clause BSD license
|
||||
// See the LICENSE file for full details
|
||||
//===========================================
|
||||
#include "sysadm-general.h"
|
||||
#include "sysadm-sourcectl.h"
|
||||
#include "sysadm-global.h"
|
||||
#include "globals.h"
|
||||
|
||||
#include <QUuid>
|
||||
#include "sysadm-general.h"
|
||||
#include "sysadm-update.h"
|
||||
#include "sysadm-global.h"
|
||||
#include "globals.h"
|
||||
|
||||
using namespace sysadm;
|
||||
|
||||
// ==================
|
||||
// INLINE FUNCTIONS
|
||||
// ==================
|
||||
|
||||
// =================
|
||||
// MAIN FUNCTIONS
|
||||
// =================
|
||||
|
||||
QJsonObject sourcectl::downloadsource(){
|
||||
// cmd that will be run = git clone https://github.com/trueos/freebsd.git /usr/src
|
||||
QJsonObject retObject;
|
||||
QString ID = QUuid::createUuid().toString(); // Create a unique ID for this queued action
|
||||
// Queue the update action
|
||||
QString cmd;
|
||||
cmd = "git clone https://github.com/trueos/freebsd.git /usr/src";
|
||||
DISPATCHER->queueProcess("sysadm_sourcectl_downloadsource::"+ID, cmd);
|
||||
|
||||
// Return some details to user that the action was queued
|
||||
retObject.insert("command", "Downloading TrueOS Source Tree");
|
||||
retObject.insert("comment", "Task Queued");
|
||||
retObject.insert("queueid", ID);
|
||||
return retObject;
|
||||
}
|
||||
|
||||
QJsonObject sourcectl::updatesource(){
|
||||
// cmd that will be run = git reset --hard && git pull
|
||||
QJsonObject retObject;
|
||||
QString ID = QUuid::createUuid().toString(); // Create a unique ID for this queued action
|
||||
// Queue the update action
|
||||
QStringList cmds;
|
||||
cmds << "git reset --hard" << "git pull";
|
||||
DISPATCHER->queueProcess("sysadm_sourcectl_updatesource::"+ID, cmds, "/usr/src/");
|
||||
|
||||
// Return some details to user that the action was queued
|
||||
retObject.insert("command", "Updating TrueOS Source Tree");
|
||||
retObject.insert("comment", "Task Queued");
|
||||
retObject.insert("queueid", ID);
|
||||
return retObject;
|
||||
}
|
||||
|
||||
QJsonObject sourcectl::deletesource(){
|
||||
// cmd that will be run = rm -rf /usr/src/
|
||||
QJsonObject retObject;
|
||||
QString ID = QUuid::createUuid().toString(); // Create a unique ID for this queued action
|
||||
// Queue the update action
|
||||
QString cmd;
|
||||
cmd = "rm -rf /usr/src/";
|
||||
DISPATCHER->queueProcess("sysadm_sourcectl_deletesource::"+ID, cmd);
|
||||
|
||||
// Return some details to user that the action was queued
|
||||
retObject.insert("command", "Deleting TrueOS Source Tree");
|
||||
retObject.insert("comment", "Task Queued");
|
||||
retObject.insert("queueid", ID);
|
||||
return retObject;
|
||||
}
|
||||
|
||||
QJsonObject sourcectl::stopsource(){}
|
||||
|
||||
QJsonObject sourcectl::downloadports(){
|
||||
// cmd that will be run = git clone https://github.com/trueos/freebsd-ports.git /usr/ports
|
||||
QJsonObject retObject;
|
||||
QString ID = QUuid::createUuid().toString(); // Create a unique ID for this queued action
|
||||
// Queue the update action
|
||||
QString cmd;
|
||||
cmd = "git clone https://github.com/trueos/freebsd-ports.git /usr/ports";
|
||||
DISPATCHER->queueProcess("sysadm_sourcectl_downloadports::"+ID, cmd);
|
||||
|
||||
// Return some details to user that the action was queued
|
||||
retObject.insert("command", "Downloading TrueOS PortsTree");
|
||||
retObject.insert("comment", "Task Queued");
|
||||
retObject.insert("queueid", ID);
|
||||
return retObject;
|
||||
}
|
||||
QJsonObject sourcectl::updateports(){
|
||||
// cmd that will be run = git reset --hard && git pull
|
||||
QJsonObject retObject;
|
||||
QString ID = QUuid::createUuid().toString(); // Create a unique ID for this queued action
|
||||
// Queue the update action
|
||||
QStringList cmds;
|
||||
cmds << "git reset --hard" << "git pull";
|
||||
DISPATCHER->queueProcess("sysadm_sourcectl_updateports::"+ID, cmds, "/usr/ports/");
|
||||
|
||||
// Return some details to user that the action was queued
|
||||
retObject.insert("command", "Updating TrueOS PortsTree");
|
||||
retObject.insert("comment", "Task Queued");
|
||||
retObject.insert("queueid", ID);
|
||||
return retObject;
|
||||
}
|
||||
|
||||
|
||||
QJsonObject sourcectl::deleteports(){
|
||||
// cmd that will be run = rm -rf /usr/ports/
|
||||
QJsonObject retObject;
|
||||
QString ID = QUuid::createUuid().toString(); // Create a unique ID for this queued action
|
||||
// Queue the update action
|
||||
QString cmd;
|
||||
cmd = "rm -rf /usr/ports/";
|
||||
DISPATCHER->queueProcess("sysadm_sourcectl_deleteports::"+ID, cmd);
|
||||
|
||||
// Return some details to user that the action was queued
|
||||
retObject.insert("command", "Deleting TrueOS PortsTree");
|
||||
retObject.insert("comment", "Task Queued");
|
||||
retObject.insert("queueid", ID);
|
||||
return retObject;
|
||||
}
|
||||
|
||||
QJsonObject sourcectl::stopports(){}
|
||||
|
||||
|
||||
void sourcectl::saveSourceLog(QString){}
|
||||
void sourcectl::savePortsLog(QString){}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
git clone https://github.com/trueos/freebsd.git /usr/src
|
||||
git clone https://github.com/trueos/freebsd-ports.git /usr/ports
|
||||
|
||||
git reset --hard && git pull
|
||||
|
||||
|
||||
rm -rf /usr/src/
|
||||
rm -rf /usr/ports/
|
||||
|
||||
|
||||
|
||||
QJsonObject SysMgmt::fetchPortsTree(QString altDir){
|
||||
//void SysMgmt::fetchPortsTree(QStringList &cmds, QStringList &dirs){
|
||||
QJsonObject out;
|
||||
if(altDir.isEmpty()){ altDir = "/usr/ports"; }
|
||||
//Does Ports tree exist? If not create it.
|
||||
if(!QFile::exists(altDir)){
|
||||
QDir dir;
|
||||
if(!dir.mkpath(altDir) ){
|
||||
out.insert("error","Could not create directory: "+altDir);
|
||||
return out;
|
||||
}
|
||||
}
|
||||
//Does a local git repo exist? If not create it.
|
||||
QString URL = "https://www.github.com/trueos/freebsd-ports.git";
|
||||
if(QFile::exists(altDir+"/.git")){
|
||||
//Check if the remote URL is correct
|
||||
QString origin = General::gitCMD(altDir, "git remote show -n origin").filter("Fetch URL:").join("").section("URL:",1,30).simplified();
|
||||
if(origin != URL){
|
||||
General::gitCMD(altDir,"git",QStringList() << "remote" << "remove" << "origin");
|
||||
General::gitCMD(altDir,"git", QStringList() << "remote" << "add" << "origin" << URL);
|
||||
}
|
||||
}else{
|
||||
//new GIT setup
|
||||
General::emptyDir(altDir);
|
||||
General::gitCMD(altDir, "git", QStringList() << "init" << altDir );
|
||||
General::gitCMD(altDir, "git", QStringList() << "remote" << "add" << "origin" << URL );
|
||||
}
|
||||
//Now update the tree with git
|
||||
QString ID = "system_fetch_ports_tree";
|
||||
DISPATCHER->queueProcess(ID, "git pull origin", altDir );
|
||||
out.insert("result","process_started");
|
||||
out.insert("process_id",ID);
|
||||
return out;
|
||||
}
|
||||
|
||||
/*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";
|
||||
}
|
||||
*/
|
||||
34
src/server/library/sysadm-sourcectl.h
Normal file
34
src/server/library/sysadm-sourcectl.h
Normal file
@@ -0,0 +1,34 @@
|
||||
//===========================================
|
||||
// TrueOS source code
|
||||
// Copyright (c) 2017, JT (q5sys)
|
||||
// Available under the 3-clause BSD license
|
||||
// See the LICENSE file for full details
|
||||
//===========================================
|
||||
#ifndef __PCBSD_LIB_UTILS_SOURCECTL_H
|
||||
#define __PCBSD_LIB_UTILS_SOURCECTL_H
|
||||
|
||||
#include <QJsonObject>
|
||||
#include "sysadm-global.h"
|
||||
|
||||
namespace sysadm{
|
||||
|
||||
class sourcectl{
|
||||
public:
|
||||
|
||||
static QJsonObject downloadports();
|
||||
static QJsonObject updateports();
|
||||
static QJsonObject deleteports();
|
||||
static QJsonObject stopports();
|
||||
static QJsonObject downloadsource();
|
||||
static QJsonObject updatesource();
|
||||
static QJsonObject deletesource();
|
||||
static QJsonObject stopsource();
|
||||
|
||||
static void saveSourceLog(QString);
|
||||
static void savePortsLog(QString);
|
||||
|
||||
};
|
||||
|
||||
} //end of sysadm namespace
|
||||
|
||||
#endif
|
||||
@@ -7,33 +7,29 @@
|
||||
#include "sysadm-general.h"
|
||||
#include "sysadm-systemmanager.h"
|
||||
#include "sysadm-global.h"
|
||||
//need access to the global DISPATCHER object
|
||||
#include "globals.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;
|
||||
QString tmp; //temporary string for use later
|
||||
|
||||
int val = General::RunCommand("apm -l").toInt(&ok);
|
||||
if ( ok && (val >= 0 && val <= 100) ) {
|
||||
retObject.insert("battery", "true");
|
||||
} else {
|
||||
//Battery Charging State
|
||||
QStringList vals = General::RunCommand("apm -alt").split("\n");
|
||||
int state =-1;
|
||||
if(vals.length()>=1){ state = vals[0].toInt(&ok); }
|
||||
//Battery Charge State is 255 if no battery available
|
||||
if (ok && state == 255 ){
|
||||
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);
|
||||
retObject.insert("battery", "true");
|
||||
// Charging state
|
||||
if ( ok && state == 0 )
|
||||
retObject.insert("status", "offline");
|
||||
else if ( ok && state == 1 )
|
||||
@@ -42,8 +38,20 @@ QJsonObject SysMgmt::batteryInfo(){
|
||||
retObject.insert("status", "backup");
|
||||
else
|
||||
retObject.insert("status", "unknown");
|
||||
|
||||
int timeleft = General::RunCommand("apm -t").toInt(&ok);
|
||||
// Charging Level
|
||||
int level = -1;
|
||||
ok = false;
|
||||
if(vals.length()>=2){ level = vals[1].toInt(&ok); }
|
||||
if ( ok ) {
|
||||
tmp.setNum(level);
|
||||
retObject.insert("level", tmp);
|
||||
} else {
|
||||
retObject.insert("level", "-1");
|
||||
}
|
||||
// Time Left (seconds)
|
||||
int timeleft = -1;
|
||||
ok = false;
|
||||
if(vals.length()>=3){ timeleft= vals[2].toInt(&ok); }
|
||||
if ( ok ) {
|
||||
tmp.setNum(timeleft);
|
||||
retObject.insert("timeleft", tmp);
|
||||
@@ -65,7 +73,7 @@ QJsonObject SysMgmt::cpuPercentage() {
|
||||
QStringList result = General::RunCommand("sysctl -n kern.cp_times").split(" ");
|
||||
static QStringList last = QStringList();
|
||||
if(last.isEmpty()){
|
||||
//need two ticks before it works properly
|
||||
//need two ticks before it works properly
|
||||
sleep(1);
|
||||
last = result;
|
||||
result = General::RunCommand("sysctl -n kern.cp_times").split(" ");
|
||||
@@ -81,7 +89,7 @@ QJsonObject SysMgmt::cpuPercentage() {
|
||||
//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());
|
||||
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
|
||||
@@ -211,7 +219,7 @@ QJsonObject SysMgmt::memoryStats() {
|
||||
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);
|
||||
@@ -262,7 +270,7 @@ QJsonObject SysMgmt::procInfo() {
|
||||
for(int i=0; i<output.length(); i++){
|
||||
if (output.at(i).contains("PID") && output.at(i).contains("USERNAME")){
|
||||
inSection = true;
|
||||
continue;
|
||||
continue;
|
||||
}
|
||||
if (!inSection)
|
||||
continue;
|
||||
@@ -291,6 +299,26 @@ QJsonObject SysMgmt::procInfo() {
|
||||
return retObject;
|
||||
}
|
||||
|
||||
// Get a sysctl
|
||||
QJsonObject SysMgmt::getSysctl(QJsonObject jsin) {
|
||||
QJsonObject retObject;
|
||||
|
||||
QStringList sysctl;
|
||||
if(jsin.value("sysctl").isArray()){ sysctl = General::JsonArrayToStringList(jsin.value("sysctl").toArray()); }
|
||||
else if(jsin.value("sysctl").isString()){ sysctl << jsin.value("sysctl").toString(); }
|
||||
if ( sysctl.isEmpty() ) {
|
||||
retObject.insert("error", "Missing required key 'sysctl'");
|
||||
return retObject;
|
||||
}
|
||||
|
||||
QStringList output = General::RunCommand("sysctl", sysctl).split("\n");
|
||||
for(int i=0; i<output.length(); i++){
|
||||
if( !output[i].contains(":") || output[i].contains("unknown oid") ){ continue; }
|
||||
retObject.insert(output[i].section(":",0,0), output[i].section(":",1,-1).simplified());
|
||||
}
|
||||
return retObject;
|
||||
}
|
||||
|
||||
// Set a sysctl
|
||||
QJsonObject SysMgmt::setSysctl(QJsonObject jsin) {
|
||||
QJsonObject retObject;
|
||||
@@ -317,18 +345,29 @@ QJsonObject SysMgmt::sysctlList() {
|
||||
QJsonObject retObject;
|
||||
|
||||
// This can be cleaned up and not use CLI
|
||||
QStringList output = General::RunCommand("sysctl -W -a").split("\n");
|
||||
QStringList output = General::RunCommand("sysctl -aW").split("\n");
|
||||
QStringList details = General::RunCommand("sysctl -aWdt").split("\n");
|
||||
|
||||
QString sysctl, value;
|
||||
QString sysctl, type, description;
|
||||
for(int i=0; i<output.length(); i++){
|
||||
if ( output.at(i).isEmpty())
|
||||
continue;
|
||||
|
||||
if ( output.at(i).isEmpty()){ continue; }
|
||||
QJsonObject tmp;
|
||||
sysctl = output.at(i).section(":", 0, 0);
|
||||
value = output.at(i).section(":", 1, -1).simplified();
|
||||
retObject.insert(sysctl, value);
|
||||
tmp.insert("value", output.at(i).section(":", 1, -1).simplified() );
|
||||
retObject.insert(sysctl, tmp);
|
||||
}
|
||||
for(int i=0; i<details.length(); i++){
|
||||
if ( details.at(i).isEmpty() ){ continue; }
|
||||
sysctl = details[i].section(":",0,0);
|
||||
type = details[i].section(":",1,1).simplified();
|
||||
description = details[i].section(":",2,-1).simplified();
|
||||
if(retObject.contains(sysctl)){
|
||||
QJsonObject tmp = retObject.value(sysctl).toObject();
|
||||
tmp.insert("type", type);
|
||||
if(!description.isEmpty()){ tmp.insert("description", description); }
|
||||
retObject.insert(sysctl, tmp); //replace the sysctl object into the output object
|
||||
}
|
||||
}
|
||||
|
||||
return retObject;
|
||||
}
|
||||
|
||||
@@ -391,3 +430,28 @@ QJsonObject SysMgmt::systemReboot() {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <QJsonObject>
|
||||
#include "sysadm-global.h"
|
||||
|
||||
|
||||
namespace sysadm{
|
||||
|
||||
class SysMgmt{
|
||||
@@ -21,13 +22,21 @@ public:
|
||||
static QJsonObject killProc(QJsonObject);
|
||||
static QJsonObject memoryStats();
|
||||
static QJsonObject procInfo();
|
||||
|
||||
//sysctl management
|
||||
static QJsonObject getSysctl(QJsonObject);
|
||||
static QJsonObject setSysctl(QJsonObject);
|
||||
static QJsonObject sysctlList();
|
||||
|
||||
static QJsonObject systemInfo();
|
||||
static QJsonObject systemReboot();
|
||||
static QJsonObject systemHalt();
|
||||
|
||||
static QJsonObject systemDevices();
|
||||
static QJsonObject fetchPortsTree(QString altDir = ""); //QStringList &cmds, QStringList &dirs);
|
||||
//static QJsonObject fetchSourceTree(QString branch, QStringList &cmds, QStringList &dirs); // This is not ready yet
|
||||
};
|
||||
|
||||
} //end of pcbsd namespace
|
||||
|
||||
} //end of namespace
|
||||
|
||||
#endif
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
#include "globals.h"
|
||||
|
||||
#define UP_PIDFILE "/tmp/.updateInProgress"
|
||||
#define UP_RBFILE "/tmp/.rebootRequired"
|
||||
#define UP_RBFILE "/tmp/.trueos-update-staged"
|
||||
#define UP_UPFILE "/tmp/.updatesAvailable"
|
||||
#define UP_UPFILE_ERR "/tmp/.updateCheckError"
|
||||
|
||||
#define UP_CONFFILE "/usr/local/etc/trueos.conf"
|
||||
|
||||
@@ -34,15 +35,23 @@ QDateTime Update::rebootRequiredSince(){
|
||||
QJsonObject Update::checkUpdates(bool fast) {
|
||||
//NOTE: The "fast" option should only be used for automated/timed checks (to prevent doing this long check too frequently)
|
||||
QJsonObject retObject;
|
||||
qDebug() << "Check for updates: fast=" << fast;
|
||||
//qDebug() << "Check for updates: fast=" << fast;
|
||||
//Quick check to ensure the tool is available
|
||||
if(!QFile::exists("/usr/local/bin/pc-updatemanager")){
|
||||
QString tool = "/usr/local/bin/pc-updatemanager";
|
||||
if(!QFile::exists(tool)){
|
||||
return retObject;
|
||||
}
|
||||
|
||||
//See if the check for updates is currently running - just return nothing at the moment for that
|
||||
if(DISPATCHER->isJobActive("sysadm_update_checkupdates")){
|
||||
retObject.insert("status", "checkingforupdates");
|
||||
return retObject;
|
||||
}
|
||||
//Check if the system is waiting to reboot
|
||||
if(QFile::exists(UP_RBFILE)){
|
||||
retObject.insert("status","rebootrequired");
|
||||
if(QFile::exists(UP_UPFILE)){ QFile::remove(UP_UPFILE); } //ensure the next fast update does a full check
|
||||
if(QFile::exists(UP_UPFILE_ERR)){ QFile::remove(UP_UPFILE_ERR); } //ensure the next fast update does a full check
|
||||
//Also add the information on what updates are pending
|
||||
retObject.insert("details", sysadm::General::readTextFile(UP_RBFILE).join("\n") );
|
||||
return retObject;
|
||||
@@ -57,36 +66,44 @@ QJsonObject Update::checkUpdates(bool fast) {
|
||||
return retObject;
|
||||
}
|
||||
}
|
||||
//Get the list of deatils from the update checks (fast/full)
|
||||
//Get the list of details from the update checks (fast/full)
|
||||
QStringList output;
|
||||
QDateTime cdt = QDateTime::currentDateTime();
|
||||
QDateTime fdt = cdt.addDays(-1); //just enough to trip the checks below if needed
|
||||
bool lasterr = false;
|
||||
if(QFile::exists(UP_UPFILE)){ fdt = QFileInfo(UP_UPFILE).lastModified(); }
|
||||
else if(QFile::exists(UP_UPFILE_ERR)){ fdt = QFileInfo(UP_UPFILE_ERR).lastModified(); lasterr = true; }
|
||||
//qDebug() << "Time Stamps (now/file):" << cdt << fdt;
|
||||
//qDebug() << " - File earlier than now:" << (fdt<cdt);
|
||||
//qDebug() << " - File +1 day earlier than now (+day):" << (fdt.addDays(1)<cdt);
|
||||
//qDebug() << " - File +1 day earlier than now (+secs):" << (fdt.addSecs(24*60)<cdt);
|
||||
//qDebug() << " - Seconds from File->Day time:" << fdt.secsTo(cdt);
|
||||
int secs = fdt.secsTo(cdt);
|
||||
if(fast && (secs<43200) ){
|
||||
//Note: The "fast" check will only be used if the last full check was less than 12 hours earlier.
|
||||
if(fast && (secs<43200) && !lasterr ){
|
||||
//Note: The "fast" check will only be used if the last full check was less than 12 hours earlier
|
||||
// (unless the last run ended in an error - then it will use the 5-minute check below)
|
||||
//qDebug() << " - UseFast Re-read";
|
||||
output = General::readTextFile(UP_UPFILE);
|
||||
}else if(secs<600 ){
|
||||
//Note: This will re-use the previous check if it was less than 10 minutes ago (prevent hammering servers from user checks)
|
||||
//qDebug() << " - Use Fast Re-read (failsafe - less than 10 minute interval)";
|
||||
output = General::readTextFile(UP_UPFILE);
|
||||
}else{
|
||||
if(lasterr){output = General::readTextFile(UP_UPFILE_ERR); }
|
||||
else{ output = General::readTextFile(UP_UPFILE); }
|
||||
|
||||
}else if(secs<300){
|
||||
//Note: This will re-use the previous check if it was less than 5 minutes ago (prevent hammering servers from user checks)
|
||||
//qDebug() << " - Use Fast Re-read (failsafe - less than 5 minute interval)";
|
||||
if(lasterr){ output = General::readTextFile(UP_UPFILE_ERR); }
|
||||
else{ output = General::readTextFile(UP_UPFILE); }
|
||||
}else{
|
||||
//qDebug() << " - Run full check";
|
||||
//output = General::RunCommand("pc-updatemanager check").split("\n");
|
||||
output.append( General::RunCommand("pc-updatemanager pkgcheck").split("\n") );
|
||||
while(output.last().simplified()==""){ output.removeLast(); }
|
||||
if(!output.last().contains("ERROR:")){ //make sure there was network access available first - otherwise let it try again soon
|
||||
General::writeTextFile(UP_UPFILE, output); //save this check for later "fast" updates
|
||||
QStringList cmds;
|
||||
if(tool.endsWith("/pc-updatemanager")){
|
||||
cmds << "pc-updatemanager syncconf" << "pc-updatemanager pkgcheck";
|
||||
}
|
||||
DISPATCHER->queueProcess("sysadm_update_checkupdates", cmds );
|
||||
retObject.insert("status", "checkingforupdates");
|
||||
//qDebug() << " - Done starting check";
|
||||
return retObject;
|
||||
}
|
||||
//qDebug() << "pc-updatemanager checks:" << output;
|
||||
|
||||
|
||||
QString nameval;
|
||||
int pnum=1;
|
||||
for ( int i = 0; i < output.size(); i++)
|
||||
@@ -135,10 +152,28 @@ QJsonObject Update::checkUpdates(bool fast) {
|
||||
retObject.insert("status", "updatesavailable");
|
||||
}
|
||||
retObject.insert("details", output.join("\n") ); //full details of the check for updates
|
||||
retObject.insert("last_check",QFileInfo(UP_UPFILE).lastModified().toString(Qt::ISODate) );
|
||||
if(QFile::exists(UP_UPFILE)){
|
||||
retObject.insert("last_check",QFileInfo(UP_UPFILE).lastModified().toString(Qt::ISODate) );
|
||||
}else if(QFile::exists(UP_UPFILE_ERR)){
|
||||
retObject.insert("last_check",QFileInfo(UP_UPFILE_ERR).lastModified().toString(Qt::ISODate) );
|
||||
}
|
||||
return retObject;
|
||||
}
|
||||
|
||||
void Update::saveCheckUpdateLog(QString log){
|
||||
QStringList output = log.split("\n");
|
||||
qDebug() << "Got Check Update Log:" << log << output;
|
||||
while(!output.isEmpty() && output.last().simplified().isEmpty()){ output.removeLast(); }
|
||||
if(output.isEmpty()){ return; }
|
||||
if(!output.last().contains("ERROR:")){ //make sure there was network access available first - otherwise let it try again soon
|
||||
General::writeTextFile(UP_UPFILE, output); //save this check for later "fast" updates
|
||||
if(QFile::exists(UP_UPFILE_ERR)){ QFile::remove(UP_UPFILE_ERR); } //remove any previous failed log
|
||||
}else{
|
||||
General::writeTextFile(UP_UPFILE_ERR,output); //save this error return for later
|
||||
if(QFile::exists(UP_UPFILE)){ QFile::remove(UP_UPFILE); } //remove any previous good log
|
||||
}
|
||||
}
|
||||
|
||||
// List available branches we can switch to
|
||||
QJsonObject Update::listBranches() {
|
||||
QJsonObject retObject;
|
||||
@@ -174,50 +209,23 @@ QJsonObject Update::listBranches() {
|
||||
QJsonObject Update::startUpdate(QJsonObject jsin) {
|
||||
QJsonObject retObject;
|
||||
|
||||
QStringList keys = jsin.keys();
|
||||
if (! keys.contains("target") ) {
|
||||
retObject.insert("error", "Missing required key 'target'");
|
||||
//Quick check to ensure the tool is available
|
||||
QString tool = "/usr/local/bin/pc-updatemanager";
|
||||
QStringList flags; flags << "pkgupdate";
|
||||
if(!QFile::exists(tool)){
|
||||
return retObject;
|
||||
}
|
||||
// Save the target
|
||||
QString target;
|
||||
target = jsin.value("target").toString();
|
||||
|
||||
QString flags;
|
||||
if ( target == "chbranch" ) {
|
||||
if (! keys.contains("branch") ) {
|
||||
retObject.insert("error", "Missing required key 'branch'");
|
||||
return retObject;
|
||||
}
|
||||
flags = "chbranch " + jsin.value("branch").toString();
|
||||
} else if ( target == "pkgupdate" ) {
|
||||
flags = "pkgupdate";
|
||||
/* } else if ( target == "fbsdupdate" ) {
|
||||
flags = "fbsdupdate";
|
||||
} else if ( target == "fbsdupdatepkgs" ) {
|
||||
flags = "fbsdupdatepkgs";*/
|
||||
} else if ( target == "standalone" ) {
|
||||
if (! keys.contains("tag") ) {
|
||||
retObject.insert("error", "Missing required key 'tag'");
|
||||
return retObject;
|
||||
}
|
||||
flags = "install " + jsin.value("tag").toString();
|
||||
} else {
|
||||
// Ruh-roh
|
||||
retObject.insert("error", "Unknown target key: " + target);
|
||||
return retObject;
|
||||
}
|
||||
|
||||
// Create a unique ID for this queued action
|
||||
QString ID = QUuid::createUuid().toString();
|
||||
|
||||
// Queue the update action
|
||||
DISPATCHER->queueProcess("sysadm_update_runupdates::"+ID, "pc-updatemanager " + flags);
|
||||
|
||||
DISPATCHER->queueProcess("sysadm_update_runupdates::"+ID, tool+" "+ flags.join(" "));
|
||||
|
||||
if(QFile::exists(UP_UPFILE)){ QFile::remove(UP_UPFILE); } //ensure the next fast update does a full check
|
||||
|
||||
|
||||
// Return some details to user that the action was queued
|
||||
retObject.insert("command", "pc-updatemanger " + flags);
|
||||
retObject.insert("command", tool+" "+ flags.join(" "));
|
||||
retObject.insert("comment", "Task Queued");
|
||||
retObject.insert("queueid", ID);
|
||||
return retObject;
|
||||
@@ -247,11 +255,19 @@ QJsonObject Update::stopUpdate() {
|
||||
return ret;
|
||||
}
|
||||
|
||||
QJsonObject Update::applyUpdates(){
|
||||
QJsonObject ret;
|
||||
if(QFile::exists("/usr/local/sbin/pc-updatemanager")){
|
||||
QProcess::startDetached("pc-updatemanager startupdate");
|
||||
}
|
||||
ret.insert("result","rebooting to complete updates");
|
||||
return ret;
|
||||
}
|
||||
//SETTINGS OPTIONS
|
||||
QJsonObject Update::readSettings(){
|
||||
QJsonObject ret;
|
||||
QStringList knownsettings;
|
||||
knownsettings << "PACKAGE_SET" << "PACKAGE_URL" << "AUTO_UPDATE" << "MAXBE" << "AUTO_UPDATE_REBOOT";// << "CDN_TYPE";
|
||||
knownsettings << "PACKAGE_SET" << "PACKAGE_URL" << "AUTO_UPDATE" << "MAXBE" << "AUTO_UPDATE_REBOOT" << "CDN_TYPE";
|
||||
|
||||
QStringList info = General::readTextFile(UP_CONFFILE);
|
||||
for(int i=0; i<info.length(); i++){
|
||||
@@ -269,7 +285,7 @@ QJsonObject Update::writeSettings(QJsonObject obj){
|
||||
QJsonObject ret;
|
||||
//Check inputs
|
||||
QStringList knownsettings;
|
||||
knownsettings << "PACKAGE_SET" << "PACKAGE_URL" << "AUTO_UPDATE" << "MAXBE" << "AUTO_UPDATE_REBOOT";// << "CDN_TYPE";
|
||||
knownsettings << "PACKAGE_SET" << "PACKAGE_URL" << "AUTO_UPDATE" << "MAXBE" << "AUTO_UPDATE_REBOOT" << "CDN_TYPE";
|
||||
QStringList keys = obj.keys();
|
||||
QStringList vals;
|
||||
bool clearlastCheck = false;
|
||||
@@ -306,6 +322,7 @@ QJsonObject Update::writeSettings(QJsonObject obj){
|
||||
ret.insert("result","success");
|
||||
if(clearlastCheck){
|
||||
if(QFile::exists(UP_UPFILE)){ QFile::remove(UP_UPFILE); } //ensure the next fast update does a full check
|
||||
if(QFile::exists(UP_UPFILE_ERR)){ QFile::remove(UP_UPFILE_ERR); } //ensure the next fast update does a full check
|
||||
}
|
||||
}else{
|
||||
ret.insert("result","error");
|
||||
|
||||
@@ -19,10 +19,13 @@ public:
|
||||
static QDateTime rebootRequiredSince();
|
||||
//Listing routines
|
||||
static QJsonObject checkUpdates(bool fast = false);
|
||||
static void saveCheckUpdateLog(QString); //Internal for Dispatcher process usage - do not expose to public API
|
||||
|
||||
static QJsonObject listBranches();
|
||||
//Start/stop update routine
|
||||
static QJsonObject startUpdate(QJsonObject);
|
||||
static QJsonObject stopUpdate();
|
||||
static QJsonObject applyUpdates();
|
||||
//Read/write update settings
|
||||
static QJsonObject readSettings();
|
||||
static QJsonObject writeSettings(QJsonObject);
|
||||
@@ -31,7 +34,7 @@ public:
|
||||
static QJsonObject readLog(QJsonObject);
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -40,3 +40,10 @@ INSTALLS += target scripts
|
||||
QMAKE_LIBDIR = /usr/local/lib/qt5 /usr/local/lib
|
||||
INCLUDEPATH += /usr/local/include
|
||||
LIBS += -L/usr/local/lib -lpam -lutil -lssl -lcrypto
|
||||
|
||||
#Some conf to redirect intermediate stuff in separate dirs
|
||||
UI_DIR=./.build/ui/
|
||||
MOC_DIR=./.build/moc/
|
||||
OBJECTS_DIR=./.build/obj
|
||||
RCC_DIR=./.build/rcc
|
||||
QMAKE_DISTCLEAN += -r ./.build
|
||||
|
||||
Reference in New Issue
Block a user