Add a new API call/system: sysadm/pkg.

This system reads the pkg database directly and returns any relevant information about the pkgs requested.
Note: The "repo" input is optional (defaults to "local"), and the "pkg_origins" will become optional here soon as well (going to make it default to listing all pkgs if the pkg_origins variable is missing/empty).

REST Request:
-------------------------------
PUT /sysadm/pkg
{
   "pkg_origins" : [
      "x11/lumina"
   ],
   "repo" : "local",
   "action" : "pkg_info"
}

WebSocket Request:
-------------------------------
{
   "name" : "pkg",
   "namespace" : "sysadm",
   "args" : {
      "repo" : "local",
      "action" : "pkg_info",
      "pkg_origins" : [
         "x11/lumina"
      ]
   },
   "id" : "fooid"
}

Response:
-------------------------------
{
  "args": {
    "pkg_info": {
      "x11/lumina": {
        "arch": "FreeBSD:11:amd64",
        "automatic": "0",
        "comment": "Lumina Desktop Environment",
        "dep_formula": "",
        "dependencies": [
          "x11-toolkits/qt5-gui",
          "x11/qt5-x11extras",
          "x11-wm/fluxbox",
          "x11/libXdamage",
          "devel/qt5-linguist",
          "x11/numlockx",
          "devel/qt5-buildtools",
          "multimedia/qt5-multimedia",
          "graphics/qt5-svg",
          "x11/xbrightness",
          "x11/xorg",
          "devel/desktop-file-utils",
          "devel/qt5-concurrent",
          "x11/libX11",
          "net/qt5-network",
          "x11-themes/fluxbox-tenr-styles-pack",
          "x11-themes/kde4-icons-oxygen",
          "devel/qt5-core",
          "x11/xscreensaver",
          "multimedia/gstreamer1-plugins-core",
          "graphics/qt5-imageformats"
        ],
        "desc": "Lumina-DE is a lightweight, BSD licensed desktop environment,\ndesigned specifically for use on FreeBSD\n\nWWW: http://lumina-desktop.org",
        "files": [
          "/usr/local/share/licenses/lumina-0.8.8_2,1/catalog.mk",
          "/usr/local/share/licenses/lumina-0.8.8_2,1/LICENSE",
          "/usr/local/share/licenses/lumina-0.8.8_2,1/BSD3CLAUSE",
          "/usr/local/bin/Lumina-DE",
          "/usr/local/bin/lumina-config",
          "/usr/local/bin/lumina-fileinfo",
          "/usr/local/bin/lumina-fm",
          "/usr/local/bin/lumina-info",
          "/usr/local/bin/lumina-open",
          "/usr/local/bin/lumina-screenshot",
          "/usr/local/bin/lumina-search",
          "/usr/local/bin/lumina-xconfig",
          "/usr/local/etc/luminaDesktop.conf.dist",
          "/usr/local/include/LuminaOS.h",
          "/usr/local/include/LuminaSingleApplication.h",
          "/usr/local/include/LuminaThemes.h",
          "/usr/local/include/LuminaUtils.h",
          "/usr/local/include/LuminaX11.h",
          "/usr/local/include/LuminaXDG.h",
          "/usr/local/lib/libLuminaUtils.so",
          "/usr/local/lib/libLuminaUtils.so.1",
          "/usr/local/lib/libLuminaUtils.so.1.0",
          "/usr/local/lib/libLuminaUtils.so.1.0.0",
          "/usr/local/share/Lumina-DE/Login.ogg",
          "/usr/local/share/Lumina-DE/Logout.ogg",
          "/usr/local/share/Lumina-DE/colors/Black.qss.colors",
          "/usr/local/share/Lumina-DE/colors/Blue-Light.qss.colors",
          "/usr/local/share/Lumina-DE/colors/Grey-Dark.qss.colors",
          "/usr/local/share/Lumina-DE/colors/Lumina-Glass.qss.colors",
          "/usr/local/share/Lumina-DE/colors/Lumina-Gold.qss.colors",
          "/usr/local/share/Lumina-DE/colors/Lumina-Green.qss.colors",
          "/usr/local/share/Lumina-DE/colors/Lumina-Purple.qss.colors",
          "/usr/local/share/Lumina-DE/colors/Lumina-Red.qss.colors",
          "/usr/local/share/Lumina-DE/colors/PCBSD10-Default.qss.colors",
          "/usr/local/share/Lumina-DE/colors/Solarized-Dark.qss.colors",
          "/usr/local/share/Lumina-DE/colors/Solarized-Light.qss.colors",
          "/usr/local/share/Lumina-DE/desktop-background.jpg",
          "/usr/local/share/Lumina-DE/fluxbox-init-rc",
          "/usr/local/share/Lumina-DE/fluxbox-keys",
          "/usr/local/share/Lumina-DE/luminaDesktop.conf",
          "/usr/local/share/Lumina-DE/quickplugins/quick-sample.qml",
          "/usr/local/share/Lumina-DE/themes/Lumina-default.qss.template",
          "/usr/local/share/Lumina-DE/themes/None.qss.template",
          "/usr/local/share/applications/lumina-fm.desktop",
          "/usr/local/share/applications/lumina-info.desktop",
          "/usr/local/share/applications/lumina-screenshot.desktop",
          "/usr/local/share/applications/lumina-search.desktop",
          "/usr/local/share/applications/lumina-support.desktop",
          "/usr/local/share/pixmaps/Insight-FileManager.png",
          "/usr/local/share/pixmaps/Lumina-DE.png",
          "/usr/local/share/wallpapers/Lumina-DE/Lumina_Wispy_gold.jpg",
          "/usr/local/share/wallpapers/Lumina-DE/Lumina_Wispy_green.jpg",
          "/usr/local/share/wallpapers/Lumina-DE/Lumina_Wispy_purple.jpg",
          "/usr/local/share/wallpapers/Lumina-DE/Lumina_Wispy_red.jpg",
          "/usr/local/share/xsessions/Lumina-DE.desktop"
        ],
        "flatsize": "12324767",
        "icon": "\\\"http://www.pcbsd.org/appcafe/icons/x11_lumina.png\\\"",
        "id": "2541",
        "licenselogic": "1",
        "licenses": [
          "BSD3CLAUSE"
        ],
        "locked": "0",
        "maintainer": "kmoore@FreeBSD.org",
        "manifestdigest": "2$0$4ypg5zrco9upyuioczmo3uwbtdd5yart7xuit6fx3gjrn1k979qb",
        "message": "[{\"message\":\"The Lumina Desktop Environment has been installed!\\n\\nAn entry for for launching Lumina from a graphical login manager has already been added to the system, but if you with to start Lumina manually, you will need to do one of the following:\\n1) Put the line \\\"exec Lumina-DE\\\" at the end of your user's \\\"~/.xinitrc\\\" file before running startx\\n2) Wrap the Lumina binary call with an X initialization call: \\nExample: \\\"xinit ${PREFIX}/bin/Lumina-DE -- :0\\\"\\n\\nAlso note that the system-wide default settings for Lumina are contained in ${PREFIX}/etc/luminaDesktop.conf[.dist]. While it is possible to customize the desktop to the user's liking after logging in, you may want to adjust the default settings as necessary if there are multiple user accounts on this system.\"}]",
        "mtree_id": "",
        "name": "lumina",
        "options": {
          "MULTIMEDIA": "on",
          "PCBSD": "on"
        },
        "origin": "x11/lumina",
        "pkg_format_version": "",
        "prefix": "/usr/local",
        "repo_type": "binary",
        "repository": "pcbsd-major",
        "screen1": "\\\"http://www.pcbsd.org/appcafe/screenshots/x11/lumina/screen1.png\\\"",
        "shlibs_provided": [
          "libLuminaUtils.so.1"
        ],
        "shlibs_required": [
          "libxcb.so.1",
          "libxcb-composite.so.0",
          "libxcb-damage.so.0",
          "libXdamage.so.1",
          "libxcb-util.so.1",
          "libGL.so.1",
          "libQt5Core.so.5",
          "libxcb-image.so.0",
          "libxcb-icccm.so.4",
          "libxcb-ewmh.so.2",
          "libQt5Gui.so.5",
          "libQt5Network.so.5",
          "libQt5Widgets.so.5",
          "libQt5Concurrent.so.5",
          "libQt5Multimedia.so.5",
          "libQt5MultimediaWidgets.so.5",
          "libQt5Svg.so.5",
          "libQt5X11Extras.so.5"
        ],
        "time": "1458334158",
        "version": "0.8.8_2,1",
        "www": "http://lumina-desktop.org"
      }
    }
  },
  "id": "fooid",
  "name": "response",
  "namespace": "sysadm"
}
This commit is contained in:
Ken Moore
2016-03-25 12:03:38 -04:00
parent 4d2dc47837
commit 0b70897ce8
6 changed files with 207 additions and 3 deletions

