75 Commits

Author SHA1 Message Date
Ken Moore
d018263559 Reverse the pkg repo enabled logic.
default to enabled, look for no/false section to disable.
Seems to fix the issue with a repo config file not including the enabled section at all, but pkg still using it.
2019-05-23 19:51:51 -04:00
Ken Moore
b7fa03d2b8 Fix up sysadm to work with the corrupted/modified version of the pkg config files that sysup now auto-generates. 2018-12-11 09:32:29 -05:00
Ken Moore
f467df4133 Cleanup some config-file logic and other random file-handling 2018-11-28 08:15:31 -05:00
Ken Moore
694951384d Get the pkg repo detection systems up and running again. 2018-10-04 04:17:35 -04:00
Ken Moore
10d00d97d9 Update sysadm-update.cpp
Remove all reference to trueos-update from the pc-updatemanager update API class.
2018-09-25 12:43:29 -04:00
Ken Moore
32d74530ad Disable the sysadm/update API class unless pc-updatemanager is installed.
This system does not work with the newer "trueos-update" utility. We will need a new API backend for that tool.
2018-09-06 08:50:25 -04:00
Ken Moore
4b0d6334d1 Make sure that the beadm backend can run if beadm is in base 2018-09-05 10:29:39 -04:00
Ken Moore
ed2c244282 Fix up the port template to use the new Qt flavors system 2018-09-05 09:52:21 -04:00
Ken Moore
8ba0b72cd0 Fix up the paths where sysadm looks for pkg repo configs. 2018-09-05 09:20:06 -04:00
Ken Moore
67e687ba4c Update the sysadm/update API class:
Make it able to use "trueos-update" as well as "pc-updatemanager" for upgrades
2018-09-01 07:33:26 -04:00
Ken Moore
0fdcdb2c9b Update WebBackend.cpp
Fix the sysadm/pkg API class detection method to account for the new location of the "pkg" binary on TrueOS 18.06
Also fix the sysadm/update API class to look for the "trueos-update" utility.
2018-09-01 06:31:49 -04:00
Ken Moore
aab6f484ef Remove the IFM_[FDDI/TOKEN] usage in SysAdm. Removed from upstream FreeBSD. 2018-06-12 09:26:41 -04:00
Ken Moore
25177739c2 Clean up some dispatcher parsing routines for sourcectl 2018-04-19 08:51:34 -04:00
Ken Moore
25ca2d430d Cleanup the service manager backend a bit.
Make it more robust concerning OpenRC vs rc.d management.
2018-04-04 10:59:45 -04:00
Ken Moore
6630432922 Merge pull request #33 from OtacilioNeto/master
Fix 100% CPU usage bug when listing users under FreeBSD.
2018-03-19 08:14:37 -04:00
Otacílio
d7c4e29d9a Fix 100% CPU usage bug when listing users under FreeBSD. 2018-03-18 22:52:13 -03:00
Ken Moore
fd5504b304 Allow the "CDN_TYPE" option to be exposed via the update settings API calls now.
This allows an easy way to switch between IPFS and CDN types.
2018-02-05 09:58:46 -05:00
Ken Moore
b08d8461fc Put all the build files into a separate directory (keep source dir clean). 2018-02-05 09:53:56 -05:00
Ken Moore
c1be586f87 Fix up the creation of new configuration files in sysadm-general (setConfFileValue()). 2017-12-18 13:27:14 -05:00
Ken Moore
dd4ab15bb4 Merge branch 'master' of github.com:trueos/sysadm 2017-12-07 09:39:46 -05:00
Ken Moore
6b43bc6a29 Couple quick updates for handling failed update checks. 2017-12-07 09:39:18 -05:00
q5sys
d6147d70f4 update port maintainer 2017-11-24 15:43:43 -05:00
Ken Moore
77986e0312 Fix the update-required flag detection 2017-11-22 10:43:00 -05:00
Ken Moore
06e2f83902 Merge branch 'master' of github.com:trueos/sysadm 2017-11-22 10:28:44 -05:00
Ken Moore
42adfc6ca1 Couple minor tweaks for the sysadm server. 2017-11-22 10:28:21 -05:00
Ken Moore
f4a2459429 Change the internal reboot/update mechanism to use the new "applyUpdates()" function instead of just systemReboot(). 2017-11-17 16:02:39 -05:00
Ken Moore
c5bf944017 [API CHANGE] Add a new API call for sysadm/update: action="applyupdate".
This takes no other inputs, and returns the following:
{
"applyupdate" : {
  "result" : "rebooting to apply updates"
  }
}
2017-11-17 15:58:37 -05:00
q5sys
2157dbb6c2 add new library files 2017-11-02 11:52:07 -04:00
q5sys
d07658ee21 more api work for sourcectl 2017-11-02 11:48:25 -04:00
q5sys
355b47d93a cut old source source from systemmanager.cpp 2017-11-02 10:10:56 -04:00
q5sys
62655ebd41 backend work for source control api 2017-11-02 10:02:03 -04:00
Ken Moore
ce53715265 Make sure the system update check is done via a DISPATCHER process.
This prevent possible hangs in the main server thread from the health check, and also ensures that the system update check API call can now return a new "checkingforupdates" status.
2017-10-27 13:35:42 -04:00
Ken Moore
4247c3529a [API CHANGE] New API call: sysadm/moused, action="get_synaptics_options"
No other input arguments required.
This will return any additional options that are available through the "synaptics" driver system (typically used for laptops).

REST Request (example):
-------------------------------
PUT /sysadm/moused
{
   "action" : "get_synaptics_options"
}

WebSocket Request:
-------------------------------
{
   "id" : "fooid",
   "args" : {
      "action" : "get_synaptics_options"
   },
   "name" : "moused",
   "namespace" : "sysadm"
}

Response:
-------------------------------
{
  "args": {
    "get_synaptics_options": {
      "disable_touchpad": "false",
      "enable_synaptics": "false",
      "enable_two_finger_scroll": "false"
    }
  },
  "id": "fooid",
  "name": "response",
  "namespace": "sysadm"
}
2017-10-26 13:32:43 -04:00
Ken Moore
bb1ec413eb [API CHANGE] sysadm/moused, action="set_tap_to_click"
Inputs (at least one required):
"enable" = "true" or "false" : Turn tap to click on or off
"timeout" = "[positive integer]" : Modify the timeout value (0 is sometimes used to disable tap-to-click)

REST Request (example):
-------------------------------
PUT /sysadm/moused
{
   "timeout" : "0",
   "action" : "set_tap_to_click"
}

WebSocket Request:
-------------------------------
{
   "namespace" : "sysadm",
   "name" : "moused",
   "args" : {
      "timeout" : "0",
      "action" : "set_tap_to_click"
   },
   "id" : "fooid"
}

Response:
-------------------------------
{
  "args": {
    "set_tap_to_click": {
      "timeout": "0"
    }
  },
  "id": "fooid",
  "name": "response",
  "namespace": "sysadm"
}
2017-10-26 13:27:51 -04:00
Ken Moore
d7ddcb5ae0 [API CHANGE] New API call: sysadm/moused, action="get_tap_to_click"
No other input arguments
This returns the information about the current settings of "tap-to-click" for laptop touchpads.

