mirror of
https://github.com/outbackdingo/sysadm.git
synced 2026-01-28 02:20:18 +00:00
Compare commits
80 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d018263559 | ||
|
|
b7fa03d2b8 | ||
|
|
f467df4133 | ||
|
|
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 | ||
|
|
2105b59780 | ||
|
|
6392cc362f | ||
|
|
dfb6e1b6a6 | ||
|
|
cad95cbec8 | ||
|
|
97f82a06d8 |
107
README.md
107
README.md
@@ -13,6 +13,23 @@
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
**General Project Information: Table of Contents**
|
||||
|
||||
- [General TrueOS Information](#gentrosinfo)
|
||||
- [TrueOS Project Documentation](#docs)
|
||||
- [TrueOS Handbook](#trueosdoc)
|
||||
- [Lumina Handbook](#luminadoc)
|
||||
- [SysAdm Handbooks](#sysadmdoc)
|
||||
- [Filing Issues or Feature Requests](#fileissues)
|
||||
- [Community Channels](#community)
|
||||
- [Discourse](#discourse)
|
||||
- [Gitter](#gitter)
|
||||
- [IRC](#irc)
|
||||
- [Subreddit](#subreddit)
|
||||
- [Social Media](#socialmedia)
|
||||
|
||||
<!-- END GENERAL INFO TOC -->
|
||||
|
||||
# SysAdm
|
||||
|
||||
Official repo for TrueOS' sysadm middleware WebSocket & REST server
|
||||
@@ -105,3 +122,93 @@ have verified the functionality of your new call you should add the output <br /
|
||||
of the test script (either from copy-n-paste, or from the file /tmp/api-response) <br />
|
||||
to your commit. (This will allow us to document the new call / class)
|
||||
|
||||
# General TrueOS Information <a name="gentrosinfo"></a>
|
||||
|
||||
This section describes where you can find more information about TrueOS and its related projects, file new issues on GitHub, and converse with other users or contributors to the project.
|
||||
|
||||
## TrueOS Project Documentation <a name="docs"></a>
|
||||
|
||||
A number of [Sphinx](http://www.sphinx-doc.org/en/stable/) generated reStructuredText handbooks are available to introduce you to the TrueOS, Lumina, and SysAdm projects. These handbooks are open source, and users are always encouraged to open GitHub issues or fix any errors they find in the documentation.
|
||||
|
||||
### TrueOS Handbook <a name="trueosdoc"></a>
|
||||
|
||||
The [TrueOS User Guide](https://www.trueos.org/handbook/trueos.html) is a comprehensive guide to install TrueOS, along with post-installation configuration help, recommendations for useful utilities and applications, and a help and support section containing solutions for common issues and links to community and development chat channels for uncommon issues. There is also a chapter describing the experimental TrueOS Pico project and links to the Lumina and SysAdm documentation. All TrueOS documentation is hosted on the [TrueOS website](https://www.trueos.org).
|
||||
|
||||
### Lumina Handbook <a name="luminadoc"></a>
|
||||
|
||||
The Lumina Desktop Environment has its own [handbook](https://lumina-desktop.org/handbook/), hosted on the [Lumina Website](https://lumina-desktop.org). This handbook contains brief installation instructions. However, due to the highly customizable nature of Lumina, the focus of the handbook lies mainly in documenting all user configurable settings. Each option is typically described in detail, with both text and screenshots. Finally, the suite of unique Qt5 utilities included with Lumina are also documented.
|
||||
|
||||
TrueOS users are encouraged to review the Lumina documentation, as the Lumina Desktop Environment is installed by default with TrueOS.
|
||||
|
||||
### SysAdm Handbooks <a name="sysadmdoc"></a>
|
||||
|
||||
Due to complexity of this project, SysAdm documentation is split into three different guides:
|
||||
|
||||
1. **API Reference Guide** (https://api.sysadm.us/getstarted.html)
|
||||
|
||||
The Application Programming Interface (API) Reference Guide is a comprehensive library of all API calls and WebSocket requests for SysAdm. In addition to documenting all SysAdm subsystems and classes, the guide provides detailed examples of requests and responses, authentication, and SSL certificate management. This guide is constantly updated, ensuring it provides accurate information at all times.
|
||||
|
||||
2. **Client Handbook** (https://sysadm.us/handbook/client/)
|
||||
|
||||
The SysAdm Client handbook documents all aspects of the SysAdm client, as well as describing of the PC-BSD system utilities is replaces. Detailed descriptions of utilities such as Appcafe, Life Preserver, and the Boot Environment Manager are contained here, as well as a general guide to using these utilities. TrueOS users are encouraged to reference this guide, as the SysAdm client is included with TrueOS.
|
||||
|
||||
3. **Server Handbook** (https://sysadm.us/handbook/server/introduction.html)
|
||||
|
||||
The Server handbook is a basic installation guide, walking new users through the process of initializing SysAdm with a bridge and server connection.
|
||||
|
||||
## Filing Issues or Feature Requests <a name="fileissues"></a>
|
||||
|
||||
Due to the number of repositories under the TrueOS "umbrella", the TrueOS Project consolidates its issue trackers into a few repositories:
|
||||
|
||||
* [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.
|
||||
* [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.
|
||||
* [trueos-website](https://github.com/trueos/trueos-website) : Issues involving any of the TrueOS Project websites:
|
||||
- https://www.lumina-desktop.org
|
||||
- https://www.trueos.org
|
||||
- https://www.sysadm.us
|
||||
|
||||
The TrueOS handbook has detailed instructions to help you report a bug (https://www.trueos.org/handbook/helpsupport.html#report-a-bug). It is recommended to refer to these instructions when creating new GitHub issues. Better bug reports usually result in faster fixes!
|
||||
|
||||
To request a feature, open a new issue in one of the related GitHub issue repositories and begin the title with *Feature Request:*.
|
||||
|
||||
## Community Channels <a name="community"></a>
|
||||
|
||||
The TrueOS community has a wide variety of chat channels and forum options available for users to interact with not only each other, but contributors to the project and the core development team too.
|
||||
|
||||
### Discourse <a name="discourse"></a>
|
||||
|
||||
TrueOS has a [Discourse channel](https://discourse.trueos.org/) managed concurrently with the TrueOS Subreddit. New users need to sign up with Discourse in order to create posts, but it is possible to view posts without an account.
|
||||
|
||||
### Gitter <a name="gitter"></a>
|
||||
|
||||
The TrueOS Project uses Gitter to provide real-time chat and collaboration with TrueOS users and developers. Gitter does not require an application to use, but does require a login using either an existing GitHub or Twitter account.
|
||||
|
||||
To access the TrueOS Gitter community, point a web browser to https://gitter.im/trueos.
|
||||
|
||||
Gitter also maintains a full archive of the chat history. This means lengthy conversations about hardware issues or workarounds are always available for reference. To access the Gitter archive, navigate to the desired TrueOS room’s archive. For example, here is the address of the TrueOS Lobby archive: https://gitter.im/trueos/Lobby/archives.
|
||||
|
||||
### IRC <a name="irc"></a>
|
||||
|
||||
Like many open source projects, TrueOS has an Internet Relay Chat (IRC) channel so users can chat and get help in real time. To get connected, use this information in your IRC client:
|
||||
|
||||
* Server name: irc.freenode.net
|
||||
* Channel name: #trueos (note the # is required)
|
||||
|
||||
### Subreddit <a name="subreddit"></a>
|
||||
|
||||
The TrueOS Project also has a [Subreddit](https://www.reddit.com/r/TrueOS/) for users who prefer to use Reddit to ask questions and to search for or post how-tos. A Reddit account is not required in order to read the Subreddit, but it is necessary to create a login account to submit or comment on posts.
|
||||
|
||||
## Social Media <a name="socialmedia"></a>
|
||||
|
||||
The TrueOS Project also maintains a number of social media accounts you can watch:
|
||||
|
||||
* Facebook: https://www.facebook.com/groups/4210443834/
|
||||
* Linkedin: http://www.linkedin.com/groups?gid=1942544
|
||||
* TrueOS Blog: https://www.trueos.org/blog/
|
||||
* Twitter: https://twitter.com/TrueOS_Project/
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
USES= pkgconfig tar:xz qmake
|
||||
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
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
Sysadm API server and websocket daemon
|
||||
SysAdm is a FreeBSD-based service which provides a stable API for
|
||||
administrating FreeBSD and TrueOS systems. It is composed of a
|
||||
headless daemon that runs in the background on any system to be
|
||||
administrated, providing both web socket and REST network services.
|
||||
|
||||
WWW: https://github.com/pcbsd/sysadm
|
||||
WWW: https://sysadm.us
|
||||
|
||||
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();
|
||||
@@ -159,11 +159,12 @@ void EventWatcher::CheckLogFiles(){
|
||||
void EventWatcher::ReadLPLogFile(){
|
||||
//Open/Read any new info in the file
|
||||
QFile LPlogfile(LPLOG);
|
||||
if( !LPlogfile.exists() ){ return; }
|
||||
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 +175,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 +244,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(){
|
||||
@@ -260,7 +260,8 @@ void EventWatcher::ReadLPRepFile(){
|
||||
QString repTotK = CONFIG->value("internal/"+QString(WS_MODE ? "ws" : "tcp")+"/lp-rep-totk","").toString();
|
||||
QString lastSize = CONFIG->value("internal/"+QString(WS_MODE ? "ws" : "tcp")+"/lp-rep-lastsize","").toString();
|
||||
//Open/Read any new info in the file
|
||||
QFile LPlogfile(LPLOG);
|
||||
QFile LPlogfile(tmpLPRepFile);
|
||||
if( !LPlogfile.exists() ){ return; }
|
||||
if( !LPlogfile.open(QIODevice::ReadOnly) ){ return; } //could not open file
|
||||
QTextStream STREAM(&LPlogfile);
|
||||
qint64 LPrep_pos = CONFIG->value("internal/"+QString(WS_MODE ? "ws" : "tcp")+"/lp-rep-pos",0).toLongLong();
|
||||
@@ -268,7 +269,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 +284,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 +344,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 +370,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 +386,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,25 +371,114 @@ 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 contents = General::readTextFile(confdir.absoluteFilePath(confs[i]));
|
||||
//Scan for any comments on the top of the file and remove them
|
||||
for(int l=0; l<contents.length(); l++){
|
||||
if(contents[l].isEmpty() || contents[l].startsWith("#")){ contents.removeAt(l); l--; }
|
||||
else{ break; }
|
||||
}
|
||||
QStringList repoinfo = contents.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 = true;
|
||||
if(enabled.contains("no") || enabled.contains("false")){ isEnabled = false; }
|
||||
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
|
||||
QProcess::execute("pkg update");
|
||||
DProcess* proc = DISPATCHER->queueProcess(Dispatcher::PKG_QUEUE, "internal_sysadm_pkg_repo_update_sync", "pkg update");
|
||||
proc->waitForFinished();
|
||||
return list_repos(true); //try again recursively (will not try to update again)
|
||||
}
|
||||
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)
|
||||
//=================
|
||||
@@ -381,7 +511,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){
|
||||
@@ -396,7 +526,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){
|
||||
@@ -411,7 +541,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;
|
||||
}
|
||||
|
||||
//==================
|
||||
@@ -429,7 +559,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(){
|
||||
@@ -443,7 +573,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(){
|
||||
@@ -471,7 +601,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(){
|
||||
@@ -485,5 +615,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
|
||||
|
||||
@@ -37,18 +37,17 @@ void MessageOutput(QtMsgType type, const QMessageLogContext &context, const QStr
|
||||
break;
|
||||
case QtWarningMsg:
|
||||
txt = QString("WARNING: %1").arg(msg);
|
||||
txt += "\n Context: "+QString(context.file)+" Line: "+QString(context.line)+" Function: "+QString(context.function);
|
||||
break;
|
||||
case QtCriticalMsg:
|
||||
txt = QString("CRITICAL: %1").arg(msg);
|
||||
txt += "\n Context: "+QString(context.file)+" Line: "+QString(context.line)+" Function: "+QString(context.function);
|
||||
break;
|
||||
case QtFatalMsg:
|
||||
txt = QString("FATAL: %1").arg(msg);
|
||||
txt += "\n Context: "+QString(context.file)+" Line: "+QString(context.line)+" Function: "+QString(context.function);
|
||||
break;
|
||||
}
|
||||
|
||||
if( type!=QtDebugMsg && !QString(context.file).isEmpty() ){
|
||||
txt += "\n Context: "+QString(context.file)+" Line: "+QString(context.line)+" Function: "+QString(context.function);
|
||||
}
|
||||
QTextStream out(&logfile);
|
||||
out << txt;
|
||||
if(!txt.endsWith("\n")){ out << "\n"; }
|
||||
@@ -79,7 +78,6 @@ qDebug() << " \"bridge_export_key [file]\": Export the public SSL key the serve
|
||||
|
||||
int main( int argc, char ** argv )
|
||||
{
|
||||
|
||||
//Check whether running as root
|
||||
if( getuid() != 0){
|
||||
qDebug() << "sysadm-server must be started as root!";
|
||||
@@ -121,7 +119,7 @@ int main( int argc, char ** argv )
|
||||
QSslCertificate cert(&cfile);
|
||||
cfile.close();
|
||||
if(!cert.isNull()){
|
||||
if(i+1<argc){
|
||||
if(i+1<argc){
|
||||
i++; QString filepath = argv[i];
|
||||
QFile outfile(filepath);
|
||||
outfile.open(QIODevice::WriteOnly | QIODevice::Truncate);
|
||||
@@ -150,11 +148,11 @@ int main( int argc, char ** argv )
|
||||
if(!file.open(QIODevice::ReadOnly)){ qDebug() << "Could not open file:" << file.fileName(); }
|
||||
else{
|
||||
QByteArray enc_key;
|
||||
if(file.fileName().endsWith(".crt")){
|
||||
QSslCertificate cert(&file, QSsl::Pem);
|
||||
if(file.fileName().endsWith(".crt")){
|
||||
QSslCertificate cert(&file, QSsl::Pem);
|
||||
if(!cert.isNull()){ enc_key = cert.publicKey().toPem(); }
|
||||
}else if(file.fileName().endsWith(".key")){
|
||||
QSslKey key( &file, QSsl::Rsa, QSsl::Pem, QSsl::PublicKey);
|
||||
}else if(file.fileName().endsWith(".key")){
|
||||
QSslKey key( &file, QSsl::Rsa, QSsl::Pem, QSsl::PublicKey);
|
||||
if(!key.isNull()){ enc_key = key.toPem(); }
|
||||
}else{
|
||||
qDebug() << "Error: Unknown file type (need .crt or .key file)";
|
||||
@@ -172,13 +170,13 @@ int main( int argc, char ** argv )
|
||||
if(QFile::exists(key)){
|
||||
QFile file(key);
|
||||
QByteArray pubkey;
|
||||
if(file.open(QIODevice::ReadOnly)){
|
||||
QSslKey sslkey( &file, QSsl::Rsa, QSsl::Pem, QSsl::PublicKey);
|
||||
if(file.open(QIODevice::ReadOnly)){
|
||||
QSslKey sslkey( &file, QSsl::Rsa, QSsl::Pem, QSsl::PublicKey);
|
||||
if(!key.isNull()){ pubkey = sslkey.toPem(); }
|
||||
else{ qDebug() << "Invalid Key file:" << file.fileName(); ok = false; }
|
||||
file.close();
|
||||
}else{ qDebug() << "Could not open file:" << file.fileName(); ok = false; }
|
||||
}
|
||||
}
|
||||
if(ok){ ok = AuthorizationManager::RegisterCertificateInternal(user, key, nickname, email); }
|
||||
if(ok){ qDebug() << "Key Added" << user << nickname; }
|
||||
else{ qDebug() << "Could not add key"; } */
|
||||
@@ -193,18 +191,20 @@ int main( int argc, char ** argv )
|
||||
|
||||
QCoreApplication a(argc, argv);
|
||||
//Now load the config file
|
||||
QStringList conf = ReadFile(CONFFILE).split("\n");
|
||||
QString conf_file = CONFFILE;
|
||||
if( !QFile::exists(conf_file) ){ conf_file.append(".dist"); } //no settings - use the default config
|
||||
QStringList conf = ReadFile(conf_file).split("\n");
|
||||
if(!conf.filter("[internal]").isEmpty()){
|
||||
//Older QSettings file - move it to the new location
|
||||
if(QFile::exists(SETTINGSFILE)){ QFile::remove(SETTINGSFILE); } //remove the new/empty settings file
|
||||
QFile::rename(CONFFILE, SETTINGSFILE);
|
||||
QFile::copy(conf_file, SETTINGSFILE);
|
||||
CONFIG->sync(); //re-sync settings structure
|
||||
conf.clear(); //No config yet
|
||||
}
|
||||
//Load the settings from the config file
|
||||
// - port number
|
||||
if(port==0){
|
||||
if(websocket){
|
||||
if(websocket){
|
||||
int index = conf.indexOf(QRegExp("PORT=*",Qt::CaseSensitive,QRegExp::Wildcard));
|
||||
bool ok = false;
|
||||
if(index>=0){ port = conf[index].section("=",1,1).toInt(&ok); }
|
||||
@@ -213,7 +213,7 @@ int main( int argc, char ** argv )
|
||||
int index = conf.indexOf(QRegExp("PORT_REST=*",Qt::CaseSensitive,QRegExp::Wildcard));
|
||||
bool ok = false;
|
||||
if(index>=0){ port = conf[index].section("=",1,1).toInt(&ok); }
|
||||
if(port<=0 || !ok){ port = PORTNUMBER; }
|
||||
if(port<=0 || !ok){ port = PORTNUMBER; }
|
||||
}
|
||||
}
|
||||
// - Blacklist options
|
||||
@@ -238,7 +238,7 @@ int main( int argc, char ** argv )
|
||||
rg = QRegExp("BRIDGE_CONNECTIONS_ONLY=*",Qt::CaseSensitive,QRegExp::Wildcard);
|
||||
if(!conf.filter(rg).isEmpty()){
|
||||
BRIDGE_ONLY = conf.filter(rg).first().section("=",1,1).simplified().toLower()=="true";
|
||||
}
|
||||
}
|
||||
|
||||
//Setup the log file
|
||||
LogManager::checkLogDir(); //ensure the logging directory exists
|
||||
@@ -254,18 +254,17 @@ int main( int argc, char ** argv )
|
||||
}
|
||||
logfile.open(QIODevice::WriteOnly | QIODevice::Append);
|
||||
qInstallMessageHandler(MessageOutput);
|
||||
|
||||
//Connect the background classes
|
||||
QObject::connect(DISPATCHER, SIGNAL(DispatchEvent(QJsonObject)), EVENTS, SLOT(DispatchEvent(QJsonObject)) );
|
||||
QObject::connect(DISPATCHER, SIGNAL(DispatchStarting(QString)), EVENTS, SLOT(DispatchStarting(QString)) );
|
||||
|
||||
|
||||
//Create the daemon
|
||||
qDebug() << "Starting the PC-BSD sysadm server...." << (websocket ? "(WebSocket)" : "(TCP)");
|
||||
WebServer *w = new WebServer();
|
||||
qDebug() << "Starting the sysadm server...." << (websocket ? "(WebSocket)" : "(TCP)");
|
||||
WebServer *w = new WebServer();
|
||||
//Start the daemon
|
||||
int ret = 1; //error return value
|
||||
if( w->startServer(port, websocket) ){
|
||||
qDebug() << " - Configuration File:" << CONFIG->fileName();
|
||||
//qDebug() << " - Configuration File:" << CONFIG->fileName();
|
||||
QThread TBACK, TBACK2;
|
||||
EVENTS->moveToThread(&TBACK);
|
||||
DISPATCHER->moveToThread(&TBACK2);
|
||||
@@ -283,7 +282,7 @@ int main( int argc, char ** argv )
|
||||
//Cleanup any globals
|
||||
delete CONFIG;
|
||||
logfile.close();
|
||||
|
||||
|
||||
//Return
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -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