View File

@@ -18,6 +18,7 @@
#include "library/sysadm-systemmanager.h"
#include "library/sysadm-update.h"
#include "library/sysadm-zfs.h"
#include "library/sysadm-pkg.h"
#include "syscache-client.h"
@@ -67,10 +68,16 @@ RestOutputStruct::ExitCode WebSocket::AvailableSubsystems(bool allaccess, QJsonO
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")){
out->insert("sysadm/pkg", "read/write");
}
// - Generic system information
out->insert("sysadm/systemmanager","read/write");
@@ -123,6 +130,8 @@ RestOutputStruct::ExitCode WebSocket::EvaluateBackendRequest(const RestInputStru
return EvaluateSysadmUpdateRequest(IN.args, out);
}else if(namesp=="sysadm" && name=="zfs"){
return EvaluateSysadmZfsRequest(IN.args, out);
}else if(namesp=="sysadm" && name=="pkg"){
return EvaluateSysadmPkgRequest(IN.args, out);
}else{
return RestOutputStruct::BADREQUEST;
}
@@ -681,3 +690,29 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmZfsRequest(const QJsonValue
return RestOutputStruct::OK;
}
// ==== SYSADM PKG API ====
RestOutputStruct::ExitCode WebSocket::EvaluateSysadmPkgRequest(const QJsonValue in_args, QJsonObject *out){
if(!in_args.isObject() || !in_args.toObject().contains("action") ){ return RestOutputStruct::BADREQUEST; }
//REQUIRED: "action"
QString act = in_args.toObject().value("action").toString();
//OPTIONAL: "repo" (uses local repo database by default)
QString repo = "local";
if(in_args.toObject().contains("repo")){ repo = in_args.toObject().value("repo").toString(); }
//OPTIONAL: "pkg_origins" (defaults to everything for listing functions)
QStringList pkgs;
if(in_args.toObject().contains("pkg_origins")){
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
if(act=="pkg_info"){
QJsonObject info = sysadm::PKG::pkg_info(pkgs, repo);
if(!pkgs.isEmpty()){ out->insert("pkg_info",info); }
}else{
//unknown action
return RestOutputStruct::BADREQUEST;
}
return RestOutputStruct::OK;
}

View File

@@ -70,6 +70,8 @@ private:
RestOutputStruct::ExitCode EvaluateSysadmUpdateRequest(const QJsonValue in_args, QJsonObject *out);
// -- sysadm ZFS API
RestOutputStruct::ExitCode EvaluateSysadmZfsRequest(const QJsonValue in_args, QJsonObject *out);
// -- sysadm PKG API
RestOutputStruct::ExitCode EvaluateSysadmPkgRequest(const QJsonValue in_args, QJsonObject *out);
private slots:
void sendReply(QString msg);

View File

@@ -13,7 +13,8 @@ HEADERS += $${PWD}/sysadm-global.h \
$${PWD}/sysadm-systemmanager.h\
$${PWD}/sysadm-update.h \
$${PWD}/sysadm-usermanager.h \
$${PWD}/sysadm-zfs.h
$${PWD}/sysadm-zfs.h \
$${PWD}/sysadm-pkg.h
SOURCES += $${PWD}/NetDevice.cpp \
$${PWD}/sysadm-general.cpp \
@@ -27,5 +28,6 @@ SOURCES += $${PWD}/NetDevice.cpp \
$${PWD}/sysadm-systemmanager.cpp \
$${PWD}/sysadm-update.cpp \
$${PWD}/sysadm-usermanager.cpp \
$${PWD}/sysadm-zfs.cpp
$${PWD}/sysadm-zfs.cpp \
$${PWD}/sysadm-pkg.cpp

View File

@@ -0,0 +1,139 @@
//===========================================
// PC-BSD source code
// Copyright (c) 2015, PC-BSD Software/iXsystems
// Available under the 3-clause BSD license
// See the LICENSE file for full details
//===========================================
#include "sysadm-general.h"
#include "sysadm-pkg.h"
#include "sysadm-global.h"
#include "globals.h"
using namespace sysadm;
// ==================
// INLINE FUNCTIONS
// ==================
//Get Option Name
inline QString option_from_id(QString id){
QSqlQuery q("SELECT option FROM option WHERE option_id = '"+id+"'");
while(q.next()){
return q.value("option").toString();
}
return ""; //nothing found
}
//Get Annotation (name/value - both use ID's)
inline QString anno_from_id(QString id){
QSqlQuery q("SELECT annotation FROM annotation WHERE annotation_id = '"+id+"'");
while(q.next()){
return q.value("annotation").toString();
}
return ""; //nothing found
}
//Get origin from package_id (for reverse lookups)
inline QStringList origins_from_package_ids(QStringList ids){
QSqlQuery q("SELECT origin FROM packages WHERE id = '"+ids.join("' OR id = '")+"'");
QStringList out;
while(q.next()){ out << q.value("origin").toString(); }
return out;
}
//Generic ID's -> Names function (known databases: users, groups, licenses, shlibs, categories )
inline QStringList names_from_ids(QStringList ids, QString db){
QSqlQuery q("SELECT name FROM "+db+" WHERE id = '"+ids.join("' OR id = '")+"'");
QStringList out;
while(q.next()){ out << q.value("name").toString(); }
return out;
}
// =================
// MAIN FUNCTIONS
// =================
QJsonObject PKG::pkg_info(QStringList origins, QString repo){
QJsonObject retObj;
//Open the local database
QSqlDatabase DB = QSqlDatabase::addDatabase("QSQLITE");
DB.setHostName("localhost");
if(repo=="local"){ DB.setDatabaseName( "/var/db/pkg/local.sqlite"); }
else{ DB.setDatabaseName( "/var/db/pkg/repo-"+repo+".sqlite"); }
DB.setConnectOptions("QSQLITE_OPEN_READONLY=1");
if( !DB.open() ){ return retObj; } //cannot open database
//Now do all the pkg info, one pkg origin at a time
QSqlQuery query("SELECT * FROM packages" +(origins.isEmpty() ? "" : " WHERE origin = '"+origins.join("' OR origin = '")+"'") );
//int id = query.record().indexOf("origin");
while(query.next()){
QJsonObject info;
QString id = query.value("id").toString(); //need this pkg id for later
QString origin = query.value("origin").toString(); //need the origin for later
//General info
for(int i=0; i<query.record().count(); i++){
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+"'");
while(q2.next()){
//include the annotations as base-level fields as well
info.insert( anno_from_id(q2.value("tag_id").toString()), anno_from_id(q2.value("value_id").toString()) );
}
//OPTIONS
QSqlQuery q3("SELECT option_id, value FROM pkg_option WHERE package_id = '"+id+"'");
QJsonObject options;
while(q3.next()){ options.insert(option_from_id(q3.value("option_id").toString()), q3.value("value").toString() ); }
if(!options.isEmpty()){ info.insert("options",options); }
//DEPENDENCIES
QSqlQuery q4("SELECT origin FROM deps WHERE package_id = '"+id+"'");
QStringList tmpList;
while(q4.next()){
tmpList << q4.value("origin").toString();
} //end deps query
if(!tmpList.isEmpty()){ info.insert("dependencies", QJsonArray::fromStringList(tmpList) ); }
//FILES
QSqlQuery q5("SELECT path FROM files WHERE package_id = '"+id+"'");
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+"'");
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)) ); }
//USERS
QSqlQuery q7("SELECT user_id FROM pkg_users WHERE package_id = '"+id+"'");
tmpList.clear();
while(q7.next()){ tmpList << q7.value("user_id").toString(); }
if(!tmpList.isEmpty()){ info.insert("users", QJsonArray::fromStringList(names_from_ids(tmpList, "users")) ); }
//GROUPS
QSqlQuery q8("SELECT group_id FROM pkg_groups WHERE package_id = '"+id+"'");
tmpList.clear();
while(q8.next()){ tmpList << q8.value("group_id").toString(); }
if(!tmpList.isEmpty()){ info.insert("groups", QJsonArray::fromStringList(names_from_ids(tmpList, "users")) ); }
//LICENSES
QSqlQuery q9("SELECT license_id FROM pkg_licenses WHERE package_id = '"+id+"'");
tmpList.clear();
while(q9.next()){ tmpList << q9.value("license_id").toString(); }
if(!tmpList.isEmpty()){ info.insert("licenses", QJsonArray::fromStringList(names_from_ids(tmpList, "licenses")) ); }
//SHARED LIBS (REQUIRED)
QSqlQuery q10("SELECT shlib_id FROM pkg_shlibs_required WHERE package_id = '"+id+"'");
tmpList.clear();
while(q10.next()){ tmpList << q10.value("shlib_id").toString(); }
if(!tmpList.isEmpty()){ info.insert("shlibs_required", QJsonArray::fromStringList(names_from_ids(tmpList, "shlibs")) ); }
//SHARED LIBS (PROVIDED)
QSqlQuery q11("SELECT shlib_id FROM pkg_shlibs_provided WHERE package_id = '"+id+"'");
tmpList.clear();
while(q11.next()){ tmpList << q11.value("shlib_id").toString(); }
if(!tmpList.isEmpty()){ info.insert("shlibs_provided", QJsonArray::fromStringList(names_from_ids(tmpList, "shlibs")) ); }
//CONFLICTS
QSqlQuery q12("SELECT conflict_id FROM pkg_conflicts WHERE package_id = '"+id+"'");
tmpList.clear();
while(q12.next()){ tmpList << q12.value("conflict_id").toString(); }
if(!tmpList.isEmpty()){ info.insert("conflicts", QJsonArray::fromStringList(origins_from_package_ids(tmpList)) ); }
//CONFIG FILES
QSqlQuery q13("SELECT path FROM config_files WHERE package_id = '"+id+"'");
tmpList.clear();
while(q13.next()){ tmpList << q13.value("path").toString(); }
if(!tmpList.isEmpty()){ info.insert("config_files", QJsonArray::fromStringList(tmpList) ); }
//Now insert this information into the main object
retObj.insert(origin,info);
} //end loop over pkg matches
DB.close();
return retObj;
}

View File

@@ -0,0 +1,26 @@
//===========================================
// PC-BSD source code
// Copyright (c) 2015, PC-BSD Software/iXsystems
// Available under the 3-clause BSD license
// See the LICENSE file for full details
//===========================================
#ifndef __PCBSD_LIB_UTILS_PKG_H
#define __PCBSD_LIB_UTILS_PKG_H
#include <QJsonObject>
#include "sysadm-global.h"
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlIndex>
#include <QSqlRecord>
#include <QSqlQuery>
namespace sysadm{
class PKG{
public:
static QJsonObject pkg_info(QStringList origins, QString repo);
};
} //end of sysadm namespace
#endif

View File

@@ -2,7 +2,7 @@ TEMPLATE = app
LANGUAGE = C++
CONFIG += qt warn_off release
QT = core network websockets concurrent
QT = core network websockets concurrent sql
HEADERS += globals.h globals-qt.h \
WebServer.h \