REST Request (example):
-------------------------------
PUT /sysadm/moused
{
   "action" : "get_tap_to_click"
}

WebSocket Request:
-------------------------------
{
   "args" : {
      "action" : "get_tap_to_click"
   },
   "name" : "moused",
   "namespace" : "sysadm",
   "id" : "fooid"
}

Response:
-------------------------------
{
  "args": {
    "get_tap_to_click": {
      "enabled": "unavailable",
      "timeout": "125000",
      "using_synaptics": "false"
    }
  },
  "id": "fooid",
  "name": "response",
  "namespace": "sysadm"
}
2017-10-26 12:55:07 -04:00
Ken Moore
654b1a72d5 Add another quick error detection flag to the new "getsysctl" action in the system manager. 2017-10-26 11:17:25 -04:00
Ken Moore
3cb89665b7 Compress the batteryInfo function into a single "apm" process call instead of 3 of them. 2017-10-26 09:59:10 -04:00
Ken Moore
5807bedd68 [API CHANGE] New API call: sysadm/systemmanager, action="getsysctl"
Required arguments: "sysctl" (string or array of strings)
This will report back the value of the requested sysctl(s).

REST Request (example):
-------------------------------
PUT /sysadm/systemmanager
{
   "action" : "getsysctl",
   "sysctl" : [
      "hw.usb.atp.touch_timeout",
      "hw.psm.tap_timeout"
   ]
}

WebSocket Request:
-------------------------------
{
   "id" : "fooid",
   "name" : "systemmanager",
   "namespace" : "sysadm",
   "args" : {
      "action" : "getsysctl",
      "sysctl" : [
         "hw.usb.atp.touch_timeout",
         "hw.psm.tap_timeout"
      ]
   }
}

Response:
-------------------------------
{
  "args": {
    "getsysctl": {
      "hw.psm.tap_timeout": "125000",
      "hw.usb.atp.touch_timeout": "125000"
    }
  },
  "id": "fooid",
  "name": "response",
  "namespace": "sysadm"
}
2017-10-26 09:26:51 -04:00
Ken Moore
2240fdd8c7 [API CHANGE] Adjust the output structure for the "sysctllist" action in the system manager.
The output now returns objects with value, type, and description of each sysctl instead of just the values.

REST Request (example):
-------------------------------
PUT /sysadm/systemmanager
{
   "action" : "sysctllist"
}

WebSocket Request:
-------------------------------
{
   "id" : "fooid",
   "namespace" : "sysadm",
   "args" : {
      "action" : "sysctllist"
   },
   "name" : "systemmanager"
}

Response:
-------------------------------
{
  "args": {
    "sysctllist": {
      "compat.ia32.maxdsiz": {
        "type": "unsigned long",
        "value": "536870912"
      },
      "compat.ia32.maxssiz": {
        "type": "unsigned long",
        "value": "67108864"
      },
      "compat.ia32.maxvmem": {
        "type": "unsigned long",
        "value": "0"
      },
      "compat.linux.osname": {
        "description": "Linux kernel OS name",
        "type": "string",
        "value": "Linux"
      },
      "compat.linux.osrelease": {
        "description": "Linux kernel OS release",
        "type": "string",
        "value": "2.6.32"
      },
      "compat.linux.oss_version": {
        "description": "Linux OSS version",
        "type": "integer",
        "value": "198144"
      }
    }
  },
  "id": "fooid",
  "name": "response",
  "namespace": "sysadm"
}
2017-10-26 09:10:02 -04:00
Ken Moore
52ae9e8e5c Add ability to specify working directory to the dispatcher, and update the new fetch_ports API call. 2017-10-04 10:56:43 -04:00
Ken Moore
068ef24a66 Enable a new API call:
sysadm/systemmanager - "action" = "fetch_ports"

OPTIONAL argument: "ports_dir" = directory to place the ports tree

REST Request (example):
-------------------------------
PUT /sysadm/systemmanager
{
   "action" : "fetch_ports"
}

WebSocket Request:
-------------------------------
{
   "namespace" : "sysadm",
   "args" : {
      "action" : "fetch_ports"
   },
   "name" : "systemmanager",
   "id" : "fooid"
}

Response:
-------------------------------
{
  "args": {
    "fetch_ports": {
      "process_id": "system_fetch_ports_tree",
      "result": "process_started"
    }
  },
  "id": "fooid",
  "name": "response",
  "namespace": "sysadm"
}
2017-10-04 10:37:11 -04:00
Ken Moore
fba91d29c9 Enable/clean the backend functio 2017-10-04 10:31:49 -04:00
Ken Moore
a239e43c05 Add a new option to the moused options:
"mouse_scroll_invert" = [true/false]
This will invert the scroll direction for the mouse device.
2017-09-15 09:31:53 -04:00
Ken Moore
991dcdd2f9 Merge branch 'master' of github.com:trueos/sysadm 2017-08-25 09:25:55 -04:00
Ken Moore
e9a338fa0f Make sure that pc-updatemanager syncconf is always run before any automated update checks begin. 2017-08-25 09:25:18 -04:00
ZackaryWelch
6447b0fed1 Moved the SysAdm client manpage to the client repo 2017-08-24 13:21:10 -04:00
ZackaryWelch
cf5b11a539 Added a man page for the sysadm cli client 2017-08-24 11:26:23 -04:00
Tim Moore
93f4d89b7d Remove (Coming Soon) from SysAdm issues tracker 2017-08-21 12:27:56 -04:00
Ken Moore
4e23691d50 Commit some more work on an /etc/rc.conf writing routine for networking.
Not enabled for API calls yet.
2017-08-10 14:53:30 -04:00
Ken Moore
376eaa4e37 [API CHANGE] Add a new action for sysadm/network class:
"action" = "list-settings"
List all the settings for network devices that are saved in /etc/rc.conf
-----------------

REST Request (example):
-------------------------------
PUT /sysadm/network
{
   "action" : "list-settings"
}

WebSocket Request:
-------------------------------
{
   "id" : "fooid",
   "namespace" : "sysadm",
   "args" : {
      "action" : "list-settings"
   },
   "name" : "network"
}

Response:
-------------------------------
{
  "args": {
    "lo0": {
      "associated_device": "",
      "device": "lo0",
      "static_gateway": "",
      "static_ipv4": "",
      "static_ipv6": "",
      "static_netmask": "",
      "use_dhcp": "false"
    },
    "re0": {
      "associated_device": "",
      "device": "re0",
      "static_gateway": "",
      "static_ipv4": "",
      "static_ipv6": "",
      "static_netmask": "",
      "use_dhcp": "true"
    },
    "ue0": {
      "associated_device": "",
      "device": "ue0",
      "static_gateway": "",
      "static_ipv4": "",
      "static_ipv6": "",
      "static_netmask": "",
      "use_dhcp": "true"
    }
  },
  "id": "fooid",
  "name": "response",
  "namespace": "sysadm"
}
2017-08-10 13:51:35 -04:00
Ken Moore
4dded17898 Important update to pkg backend:
1) Change the API calls to all be name-based instead of origin-based
2) This fixes the detection/viewing of base packages
3) This makes the AppCafe 100% ready for the flavours/subpackages which is coming from upstream pkg sometime soon.
2017-08-10 10:38:32 -04:00
Ken Moore
6ab7c3dc01 Adjust the logic of the dispatcher processes a bit:
For the incremental process notifications, only include the latest output from the process, not the full thing up to that point. Some processes (life preserver, pc-updatemanager) run for a long time and eventually get massive log files.
2017-08-07 11:52:06 -04:00
Ken Moore
34b388277d [API CHANGE] Add a new "action" for sysadm/systemmanager
"action"="deviceinfo" will return the full information about all devices attached to the system (via `pciconf -lv`)
-------------------

REST Request (example):
-------------------------------
PUT /sysadm/systemmanager
{
   "action" : "deviceinfo"
}

WebSocket Request:
-------------------------------
{
   "id" : "fooid",
   "name" : "systemmanager",
   "namespace" : "sysadm",
   "args" : {
      "action" : "deviceinfo"
   }
}

Response:
-------------------------------
{
  "args": {
    "deviceinfo": {
      "ahci0": {
        "class": "mass storage",
        "device": "8 Series/C220 Series Chipset Family 6-port SATA Controller 1 [AHCI mode]",
        "subclass": "SATA",
        "vendor": "Intel Corporation"
      },
      "ehci0": {
        "class": "serial bus",
        "device": "8 Series/C220 Series Chipset Family USB EHCI",
        "subclass": "USB",
        "vendor": "Intel Corporation"
      },
      "ehci1": {
        "class": "serial bus",
        "device": "8 Series/C220 Series Chipset Family USB EHCI",
        "subclass": "USB",
        "vendor": "Intel Corporation"
      },
      "hdac0": {
        "class": "multimedia",
        "subclass": "HDA",
        "vendor": "NVIDIA Corporation"
      },
      "hdac1": {
        "class": "multimedia",
        "device": "8 Series/C220 Series Chipset High Definition Audio Controller",
        "subclass": "HDA",
        "vendor": "Intel Corporation"
      },
      "hostb0": {
        "class": "bridge",
        "device": "4th Gen Core Processor DRAM Controller",
        "subclass": "HOST-PCI",
        "vendor": "Intel Corporation"
      },
      "isab0": {
        "class": "bridge",
        "device": "B85 Express LPC Controller",
        "subclass": "PCI-ISA",
        "vendor": "Intel Corporation"
      },
      "none0": {
        "class": "simple comms",
        "device": "8 Series/C220 Series Chipset Family MEI Controller",
        "vendor": "Intel Corporation"
      },
      "none1": {
        "class": "serial bus",
        "device": "8 Series/C220 Series Chipset Family SMBus Controller",
        "subclass": "SMBus",
        "vendor": "Intel Corporation"
      },
      "pcib1": {
        "class": "bridge",
        "device": "Xeon E3-1200 v3/4th Gen Core Processor PCI Express x16 Controller",
        "subclass": "PCI-PCI",
        "vendor": "Intel Corporation"
      },
      "pcib2": {
        "class": "bridge",
        "device": "8 Series/C220 Series Chipset Family PCI Express Root Port",
        "subclass": "PCI-PCI",
        "vendor": "Intel Corporation"
      },
      "pcib3": {
        "class": "bridge",
        "device": "8 Series/C220 Series Chipset Family PCI Express Root Port",
        "subclass": "PCI-PCI",
        "vendor": "Intel Corporation"
      },
      "pcib4": {
        "class": "bridge",
        "device": "8 Series/C220 Series Chipset Family PCI Express Root Port",
        "subclass": "PCI-PCI",
        "vendor": "Intel Corporation"
      },
      "pcib5": {
        "class": "bridge",
        "device": "82801 PCI Bridge",
        "subclass": "PCI-PCI",
        "vendor": "Intel Corporation"
      },
      "re0": {
        "class": "network",
        "device": "RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller",
        "subclass": "ethernet",
        "vendor": "Realtek Semiconductor Co., Ltd."
      },
      "vgapci0": {
        "class": "display",
        "device": "GM206 [GeForce GTX 960]",
        "subclass": "VGA",
        "vendor": "NVIDIA Corporation"
      },
      "xhci0": {
        "class": "serial bus",
        "device": "8 Series/C220 Series Chipset Family USB xHCI",
        "subclass": "USB",
        "vendor": "Intel Corporation"
      }
    }
  },
  "id": "fooid",
  "name": "response",
  "namespace": "sysadm"
}
2017-08-02 11:14:38 -04:00
Ken Moore
00251895a3 [API CHANGE] Add a new "pkg_install_verify" action to sysadm/pkg
Add an "action" = "pkg_install_verify" option to the sysadm/pkg API call.
REQUIRED: "pkg_origins" - string or array of pkg origins
	"repo" - remote repository that the package will be installed from.
----------

REST Request (example):
-------------------------------
PUT /sysadm/pkg
{
   "repo" : "trueos-major",
   "action" : "pkg_install_verify",
   "pkg_origins" : [
      "www/qupzilla-qt5-webkit"
   ]
}

WebSocket Request:
-------------------------------
{
   "name" : "pkg",
   "namespace" : "sysadm",
   "id" : "fooid",
   "args" : {
      "action" : "pkg_install_verify",
      "pkg_origins" : [
         "www/qupzilla-qt5-webkit"
      ],
      "repo" : "trueos-major"
   }
}

Response:
-------------------------------
{
  "args": {
    "pkg_install_verify": {
      "conflicts": [],
      "install": {
        "qupzilla-qt5-webkit": {
          "comment": "Web browser based on WebKit engine and Qt Framework",
          "flatsize": "13380132",
          "name": "qupzilla-qt5-webkit",
          "origin": "www/qupzilla-qt5-webkit",
          "pkgsize": "2574460",
          "version": "1.8.9_3"
        }
      }
    }
  },
  "id": "fooid",
  "name": "response",
  "namespace": "sysadm"
}
2017-08-01 13:08:51 -04:00
Ken Moore
09c41edaca Clean up some more whitespace in sysadm server files. 2017-07-27 15:19:54 -04:00
Ken Moore
4a866b924f Disable some debug statements that were dumping Life Preserver logs into the webserver log file. 2017-07-27 15:14:22 -04:00
Ken Moore
58d309031f Fix up a couple Qt5 dependencies in the sysutils/sysadm port. 2017-07-07 09:11:06 -04:00
Ken Moore
e78f6a003f Merge branch 'master' of github.com:trueos/sysadm 2017-07-03 10:36:52 -04:00
Ken Moore
724c8ac7a8 Cleanup some whitespace in SysAdm files while I was looking for something... 2017-07-03 10:36:21 -04:00
q5sys
c4750152a6 prototype sourcetree management for sysadm 2017-06-05 11:40:50 -04:00
q5sys
a0a585353c a few additions to sysadm-general 2017-06-05 10:41:40 -04:00
Ken Moore
3276b32f80 NEW API: sysadm/iocage "action"="cleantemplates"
This will delete all templates which have been cached on the local system.
-----------

REST Request (example):
-------------------------------
PUT /sysadm/iocage
{
   "action" : "cleantemplates"
}

WebSocket Request:
-------------------------------
{
   "namespace" : "sysadm",
   "args" : {
      "action" : "cleantemplates"
   },
   "id" : "fooid",
   "name" : "iocage"
}

Response:
-------------------------------
{
  "args": {
    "cleantemplates": {
      "success": "All templates have been cleaned."
    }
  },
  "id": "fooid",
  "name": "response",
  "namespace": "sysadm"
}
2017-06-02 11:13:29 -04:00
Ken Moore
c744785115 New API: sysadm/iocage "action"="cleanreleases"
This will remove all the RELEASE's which have been downloaded/cached on the local system.

----------

REST Request (example):
-------------------------------
PUT /sysadm/iocage
{
   "action" : "cleanreleases"
}

WebSocket Request:
-------------------------------
{
   "namespace" : "sysadm",
   "args" : {
      "action" : "cleanreleases"
   },
   "id" : "fooid",
   "name" : "iocage"
}

Response:
-------------------------------
{
  "args": {
    "cleanreleases": {
      "success": "All RELEASEs have been cleaned."
    }
  },
  "id": "fooid",
  "name": "response",
  "namespace": "sysadm"
}
2017-06-02 11:11:46 -04:00
Ken Moore
acbf8d6730 Adjust the fetch plugin function to use the new syntax that brandon just committed upstream (did not work properly anyway with the old syntax) 2017-06-02 10:29:52 -04:00
Ken Moore
b428fb531a NEW API: sysadm/iocage "action"="createplugin"
This will fetch/create a new plugin jail.
Required Arguements:
"action"="createplugin"
"plugin"="name_of_plugin"
"net_device"="network_device_to_use"
"ip4" *or* "ip6" with the address to assign to the new jail (IPv4 or IPv6 address)

Changelog: yes
------------

REST Request (example):
-------------------------------
PUT /sysadm/iocage
{
   "plugin" : "gitlab",
   "net_device" : "re0",
   "action" : "createplugin",
   "ip4" : "10.20.0.130"
}

WebSocket Request:
-------------------------------
{
   "id" : "fooid",
   "name" : "iocage",
   "args" : {
      "ip4" : "10.20.0.130",
      "plugin" : "gitlab",
      "net_device" : "re0",
      "action" : "createplugin"
   },
   "namespace" : "sysadm"
}

Response:
-------------------------------
{
  "args": {
    "createplugin": {
      "started_dispatcher_id": "sysadm_iocage_fetch_plugin_gitlab"
    }
  },
  "id": "fooid",
  "name": "response",
  "namespace": "sysadm"
}
2017-06-02 09:52:53 -04:00
Ken Moore
737e90c42a NEW API CALL: sysadm/iocage "action"="fetchreleases"
This will fetch any remotely-available FreeBSD releases and update/cache them on the local system for use when spinning up jails.
REQUIRED ARGUMENTS:
"action" = "fetchreleases"
"releases": String *or* Array of strings containing the version/release to fetch.

Changelog: yes
-----------------

REST Request (example):
-------------------------------
PUT /sysadm/iocage
{
   "action" : "fetchreleases",
   "releases" : [
      "10.3-RELEASE",
      "10.2-RELEASE"
   ]
}

WebSocket Request:
-------------------------------
{
   "args" : {
      "releases" : [
         "10.3-RELEASE",
         "10.2-RELEASE"
      ],
      "action" : "fetchreleases"
   },
   "name" : "iocage",
   "namespace" : "sysadm",
   "id" : "fooid"
}

Response:
-------------------------------
{
  "args": {
    "fetchreleases": {
      "started_dispatcher_id": [
        "sysadm_iocage_fetch_release_10.3-RELEASE",
        "sysadm_iocage_fetch_release_10.2-RELEASE"
      ]
    }
  },
  "id": "fooid",
  "name": "response",
  "namespace": "sysadm"
}
2017-06-02 08:24:45 -04:00
Ken Moore
a3c44a5f5b NEW API: sysadm/iocage "action"="listjails"
This will list all the current jails on the system.

Output syntax is almost exactly the same as the listtemplates action:

{
  "listjails":{
    "jails:{
      "jid_1":{
        [same 10 fields: jid, uuid, boot, state, tag, type, release, ip4, ip6, template]
      },
      "jid_2":{
        [same 10 fields]
      }
    }
  }
}
2017-06-01 16:13:34 -04:00
Ken Moore
fdd864e298 NEW API: sysadm/iocage "action"="listtemplates"
This will list all the jail templates currently available on the local system.

Example Arguments Reply:

{
 "listtemplates":{
  "templates" : {
   "template_1" : {
    "jid" : "jail_id",
    "uuid": "unique_id_string",
    "boot": "on/off",
    "state":"jail_state",
    "tag":"jail_tag",
    "type":"jail_type",
    "release":"freebsd_release",
    "ip4":"ipv4_address",
    "ip6":"ipv6_address",
    "template":"template_1"
   }
  }
 }
}
2017-06-01 16:04:01 -04:00
Ken Moore
4333ac6fba API Adjustment: sysadm/iocage "action"="listplugins"
Adjust the output syntax to also include "local" plugins (those already installed). The "remote" plugin format is unchanged.

Output Arguments example:
{
 "listplugins":{
  "remote" : { [same as before] },
  "local" : {
    "pluginname_jid" : {
      "jid" : "number",
      "uuid" : "uuid_string",
      "boot" : "on/off",
      "state" : "activestate",
      "tag" : "pluginname",
      "type" : "plugin",
      "release" : "freebsd_release",
      "ip4" : "ipv4_address",
      "ip6" : "ipv6_address",
      "template" : "-"
    }
  }
 }
}
2017-06-01 15:34:26 -04:00
Ken Moore
859b441ae2 New API Call: sysadm/iocage "action"="activatestatus"
This will list the current pool which is activated for iocage to use.

Example API return (arguments):
{
  "activatestatus":{
    "activated":"true",
    "pool":"my_zpool"
  }
}

Changelog: yes
2017-06-01 13:50:10 -04:00
Ken Moore
ebae121639 Start fixing up the iocage plugin fetch function - not working yet. 2017-05-31 14:34:54 -04:00
Ken Moore
2bf23b8aaf API Change: sysadm/iocage - "action"="listreleases"
Add an API call for listing all the FreeBSD releases supported for iocage jails

REST Request (example):
-------------------------------
PUT /sysadm/iocage
{
   "action" : "listreleases"
}

WebSocket Request:
-------------------------------
{
   "args" : {
      "action" : "listreleases"
   },
   "name" : "iocage",
   "id" : "fooid",
   "namespace" : "sysadm"
}

Response:
-------------------------------
{
  "args": {
    "listreleases": {
      "local": [
        ""
      ],
      "remote": [
        "9.3-RELEASE (EOL)",
        "10.1-RELEASE (EOL)",
        "10.2-RELEASE (EOL)",
        "10.3-RELEASE",
        "11.0-RELEASE"
      ]
    }
  },
  "id": "fooid",
  "name": "response",
  "namespace": "sysadm"
}

Changelog: yes
2017-05-31 14:24:21 -04:00
Ken Moore
f1c3651ba5 API CHANGE: sysadm/iocage - "action"="listplugins"
Add this API call to list all the available plugins for iocage.

REST Request (example):
-------------------------------
PUT /sysadm/iocage
{
   "action" : "listplugins"
}

WebSocket Request:
-------------------------------
{
   "namespace" : "sysadm",
   "args" : {
      "action" : "listplugins"
   },
   "name" : "iocage",
   "id" : "fooid"
}

Response:
-------------------------------
{
  "args": {
    "listplugins": {
      "remote": {
        "btsync": {
          "description": "Resilient, fast and scalable file sync software for enterprises and individuals.",
          "id": "btsync",
          "name": "BitTorrent Sync"
        },
        "couchpotato": {
          "description": "CouchPotato is an automatic NZB and torrent downloader.",
          "id": "couchpotato",
          "name": "CouchPotato"
        },
        "crashplan": {
          "description": "Computer backup and data storage made simple.",
          "id": "crashplan",
          "name": "Crashplan"
        },
        "deluge": {
          "description": "Bittorrent client using Python, and libtorrent-rasterbar",
          "id": "deluge",
          "name": "Deluge"
        },
        "emby": {
          "description": "Home media server built using mono and other open source technologies",
          "id": "emby",
          "name": "Emby"
        },
        "gitlab": {
          "description": "Powerful features for modern software development",
          "id": "gitlab",
          "name": "GitLab"
        },
        "jenkins": {
          "description": "Jenkins CI",
          "id": "jenkins",
          "name": "Jenkins"
        },
        "jenkins-lts": {
          "description": "Jenkins CI (Long Term Support Version)",
          "id": "jenkins-lts",
          "name": "Jenkins (LTS)"
        },
        "madsonic": {
          "description": "Open-source web-based media streamer and jukebox.",
          "id": "madsonic",
          "name": "MadSonic"
        },
        "nextcloud": {
          "description": "Access, share and protect your files, calendars, contacts, communication & more at home and in your enterprise.",
          "id": "nextcloud",
          "name": "NextCloud"
        },
        "plexmediaserver": {
          "description": "The Plex media server system",
          "id": "plexmediaserver",
          "name": "Plex Media Server"
        },
        "plexmediaserver-plexpass": {
          "description": "The Plex media server system",
          "id": "plexmediaserver-plexpass",
          "name": "Plex Media Server (PlexPass)"
        },
        "quasselcore": {
          "description": "Quassel Core is a daemon/headless IRC client, part of Quassel, that supports 24/7 connectivity. Quassel Client can be attached to it to.",
          "id": "quasselcore",
          "name": "Quasselcore"
        },
        "sickrage": {
          "description": "Automatic Video Library Manager for TV Shows",
          "id": "sickrage",
          "name": "SickRage"
        },
        "sonarr": {
          "description": "PVR for Usenet and BitTorrent users",
          "id": "sonarr",
          "name": "Sonarr"
        },
        "subsonic": {
          "description": "Open-source web-based media streamer and jukebox.",
          "id": "subsonic",
          "name": "SubSonic"
        },
        "syncthing": {
          "description": "Personal cloud sync",
          "id": "syncthing",
          "name": "Syncthing"
        },
        "transmission": {
          "description": "Fast and lightweight daemon BitTorrent client",
          "id": "transmission",
          "name": "Transmission"
        }
      }
    }
  },
  "id": "fooid",
  "name": "response",
  "namespace": "sysadm"
}

Changelog: yes
2017-05-31 14:19:10 -04:00
Ken Moore
35759b12a1 Merge branch 'master' of github.com:trueos/sysadm 2017-05-30 11:46:59 -04:00
Ken Moore
0e2d941ae1 Add the qMDNS class to SysAdm's backend source tree (not tied in yet) 2017-05-30 11:46:31 -04:00
37 changed files with 2220 additions and 476 deletions

View File

@@ -162,7 +162,7 @@ Due to the number of repositories under the TrueOS "umbrella", the TrueOS Projec
* [trueos-core](https://github.com/trueos/trueos-core) : Used for general TrueOS issues, Pico issues, and feature requests.
* [lumina](https://github.com/trueos/lumina) : Issues related to using the Lumina Desktop Environment.
* (Coming Soon) [sysadm](https://github.com/trueos/sysadm) : Issues with using the SysAdm client or server.
* [sysadm](https://github.com/trueos/sysadm) : Issues with using the SysAdm client or server.
* [trueos-docs](https://github.com/trueos/trueos-docs) : Issues related to the TrueOS Handbook.
* [lumina-docs](https://github.com/trueos/lumina-docs) : Issues related to the Lumina Handbook.
* [sysadm-docs](https://github.com/trueos/sysadm-docs) : Issues related to the SysAdm API Guide, Client, and Server Handbooks.

View File

@@ -1,18 +1,17 @@
# Created by: Kris Moore <kmoore@FreeBSD.org>
# $FreeBSD$
PORTNAME= sysadm
PORTVERSION= %%CHGVERSION%%
CATEGORIES= sysutils
MAINTAINER= kmoore@FreeBSD.org
MAINTAINER= jt@ixsystems.com
COMMENT= SysAdm API server
LICENSE= BSD3CLAUSE
WRKSRC_SUBDIR= src
USE_QT5= concurrent core network buildtools qmake gui websockets sql
USES= pkgconfig tar:xz qmake ssl
USES= pkgconfig tar:xz qmake ssl qt:5
USE_QT= concurrent core network buildtools_build qmake_build websockets sql
MAKE_ARGS= PREFIX=${STAGEDIR}${PREFIX}
USE_GITHUB= yes

21
src/qMDNS/LICENSE.md Normal file
View 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
View 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
View 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
View File

@@ -0,0 +1,7 @@
QT *= core network
INCLUDEPATH *= $$PWD
HEADERS *= $$PWD/qMDNS.h
SOURCES *= $$PWD/qMDNS.cpp

View File

@@ -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
}

View File

@@ -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

View File

@@ -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){
}*/

View File

@@ -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();

View File

@@ -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]);
}
}

View File

@@ -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;
}
}
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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) {

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -14,6 +14,20 @@ using namespace sysadm;
// ==================
// INLINE FUNCTIONS
// ==================
inline QStringList ids_from_origins(QStringList origins, QSqlDatabase DB){
QSqlQuery q("SELECT id FROM packages WHERE origin IN ('"+origins.join("', '")+"')",DB);
QStringList out;
while(q.next()){ out << q.value("id").toString(); }
return out;
}
inline QStringList ids_from_names(QStringList names, QSqlDatabase DB){
QSqlQuery q("SELECT id FROM packages WHERE name IN ('"+names.join("', '")+"')",DB);
QStringList out;
while(q.next()){ out << q.value("id").toString(); }
return out;
}
//Get annotation variable/values
inline void annotations_from_ids(QStringList var_ids, QStringList val_ids, QJsonObject *out, QSqlDatabase DB){
//Note: Both input lists *must* be the same length (one variable for one value)
@@ -21,16 +35,16 @@ inline void annotations_from_ids(QStringList var_ids, QStringList val_ids, QJson
tot.removeDuplicates();
int index = -1;
QSqlQuery q("SELECT annotation, annotation_id FROM annotation WHERE annotation_id IN ('"+tot.join("', '")+"')",DB);
while(q.next()){
while(q.next()){
//qDebug() << "Got query result:" << q.value("annotation_id").toString() << q.value("annotation").toString();
index = var_ids.indexOf(q.value("annotation_id").toString());
while(index>=0){
var_ids.replace(index, q.value("annotation").toString());
while(index>=0){
var_ids.replace(index, q.value("annotation").toString());
index = var_ids.indexOf(q.value("annotation_id").toString());
}
index = val_ids.indexOf(q.value("annotation_id").toString());
while(index>=0){
val_ids.replace(index, q.value("annotation").toString());
while(index>=0){
val_ids.replace(index, q.value("annotation").toString());
index = val_ids.indexOf(q.value("annotation_id").toString());
}
}
@@ -47,7 +61,7 @@ inline QStringList origins_from_package_ids(QStringList ids, QSqlDatabase DB){
while(q.next()){ out << q.value("origin").toString(); }
return out;
}
//Generic ID's -> Names function (known databases: users, groups, licenses, shlibs, categories )
//Generic ID's -> Names function (known databases: users, groups, licenses, shlibs, categories, packages )
inline QStringList names_from_ids(QStringList ids, QString db, QSqlDatabase DB){
QSqlQuery q("SELECT name FROM "+db+" WHERE id IN ('"+ids.join("', '")+"')",DB);
QStringList out;
@@ -68,15 +82,35 @@ inline QStringList requires_from_ids(QStringList ids, QSqlDatabase DB){
while(q.next()){ out << q.value("require").toString(); }
return out;
}
//conflict ID's from package ID's
inline QStringList conflicts_from_ids(QStringList ids, QSqlDatabase DB){
QSqlQuery q("SELECT conflict_id FROM pkg_conflicts WHERE package_id IN ('"+ids.join("', '")+"')", DB);
QStringList out;
while(q.next()){ out << q.value("conflict_id").toString(); }
qDebug() << "Last Conflict detection Error:" << q.lastError().text();
return out;
}
//dependencies from package ID's
inline QStringList depends_from_ids(QStringList ids, QSqlDatabase DB){
//Note: This returns package names, not ID's
QSqlQuery q("SELECT name FROM deps WHERE package_id IN ('"+ids.join("', '")+"')", DB);
QStringList out;
while(q.next()){ out << q.value("name").toString(); }
return out;
}
inline QString getRepoFile(QString repo){
if(repo=="local"){ return "/var/db/pkg/local.sqlite"; }
else{ return ("/var/db/pkg/repo-"+repo+".sqlite"); }
else{ return ("/var/db/pkg/repo-"+repo+".sqlite"); }
}
inline QString openDB(QString repo){
//This ensures that each request for a database gets its own unique connection
//This ensures that each request for a database gets its own unique connection
// (preventing conflict between concurrent calls)
QSqlDatabase DB = QSqlDatabase::addDatabase("QSQLITE", repo+QUuid::createUuid().toString());
DB.setConnectOptions("QSQLITE_OPEN_READONLY=1");
DB.setConnectOptions("QSQLITE_OPEN_READONLY=1");
DB.setHostName("localhost");
QString path = getRepoFile(repo);
DB.setDatabaseName(path); //path to the database file
@@ -100,15 +134,15 @@ QJsonObject PKG::pkg_info(QStringList origins, QString repo, QString category, b
QJsonObject retObj;
//if(origins.contains("math/R")){ qDebug() << "pkg_info:" << repo << category; }
QString dbconn = openDB(repo);
if(!dbconn.isEmpty()){
if(!dbconn.isEmpty()){
QSqlDatabase DB = QSqlDatabase::database(dbconn);
if(!DB.isOpen()){ return retObj; } //could not open DB (file missing?)
//Now do all the pkg info, one pkg origin at a time
origins.removeAll("");
origins.removeDuplicates();
QString q_string = "SELECT * FROM packages";
if(!origins.isEmpty()){
q_string.append(" WHERE origin IN ('"+origins.join("', '")+"')");
if(!origins.isEmpty()){
q_string.append(" WHERE name IN ('"+origins.join("', '")+"')");
//Also keep the ordering of the origins preserved
/*q_string.append(" ORDER BY CASE origins ");
for(int i=0; i<origins.length(); i++){ q_string.append("WHEN '"+origins[i]+"' THEN '"+QString::number(i+1)+"' "); }
@@ -119,13 +153,13 @@ QJsonObject PKG::pkg_info(QStringList origins, QString repo, QString category, b
QSqlQuery query(q_string, DB);
while(query.next()){
QString id = query.value("id").toString(); //need this pkg id for later
QString origin = query.value("origin").toString(); //need the origin for later
QString name = query.value("name").toString(); //need the origin for later
//if(origins.contains("math/R")){ qDebug() << "Found origin:" << origin << id; }
if(id.isEmpty() || origin.isEmpty()){ continue; }
if(id.isEmpty() || name.isEmpty()){ continue; }
QJsonObject info;
//General info
for(int i=0; i<query.record().count(); i++){
info.insert(query.record().fieldName(i), query.value(i).toString() );
info.insert(query.record().fieldName(i), query.value(i).toString() );
}
//ANNOTATIONS
QSqlQuery q2("SELECT tag_id, value_id FROM pkg_annotation WHERE package_id = '"+id+"'", DB);
@@ -134,7 +168,7 @@ QJsonObject PKG::pkg_info(QStringList origins, QString repo, QString category, b
tags << q2.value("tag_id").toString(); vals << q2.value("value_id").toString();
}
if(!tags.isEmpty()){ annotations_from_ids(tags, vals, &info, DB); }
if(!fullresults){ retObj.insert(origin,info); continue; } //skip the rest of the info queries
if(!fullresults){ retObj.insert(name,info); continue; } //skip the rest of the info queries
//OPTIONS
QSqlQuery q3("SELECT value, option FROM pkg_option INNER JOIN option ON pkg_option.option_id = option.option_id WHERE pkg_option.package_id = '"+id+"'", DB);
QJsonObject options;
@@ -144,22 +178,29 @@ QJsonObject PKG::pkg_info(QStringList origins, QString repo, QString category, b
}
if(!options.isEmpty()){ info.insert("options",options); }
//DEPENDENCIES
QSqlQuery q4("SELECT origin FROM deps WHERE package_id = '"+id+"'", DB);
QStringList tmpList;
while(q4.next()){
QSqlQuery q4("SELECT origin, name FROM deps WHERE package_id = '"+id+"'", DB);
QStringList tmpList, tmpListN;
while(q4.next()){
tmpList << q4.value("origin").toString();
tmpListN << q4.value("name").toString();
} //end deps query
if(!tmpList.isEmpty()){ info.insert("dependencies", QJsonArray::fromStringList(tmpList) ); }
if(!tmpList.isEmpty()){
//info.insert("dependencies_origins", QJsonArray::fromStringList(tmpList) );
info.insert("dependencies", QJsonArray::fromStringList(tmpListN) );
}
//FILES
QSqlQuery q5("SELECT path FROM files WHERE package_id = '"+id+"'", DB);
tmpList.clear();
while(q5.next()){ tmpList << q5.value("path").toString(); }
if(!tmpList.isEmpty()){ info.insert("files", QJsonArray::fromStringList(tmpList) ); }
//REVERSE DEPENDENCIES
QSqlQuery q6("SELECT package_id FROM deps WHERE origin = '"+origin+"'", DB);
QSqlQuery q6("SELECT package_id FROM deps WHERE name = '"+name+"'", DB);
tmpList.clear();
while(q6.next()){ tmpList << q6.value("package_id").toString(); }
if(!tmpList.isEmpty()){ info.insert("reverse_dependencies", QJsonArray::fromStringList(origins_from_package_ids(tmpList, DB)) ); }
if(!tmpList.isEmpty()){
//info.insert("reverse_dependencies_origins", QJsonArray::fromStringList(origins_from_package_ids(tmpList, DB)) );
info.insert("reverse_dependencies", QJsonArray::fromStringList(names_from_ids(tmpList, "packages", DB)) );
}
//USERS
QSqlQuery q7("SELECT user_id FROM pkg_users WHERE package_id = '"+id+"'", DB);
tmpList.clear();
@@ -204,9 +245,9 @@ QJsonObject PKG::pkg_info(QStringList origins, QString repo, QString category, b
QSqlQuery q15("SELECT require_id FROM pkg_requires WHERE package_id = '"+id+"'", DB);
tmpList.clear();
while(q15.next()){ tmpList << q15.value("require_id").toString(); }
if(!tmpList.isEmpty()){ info.insert("requires", QJsonArray::fromStringList(requires_from_ids(tmpList, DB)) ); }
if(!tmpList.isEmpty()){ info.insert("requires", QJsonArray::fromStringList(requires_from_ids(tmpList, DB)) ); }\
//Now insert this information into the main object
retObj.insert(origin,info);
retObj.insert(name,info);
} //end loop over pkg matches
DB.close();
}//end if dbconn exists (force DB out of scope now)
@@ -218,67 +259,67 @@ QJsonObject PKG::pkg_info(QStringList origins, QString repo, QString category, b
QStringList PKG::pkg_search(QString repo, QString searchterm, QStringList searchexcludes, QString category){
QString dbconn = openDB(repo);
QStringList found;
if(!dbconn.isEmpty()){
if(!dbconn.isEmpty()){
QSqlDatabase DB = QSqlDatabase::database(dbconn);
if(!DB.isOpen()){ return QStringList(); } //could not open DB (file missing?)
QStringList terms = searchterm.split(" ",QString::SkipEmptyParts);
searchexcludes.removeAll("");
QString q_string;
int numtry = 0;
while(found.isEmpty() && numtry<2){
if(numtry<1 && !searchterm.contains(" ")){ //single-word-search (exact names never have multiple words)
q_string = "SELECT origin FROM packages WHERE name = '"+searchterm+"' OR origin LIKE '%/"+searchterm+"'";
q_string = "SELECT name FROM packages WHERE name = '"+searchterm+"' OR origin LIKE '%/"+searchterm+"'";
if(!category.isEmpty()){ q_string.append(" AND origin LIKE '"+category+"/%'"); }
if(!searchexcludes.isEmpty()){ q_string.append(" AND name NOT LIKE '%"+searchexcludes.join("%' AND name NOT LIKE '%")+"%'"); }
q_string.append(" COLLATE NOCASE"); // Case insensitive
QSqlQuery query(q_string, DB);
while(query.next()){
found << query.value("origin").toString(); //need the origin for later
found << query.value("name").toString(); //need the origin for later
}
}
if(found.length()<60 && numtry<1){
//Expand the search to names containing the term
q_string = "SELECT origin FROM packages WHERE name LIKE '"+searchterm+"%'";
q_string = "SELECT name FROM packages WHERE name LIKE '"+searchterm+"%'";
if(!category.isEmpty()){ q_string.append(" AND origin LIKE '"+category+"/%'"); }
if(!searchexcludes.isEmpty()){ q_string.append(" AND name NOT LIKE '%"+searchexcludes.join("%' AND name NOT LIKE '%")+"%'"); }
QSqlQuery q2(q_string, DB);
while(q2.next()){
found << q2.value("origin").toString(); //need the origin for later
found << q2.value("name").toString(); //need the origin for later
}
}
if(found.length()<60 && numtry<1){
//Expand the search to names containing the term
q_string = "SELECT origin FROM packages WHERE name LIKE '%"+searchterm+"%'";
q_string = "SELECT name FROM packages WHERE name LIKE '%"+searchterm+"%'";
if(!category.isEmpty()){ q_string.append(" AND origin LIKE '"+category+"/%'"); }
if(!searchexcludes.isEmpty()){ q_string.append(" AND name NOT LIKE '%"+searchexcludes.join("%' AND name NOT LIKE '%")+"%'"); }
QSqlQuery q2(q_string, DB);
while(q2.next()){
found << q2.value("origin").toString(); //need the origin for later
found << q2.value("name").toString(); //need the origin for later
}
}
if(found.length()<60){
//Expand the search to comments
if(terms.length()<2){ q_string = "SELECT origin FROM packages WHERE comment LIKE '%"+searchterm+"%'"; }
else if(numtry==0){ q_string = "SELECT origin FROM packages WHERE comment LIKE '%"+terms.join("%' AND comment LIKE '%")+"%'"; }
else if(numtry==1){ q_string = "SELECT origin FROM packages WHERE comment LIKE '%"+terms.join("%' OR comment LIKE '%")+"%'"; }
if(terms.length()<2){ q_string = "SELECT nameFROM packages WHERE comment LIKE '%"+searchterm+"%'"; }
else if(numtry==0){ q_string = "SELECT name FROM packages WHERE comment LIKE '%"+terms.join("%' AND comment LIKE '%")+"%'"; }
else if(numtry==1){ q_string = "SELECT name FROM packages WHERE comment LIKE '%"+terms.join("%' OR comment LIKE '%")+"%'"; }
if(!category.isEmpty()){ q_string.append(" AND origin LIKE '"+category+"/%'"); }
if(!searchexcludes.isEmpty()){ q_string.append(" AND comment NOT LIKE '%"+searchexcludes.join("%' AND comment NOT LIKE '%")+"%'"); }
QSqlQuery q2(q_string, DB);
while(q2.next()){
found << q2.value("origin").toString(); //need the origin for later
found << q2.value("name").toString(); //need the origin for later
}
}
if(found.length()<100){
//Expand the search to full descriptions
if(terms.length()<2){ q_string = "SELECT origin FROM packages WHERE desc LIKE '%"+searchterm+"%'"; }
else if(numtry==0){ q_string = "SELECT origin FROM packages WHERE desc LIKE '%"+terms.join("%' AND desc LIKE '%")+"%'"; }
else if(numtry==1){ q_string = "SELECT origin FROM packages WHERE desc LIKE '%"+terms.join("%' OR desc LIKE '%")+"%'"; }
if(terms.length()<2){ q_string = "SELECT name FROM packages WHERE desc LIKE '%"+searchterm+"%'"; }
else if(numtry==0){ q_string = "SELECT name FROM packages WHERE desc LIKE '%"+terms.join("%' AND desc LIKE '%")+"%'"; }
else if(numtry==1){ q_string = "SELECT name FROM packages WHERE desc LIKE '%"+terms.join("%' OR desc LIKE '%")+"%'"; }
if(!category.isEmpty()){ q_string.append(" AND origin LIKE '"+category+"/%'"); }
if(!searchexcludes.isEmpty()){ q_string.append(" AND desc NOT LIKE '%"+searchexcludes.join("%' AND desc NOT LIKE '%")+"%'"); }
QSqlQuery q2(q_string, DB);
while(q2.next()){
found << q2.value("origin").toString(); //need the origin for later
found << q2.value("name").toString(); //need the origin for later
}
}
//Now bump the try count
@@ -297,7 +338,7 @@ while(found.isEmpty() && numtry<2){
QJsonArray PKG::list_categories(QString repo){
QString dbconn = openDB(repo);
QStringList found;
if(!dbconn.isEmpty()){
if(!dbconn.isEmpty()){
QSqlDatabase DB = QSqlDatabase::database(dbconn);
if(!DB.isOpen()){ return QJsonArray(); } //could not open DB (file missing?)
@@ -313,7 +354,7 @@ QJsonArray PKG::list_categories(QString repo){
while(query.next()){
found << query.value("name").toString(); //need the origin for later
}
//Now check all the categories to ensure that pkgs exist within it
for(int i=0; i<found.length(); i++){
if(origins.filter(found[i]+"/").isEmpty()){ found.removeAt(i); i--; }
@@ -330,17 +371,32 @@ 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
DProcess* proc = DISPATCHER->queueProcess(Dispatcher::PKG_QUEUE, "internal_sysadm_pkg_repo_update_sync", "pkg update");
@@ -350,6 +406,79 @@ QJsonArray PKG::list_repos(bool updated){
return QJsonArray::fromStringList(found);
}
QJsonObject PKG::evaluateInstall(QStringList origins, QString repo){
//qDebug() << "Verify Install:" << origins << repo;
QJsonObject out;
out.insert("install_origins", QJsonArray::fromStringList(origins) );
out.insert("repo", repo);
if(repo=="local" || origins.isEmpty()){ return out; } //nothing to do
QString dbconn = openDB(repo);
QString ldbconn = openDB("local");
if(!dbconn.isEmpty()){
QSqlDatabase DB = QSqlDatabase::database(dbconn);
QSqlDatabase LDB = QSqlDatabase::database(ldbconn);
if(!DB.isOpen() || !LDB.isOpen()){ return out; } //could not open DB (file missing?)
//First get the list of all packages which need to be installed (ID's) from the remote database
QStringList toInstall_id;
QStringList tmp;
if(origins.first().contains("/")){ tmp = names_from_ids( ids_from_origins(origins, DB), "packages", DB); }
else{ tmp = origins; } //already given names
//qDebug() << " - Initial names:" << tmp;
while(!tmp.isEmpty()){
QStringList ids = ids_from_names(tmp, DB);
for(int i=0; i<ids.length(); i++){
if(toInstall_id.contains(ids[i])){ ids.removeAt(i); i--; } //remove any duplicate/evaluated ID's
}
if(ids.isEmpty()){ break; } //stop the loop - found the last round of dependencies
toInstall_id << ids; //add these to the list which are going to get installed
tmp = depends_from_ids(ids, DB); //now get the depdendencies of these packages
//qDebug() << " - Iteration names:" << tmp;
}
//Now go through and remove any packages from the list which are already installed locally
QStringList names = names_from_ids(toInstall_id, "packages", DB); //same order
//qDebug() << " - Total Names:" << names;
QStringList local_names = names_from_ids( ids_from_names(names, LDB), "packages", LDB);
//qDebug() << " - Local Names:" << local_names;
for(int i=0; i<local_names.length(); i++){
names.removeAll(local_names[i]);
}
//qDebug() << " - Filtered Names:" << names;
toInstall_id = ids_from_names(names, DB); //now get the shorter/filtered list of ID's (remote)
//qDebug() << " - Filtered ID's:" << toInstall_id;
//Get the list of conflicting packages which are already installed
QStringList conflict_ids = conflicts_from_ids(toInstall_id, DB); //also get the list of any conflicts for these packages
conflict_ids.removeDuplicates();
QStringList conflict_names = names_from_ids(conflict_ids, "packages", DB);
//qDebug() << " - Conflicts (remote):" << conflict_ids << conflict_names;
out.insert("conflicts", QJsonArray::fromStringList(names_from_ids( ids_from_names(conflict_names, LDB), "packages", LDB) ) );
//Now assemble all the information about the packages (remote database)
QJsonObject install;
//qDebug() << "Perform Query";
QSqlQuery qi("SELECT * FROM packages WHERE id IN ('"+toInstall_id.join("', '")+"')", DB);
while(qi.next()){
QJsonObject obj;
obj.insert( "name", qi.value("name").toString());
obj.insert( "origin", qi.value("origin").toString());
obj.insert( "pkgsize", qi.value("pkgsize").toString());
obj.insert( "flatsize", qi.value("flatsize").toString());
obj.insert( "version", qi.value("version").toString());
obj.insert( "comment", qi.value("comment").toString());
install.insert(qi.value("name").toString(), obj);
}
//qDebug() << "Final Install Object:" << install;
//qDebug() << "Last Query Error:" << qi.lastError().text();
//Add the info to the output object and close the databases
out.insert("install", install);
DB.close();
LDB.close();
} //force DB out of scope
QSqlDatabase::removeDatabase(dbconn);
QSqlDatabase::removeDatabase(ldbconn);
return out;
}
//=================
//pkg modification routines (dispatcher events for notifications)
//=================
@@ -382,7 +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){
@@ -397,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){
@@ -412,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;
}
//==================
@@ -430,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(){
@@ -444,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(){
@@ -472,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(){
@@ -486,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;
}

View File

@@ -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

View File

@@ -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;

View File

@@ -92,9 +92,10 @@ private:
void loadRunlevels();
bool enableDisableService(QString name, bool enable=false);
QString chroot;
QString ip;
bool usingOpenRC;
};
}
#endif // SERVICEMANAGER_H

View 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";
}
*/

View 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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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");

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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