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. * [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. * [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. * [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. * [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. * [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$ # $FreeBSD$
PORTNAME= sysadm PORTNAME= sysadm
PORTVERSION= %%CHGVERSION%% PORTVERSION= %%CHGVERSION%%
CATEGORIES= sysutils CATEGORIES= sysutils
MAINTAINER= kmoore@FreeBSD.org MAINTAINER= jt@ixsystems.com
COMMENT= SysAdm API server COMMENT= SysAdm API server
LICENSE= BSD3CLAUSE LICENSE= BSD3CLAUSE
WRKSRC_SUBDIR= src WRKSRC_SUBDIR= src
USE_QT5= concurrent core network buildtools qmake gui websockets sql USES= pkgconfig tar:xz qmake ssl qt:5
USES= pkgconfig tar:xz qmake ssl USE_QT= concurrent core network buildtools_build qmake_build websockets sql
MAKE_ARGS= PREFIX=${STAGEDIR}${PREFIX} MAKE_ARGS= PREFIX=${STAGEDIR}${PREFIX}
USE_GITHUB= yes 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; rawcmds = cmds;
proclog.insert("cmd_list",QJsonArray::fromStringList(cmds)); proclog.insert("cmd_list",QJsonArray::fromStringList(cmds));
proclog.insert("process_id",ID); proclog.insert("process_id",ID);
proclog.insert("state","pending"); proclog.insert("state","pending");
this->emit ProcUpdate(ID, proclog); this->emit ProcUpdate(ID, proclog);
uptimer->setSingleShot(false); uptimer->setSingleShot(false);
uptimer->setInterval(2000); //2 second intervals for "pending" pings uptimer->setInterval(2000); //2 second intervals for "pending" pings
@@ -42,12 +42,12 @@ void DProcess::procReady(){
void DProcess::startProc(){ void DProcess::startProc(){
cmds.removeAll(""); //make sure no empty commands cmds.removeAll(""); //make sure no empty commands
if(cmds.isEmpty()){ if(cmds.isEmpty()){
proclog.insert("state","finished"); proclog.insert("state","finished");
proclog.insert("time_finished", QDateTime::currentDateTime().toString(Qt::ISODate)); proclog.insert("time_finished", QDateTime::currentDateTime().toString(Qt::ISODate));
proclog.remove("current_cmd"); proclog.remove("current_cmd");
emit ProcFinished(ID, proclog); emit ProcFinished(ID, proclog);
return; return;
} }
if(proclog.value("state").toString()=="pending"){ if(proclog.value("state").toString()=="pending"){
//first cmd started //first cmd started
@@ -66,7 +66,7 @@ void DProcess::startProc(){
} }
bool DProcess::isRunning(){ bool DProcess::isRunning(){
return (this->state()!=QProcess::NotRunning); return (this->state()!=QProcess::NotRunning);
} }
bool DProcess::isDone(){ bool DProcess::isDone(){
@@ -90,10 +90,10 @@ void DProcess::cmdFinished(int ret, QProcess::ExitStatus status){
//update the log before starting another command //update the log before starting another command
proclog.insert(cCmd, proclog.value(cCmd).toString().append(this->readAllStandardOutput()) ); proclog.insert(cCmd, proclog.value(cCmd).toString().append(this->readAllStandardOutput()) );
proclog.insert("return_codes/"+cCmd, QString::number(ret)); proclog.insert("return_codes/"+cCmd, QString::number(ret));
//Now run any additional commands //Now run any additional commands
//qDebug() << "Proc Finished:" << ID << success << proclog; //qDebug() << "Proc Finished:" << ID << success << proclog;
if(success && !cmds.isEmpty()){ if(success && !cmds.isEmpty()){
emit ProcUpdate(ID, proclog); emit ProcUpdate(ID, proclog);
startProc(); startProc();
}else{ }else{
@@ -105,12 +105,18 @@ void DProcess::cmdFinished(int ret, QProcess::ExitStatus status){
} }
void DProcess::updateLog(){ 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(); } if(!uptimer->isActive()){ uptimer->start(); }
} }
void DProcess::emitUpdate(){ 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(){ Dispatcher::~Dispatcher(){
} }
QJsonObject Dispatcher::listJobs(){ QJsonObject Dispatcher::listJobs(){
@@ -153,7 +159,7 @@ QJsonObject Dispatcher::listJobs(){
} }
out.insert(qname,obj); out.insert(qname,obj);
} }
} //end loop over queue types } //end loop over queue types
return out; return out;
} }
@@ -176,6 +182,24 @@ QJsonObject Dispatcher::killJobs(QStringList ids){
return obj; 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){ void Dispatcher::start(QString queuefile){
//Setup connections here (in case it was moved to different thread after creation) //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*)) ); //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) //Overloaded Main Calling Functions (single command, or multiple in-order commands)
DProcess* Dispatcher::queueProcess(QString ID, QString cmd){ DProcess* Dispatcher::queueProcess(QString ID, QString cmd, QString workdir){
return queueProcess(NO_QUEUE, ID, QStringList() << cmd); return queueProcess(NO_QUEUE, ID, QStringList() << cmd, workdir);
} }
DProcess* Dispatcher::queueProcess(QString ID, QStringList cmds){ DProcess* Dispatcher::queueProcess(QString ID, QStringList cmds, QString workdir){
return queueProcess(NO_QUEUE, ID, cmds); return queueProcess(NO_QUEUE, ID, cmds, workdir);
} }
DProcess* Dispatcher::queueProcess(Dispatcher::PROC_QUEUE queue, QString ID, QString cmd){ DProcess* Dispatcher::queueProcess(Dispatcher::PROC_QUEUE queue, QString ID, QString cmd, QString workdir){
return queueProcess(queue, ID, QStringList() << cmd); 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 //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) //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; //qDebug() << "Queue Process:" << queue << ID << cmds;
DProcess *P = createProcess(ID, cmds); DProcess *P = createProcess(ID, cmds, workdir);
this->emit mkprocs(queue, P); this->emit mkprocs(queue, P);
return P; return P;
} }
// === PRIVATE === // === PRIVATE ===
//Simplification routine for setting up a process //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(); DProcess *P = new DProcess();
P->moveToThread(this->thread()); P->moveToThread(this->thread());
P->cmds = cmds; P->cmds = cmds;
P->ID = ID; P->ID = ID;
if(!workdir.isEmpty()){ P->setWorkingDirectory(workdir); }
return P; return P;
} }
// === PRIVATE SLOTS === // === PRIVATE SLOTS ===
void Dispatcher::mkProcs(Dispatcher::PROC_QUEUE queue, DProcess *P){ void Dispatcher::mkProcs(Dispatcher::PROC_QUEUE queue, DProcess *P){
//qDebug() << "mkProcs()"; //qDebug() << "mkProcs()";
QList<DProcess*> list = HASH.value(queue); QList<DProcess*> list = HASH.value(queue);
list << P; list << P;
//qDebug() << " - add to queue:" << queue; //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(ProcFinished(QString, QJsonObject)), this, SLOT(ProcFinished(QString, QJsonObject)) );
connect(P, SIGNAL(ProcUpdate(QString, QJsonObject)), this, SLOT(ProcUpdated(QString, QJsonObject)) ); connect(P, SIGNAL(ProcUpdate(QString, QJsonObject)), this, SLOT(ProcUpdated(QString, QJsonObject)) );
P->procReady(); P->procReady();
@@ -236,7 +261,7 @@ void Dispatcher::ProcFinished(QString ID, QJsonObject log){
//qDebug() << " - Got Proc Finished Signal:" << ID; //qDebug() << " - Got Proc Finished Signal:" << ID;
LogManager::log(LogManager::DISPATCH, log); LogManager::log(LogManager::DISPATCH, log);
//First emit any subsystem-specific event, falling back on the raw 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()){ if(!ev.isEmpty()){
emit DispatchEvent(ev); emit DispatchEvent(ev);
}else{ }else{
@@ -247,7 +272,7 @@ void Dispatcher::ProcFinished(QString ID, QJsonObject log){
void Dispatcher::ProcUpdated(QString ID, QJsonObject log){ void Dispatcher::ProcUpdated(QString ID, QJsonObject log){
//See if this needs to generate an event //See if this needs to generate an event
QJsonObject ev = CreateDispatcherEventNotification(ID,log); QJsonObject ev = CreateDispatcherEventNotification(ID,log, false);
if(!ev.isEmpty()){ if(!ev.isEmpty()){
emit DispatchEvent(ev); emit DispatchEvent(ev);
} }
@@ -280,6 +305,6 @@ for(int i=0; i<enum_length; i++){
} }
} //end loop over list } //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: public:
DProcess(QObject *parent = 0); DProcess(QObject *parent = 0);
~DProcess(); ~DProcess();
QString ID; QString ID;
QStringList cmds; QStringList cmds;
@@ -35,7 +35,7 @@ public slots:
void startProc(); void startProc();
private: private:
QString cCmd; QString cCmd, lognew;
QJsonObject proclog; QJsonObject proclog;
QTimer *uptimer; QTimer *uptimer;
@@ -50,19 +50,20 @@ signals:
//Generic signals for subsystem usage (no direct proc access later) //Generic signals for subsystem usage (no direct proc access later)
void ProcUpdate(QString, QJsonObject); // ID/log void ProcUpdate(QString, QJsonObject); // ID/log
}; };
class Dispatcher : public QObject{ class Dispatcher : public QObject{
Q_OBJECT Q_OBJECT
public: public:
enum PROC_QUEUE { NO_QUEUE = 0, PKG_QUEUE, IOCAGE_QUEUE }; 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 #define enum_length 3 //This needs to be the number of items in the enum above
Dispatcher(); Dispatcher();
~Dispatcher(); ~Dispatcher();
QJsonObject listJobs(); QJsonObject listJobs();
QJsonObject killJobs(QStringList ids); QJsonObject killJobs(QStringList ids);
bool isJobActive(QString ID); //returns true if a job with this ID is running/pending
public slots: public slots:
//Main start/stop //Main start/stop
@@ -70,21 +71,21 @@ public slots:
void stop(); //save any currently-unrun processes for next time void stop(); //save any currently-unrun processes for next time
//Main Calling Functions (single command, or multiple in-order commands) //Main Calling Functions (single command, or multiple in-order commands)
DProcess* queueProcess(QString ID, QString cmd); //uses NO_QUEUE DProcess* queueProcess(QString ID, QString cmd, QString workdir = ""); //uses NO_QUEUE
DProcess* queueProcess(QString ID, QStringList cmds); //uses NO_QUEUE DProcess* queueProcess(QString ID, QStringList cmds, QString workdir = ""); //uses NO_QUEUE
DProcess* queueProcess(Dispatcher::PROC_QUEUE, QString ID, QString cmd); DProcess* queueProcess(Dispatcher::PROC_QUEUE, QString ID, QString cmd, QString workdir = "");
DProcess* queueProcess(Dispatcher::PROC_QUEUE, QString ID, QStringList cmds); DProcess* queueProcess(Dispatcher::PROC_QUEUE, QString ID, QStringList cmds, QString workdir = "");
private: private:
// Queue file // Queue file
QString queue_file; QString queue_file;
//Internal lists //Internal lists
QHash<PROC_QUEUE, QList<DProcess*> > HASH; QHash<PROC_QUEUE, QList<DProcess*> > HASH;
//Simplification routine for setting up a process //Simplification routine for setting up a process
DProcess* createProcess(QString ID, QStringList cmds); DProcess* createProcess(QString ID, QStringList cmds, QString workdir = "");
QJsonObject CreateDispatcherEventNotification(QString, QJsonObject); QJsonObject CreateDispatcherEventNotification(QString, QJsonObject, bool);
// Functions to do parsing out dispatcher queued tasks // Functions to do parsing out dispatcher queued tasks
// Please keep these sorted // Please keep these sorted
@@ -104,7 +105,7 @@ signals:
//Signals for private usage //Signals for private usage
void mkprocs(Dispatcher::PROC_QUEUE, DProcess*); void mkprocs(Dispatcher::PROC_QUEUE, DProcess*);
void checkProcs(); void checkProcs();
}; };
#endif #endif

View File

@@ -9,8 +9,13 @@
#include "globals-qt.h" #include "globals-qt.h"
#include "EventWatcher.h" #include "EventWatcher.h"
#include "Dispatcher.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 //key outputs - need to set these if an event is going to be sent out
QJsonObject args; //any arguments to send out QJsonObject args; //any arguments to send out
QString namesp, name; //the namespace/name of the subsystem used 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 //Add the generic process values
args.insert("state",log.value("state").toString()); args.insert("state",log.value("state").toString());
args.insert("process_details", log); //full process log array here args.insert("process_details", log); //full process log array here
//Now parse the notification based on the dispatch ID or current command //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) //NOTE: There might be a random string on the end of the ID (to accomodate similar process calls)
// == sysadm/iohyve == // == sysadm/iohyve ==
@@ -37,14 +42,18 @@ QJsonObject Dispatcher::CreateDispatcherEventNotification(QString ID, QJsonObjec
//Do some parsing of the log //Do some parsing of the log
parseIohyveFetchOutput(cLog,&args); parseIohyveFetchOutput(cLog,&args);
} }
// == sysadm/update == // == sysadm/update ==
}else if(ID.startsWith("sysadm_update")){ }else if(ID.startsWith("sysadm_update")){
namesp = "sysadm"; name="update"; if(ID.section("::",0,0)=="sysadm_update_runupdates"){
//No special parsing here: the pc-updatemanager output should be available as-is namesp = "sysadm"; name="update";
args.insert("update_log",cLog); //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 == // == sysadm/pkg ==
}else if(ID.startsWith("sysadm_pkg")){ }else if(ID.startsWith("sysadm_pkg")){
namesp = "sysadm"; name="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."); bool hasupdates = !cLog.contains("Your packages are up to date.");
args.insert("updates_available", hasupdates ? "true" : "false"); args.insert("updates_available", hasupdates ? "true" : "false");
} }
}else if(ID.section("-",0,0)=="sysadm_pkg_audit" && isFinished){ }else if(ID.section("-",0,0)=="sysadm_pkg_audit" && isFinished){
QStringList info = cLog.split("\n"); QStringList info = cLog.split("\n");
QStringList vuln, effects; QStringList vuln, effects;
@@ -71,11 +80,21 @@ QJsonObject Dispatcher::CreateDispatcherEventNotification(QString ID, QJsonObjec
args.insert("vulnerable_pkgs",QJsonArray::fromStringList(vuln)); args.insert("vulnerable_pkgs",QJsonArray::fromStringList(vuln));
args.insert("impacts_pkgs",QJsonArray::fromStringList(effects)); 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 //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); args.insert("event_system",namesp+"/"+name);
return args; return args;
} }
@@ -93,3 +112,7 @@ void Dispatcher::parseIohyveFetchOutput(QString outputLog, QJsonObject *out){
break; break;
} }
} }
/*void Dispatcher::parseUpdateCheckOutput(QString outputLog, QJsonObject *out){
}*/

View File

@@ -40,7 +40,7 @@ void EventWatcher::start(){
// - Life Preserver Events // - Life Preserver Events
WatcherUpdate(LPLOG); //load it initially (will also add it to the watcher); WatcherUpdate(LPLOG); //load it initially (will also add it to the watcher);
WatcherUpdate(LPERRLOG); //load it initially (will also add it to the watcher); WatcherUpdate(LPERRLOG); //load it initially (will also add it to the watcher);
filechecktimer->start(); filechecktimer->start();
syschecktimer->start(); syschecktimer->start();
QTimer::singleShot(60000, this, SLOT(CheckSystemState()) ); //wait 1 minute for networking to settle down first 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){ QJsonValue EventWatcher::lastEvent(EVENT_TYPE type){
CheckLogFiles(); CheckLogFiles();
if(HASH.contains(type)){ return HASH.value(type); } if(HASH.contains(type)){ return HASH.value(type); }
else{ qDebug() << "No saved event:" << type; return QJsonValue(); } else{ return QJsonValue(); }
} }
// === PRIVATE === // === PRIVATE ===
@@ -88,11 +88,11 @@ QString EventWatcher::readFile(QString path){
QString contents = in.readAll(); QString contents = in.readAll();
file.close(); file.close();
if(contents.endsWith("\n")){ contents.chop(1); } if(contents.endsWith("\n")){ contents.chop(1); }
return contents; return contents;
} }
double EventWatcher::displayToDoubleK(QString displayNumber){ double EventWatcher::displayToDoubleK(QString displayNumber){
QStringList labels; QStringList labels;
labels << "K" << "M" << "G" << "T" << "P" << "E"; labels << "K" << "M" << "G" << "T" << "P" << "E";
QString clab = displayNumber.right(1); //last character is the size label QString clab = displayNumber.right(1); //last character is the size label
displayNumber.chop(1); //remove the label from the number displayNumber.chop(1); //remove the label from the number
@@ -122,12 +122,12 @@ void EventWatcher::DispatchStarting(QString ID){
void EventWatcher::DispatchEvent(QJsonObject obj){ void EventWatcher::DispatchEvent(QJsonObject obj){
LogManager::log(LogManager::EV_DISPATCH, obj); LogManager::log(LogManager::EV_DISPATCH, obj);
//qDebug() << "Got Dispatch Finished: sending event..."; //qDebug() << "Got Dispatch Finished: sending event...";
emit NewEvent(DISPATCHER, obj); emit NewEvent(DISPATCHER, obj);
} }
// === PRIVATE SLOTS === // === PRIVATE SLOTS ===
void EventWatcher::WatcherUpdate(const QString &path){ void EventWatcher::WatcherUpdate(const QString &path){
if(!starting){ qDebug() << "Event Watcher Update:" << path; } //if(!starting){ qDebug() << "Event Watcher Update:" << path; }
if(path==LPLOG){ if(path==LPLOG){
//Main Life Preserver Log File //Main Life Preserver Log File
ReadLPLogFile(); ReadLPLogFile();
@@ -159,11 +159,12 @@ void EventWatcher::CheckLogFiles(){
void EventWatcher::ReadLPLogFile(){ void EventWatcher::ReadLPLogFile(){
//Open/Read any new info in the file //Open/Read any new info in the file
QFile LPlogfile(LPLOG); QFile LPlogfile(LPLOG);
if( !LPlogfile.exists() ){ return; }
if( !LPlogfile.open(QIODevice::ReadOnly) ){ return; } //could not open file if( !LPlogfile.open(QIODevice::ReadOnly) ){ return; } //could not open file
QTextStream STREAM(&LPlogfile); QTextStream STREAM(&LPlogfile);
qint64 LPlog_pos = CONFIG->value("internal/"+QString(WS_MODE ? "ws" : "tcp")+"/lp-log-pos",0).toLongLong(); 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() ){ if(LPlog_pos>0 && QFileInfo(LPlogfile).created() < CONFIG->value("internal/"+QString(WS_MODE ? "ws" : "tcp")+"/lp-log-lastread").toDateTime() ){
STREAM.seek(LPlog_pos); STREAM.seek(LPlog_pos);
} }
QStringList info = STREAM.readAll().split("\n"); QStringList info = STREAM.readAll().split("\n");
//Now save the file pointer for later //Now save the file pointer for later
@@ -174,7 +175,7 @@ void EventWatcher::ReadLPLogFile(){
for(int i=0; i<info.length(); i++){ for(int i=0; i<info.length(); i++){
if(info[i].isEmpty()){ continue; } if(info[i].isEmpty()){ continue; }
QString log = info[i]; 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 //Divide up the log into it's sections
QString timestamp = log.section(":",0,2).simplified(); QString timestamp = log.section(":",0,2).simplified();
QString time = timestamp.section(" ",3,3).simplified(); QString time = timestamp.section(" ",3,3).simplified();
@@ -243,16 +244,15 @@ void EventWatcher::ReadLPLogFile(){
HASH.insert(122, tr("Replication Failed") ); //summary HASH.insert(122, tr("Replication Failed") ); //summary
HASH.insert(123, tt ); HASH.insert(123, tt );
HASH.insert(124, timestamp); //full timestamp 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+">" ); HASH.insert(126, tr("Replication Error Log")+" <"+file+">" );
sendLPEvent("replication", 7, timestamp+": "+tt); sendLPEvent("replication", 7, timestamp+": "+tt);
} }
} }
} }
void EventWatcher::ReadLPErrFile(){ void EventWatcher::ReadLPErrFile(){
} }
void EventWatcher::ReadLPRepFile(){ 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 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(); QString lastSize = CONFIG->value("internal/"+QString(WS_MODE ? "ws" : "tcp")+"/lp-rep-lastsize","").toString();
//Open/Read any new info in the file //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 if( !LPlogfile.open(QIODevice::ReadOnly) ){ return; } //could not open file
QTextStream STREAM(&LPlogfile); QTextStream STREAM(&LPlogfile);
qint64 LPrep_pos = CONFIG->value("internal/"+QString(WS_MODE ? "ws" : "tcp")+"/lp-rep-pos",0).toLongLong(); 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 //New file location
stat.clear(); stat.clear();
repTotK.clear(); repTotK.clear();
lastSize.clear(); lastSize.clear();
} }
QStringList info = STREAM.readAll().split("\n"); QStringList info = STREAM.readAll().split("\n");
CONFIG->setValue("internal/"+QString(WS_MODE ? "ws" : "tcp")+"/lp-rep-pos",STREAM.pos()); 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 else{ stat = line; } //only save the relevant/latest status line
} }
if(!stat.isEmpty()){ if(!stat.isEmpty()){
//qDebug() << "New Status Message:" << stat; //qDebug() << "New Status Message:" << stat;
//Divide up the status message into sections //Divide up the status message into sections
stat.replace("\t"," "); stat.replace("\t"," ");
QString dataset = stat.section(" ",2,2,QString::SectionSkipEmpty).section("/",0,0).simplified(); QString dataset = stat.section(" ",2,2,QString::SectionSkipEmpty).section("/",0,0).simplified();
@@ -343,7 +344,7 @@ void EventWatcher::CheckSystemState(){
} }
obj.insert("hostname",oldhostname); obj.insert("hostname",oldhostname);
//Next Check zpools //Next Check zpools
QJsonObject zpools = sysadm::ZFS::zpool_list(); QJsonObject zpools = sysadm::ZFS::zpool_list();
if(!zpools.isEmpty()){ if(!zpools.isEmpty()){
//Scan each pool for any bad indicators //Scan each pool for any bad indicators
@@ -369,10 +370,11 @@ void EventWatcher::CheckSystemState(){
//Next Check for Updates //Next Check for Updates
QJsonObject updates = sysadm::Update::checkUpdates(true); //do the "fast" version of 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.isEmpty()){
if(updates.value("status").toString()!="noupdates"){ if(updates.value("status").toString()!="noupdates"){
int tmp = 2; int tmp = 2;
if(updates.value("status").toString()=="rebootrequired"){ if(updates.value("status").toString()=="rebootrequired"){
tmp = 9; //user input required tmp = 9; //user input required
//Check if the auto_update_reboot flag is set, and reboot as needed //Check if the auto_update_reboot flag is set, and reboot as needed
QJsonObject upset = sysadm::Update::readSettings(); QJsonObject upset = sysadm::Update::readSettings();
@@ -384,10 +386,12 @@ void EventWatcher::CheckSystemState(){
QDateTime finished = sysadm::Update::rebootRequiredSince(); QDateTime finished = sysadm::Update::rebootRequiredSince();
QDateTime cdt = QDateTime::currentDateTime(); 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 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"){ }else if(updates.value("status").toString()!="updaterunning"){
//updates are available - see if the auto-update flag is set, and start the updates as needed //updates are available - see if the auto-update flag is set, and start the updates as needed
QJsonObject upset = sysadm::Update::readSettings(); QJsonObject upset = sysadm::Update::readSettings();

View File

@@ -18,7 +18,7 @@ void LogManager::checkLogDir(){
QDir dir(logd); QDir dir(logd);
dir.mkpath(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){ if(daysold>0){
LogManager::pruneLogs(QDate::currentDate().addDays(0-daysold)); LogManager::pruneLogs(QDate::currentDate().addDays(0-daysold));
} }
@@ -35,7 +35,7 @@ void LogManager::pruneLogs(QDate olderthan){
for(int i=0; i<files.length(); i++){ for(int i=0; i<files.length(); i++){
QDate fdate = QDate::fromString( files[i].section(".log",0,0).section("-",-3,-1), Qt::ISODate); QDate fdate = QDate::fromString( files[i].section(".log",0,0).section("-",-3,-1), Qt::ISODate);
//qDebug() << "Check File Date:" << fdate << olderthan; //qDebug() << "Check File Date:" << fdate << olderthan;
if( fdate < olderthan && fdate.isValid()){ if( fdate < olderthan && fdate.isValid()){
dir.remove(files[i]); dir.remove(files[i]);
} }
} }

View File

@@ -25,6 +25,7 @@
#include "library/sysadm-firewall.h" #include "library/sysadm-firewall.h"
#include "library/sysadm-moused.h" #include "library/sysadm-moused.h"
#include "library/sysadm-powerd.h" #include "library/sysadm-powerd.h"
#include "library/sysadm-sourcectl.h"
#define DEBUG 0 #define DEBUG 0
//#define SCLISTDELIM QString("::::") //SysCache List Delimiter //#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"); out->insert("rpc/logs", allaccess ? "read/write" : "read");
// - beadm // - 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"); out->insert("sysadm/beadm", "read/write");
} }
// - dispatcher (Internal to server - always available) // - dispatcher (Internal to server - always available)
//"read" is the event notifications, "write" is the ability to queue up jobs //"read" is the event notifications, "write" is the ability to queue up jobs
out->insert("rpc/dispatcher", allaccess ? "read/write" : "read"); 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")){ if(QFile::exists("/usr/local/sbin/iohyve")){
out->insert("sysadm/iohyve", "read/write"); out->insert("sysadm/iohyve", "read/write");
} }
// - zfs // - zfs
if(QFile::exists("/sbin/zfs") && QFile::exists("/sbin/zpool")){ if(QFile::exists("/sbin/zfs") && QFile::exists("/sbin/zpool")){
out->insert("sysadm/zfs", allaccess ? "read/write" : "read"); out->insert("sysadm/zfs", allaccess ? "read/write" : "read");
} }
// - pkg // - 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"); out->insert("sysadm/pkg", "read/write");
} }
// - Generic system information // - Generic system information
out->insert("sysadm/systemmanager","read/write"); out->insert("sysadm/systemmanager","read/write");
// - PC-BSD/TrueOS Updater // - Legacy PC-BSD/TrueOS Updater
if(QFile::exists("/usr/local/bin/pc-updatemanager")){ if( QFile::exists("/usr/local/bin/pc-updatemanager") ){
out->insert("sysadm/update", "read/write"); out->insert("sysadm/update", "read/write");
} }
@@ -104,6 +104,10 @@ RestOutputStruct::ExitCode WebSocket::AvailableSubsystems(bool allaccess, QJsonO
if(QFile::exists("/usr/sbin/powerd")){ if(QFile::exists("/usr/sbin/powerd")){
out->insert("sysadm/powerd", "read/write"); out->insert("sysadm/powerd", "read/write");
} }
// - sourcectl
if(QFile::exists("/usr/local/bin/git")){
out->insert("sysadm/sourcectl", "read/write");
}
return RestOutputStruct::OK; return RestOutputStruct::OK;
} }
@@ -164,6 +168,8 @@ RestOutputStruct::ExitCode WebSocket::EvaluateBackendRequest(const RestInputStru
return EvaluateSysadmMousedRequest(IN.args, out); return EvaluateSysadmMousedRequest(IN.args, out);
}else if(namesp=="sysadm" && name=="powerd"){ }else if(namesp=="sysadm" && name=="powerd"){
return EvaluateSysadmPowerdRequest(IN.args, out); return EvaluateSysadmPowerdRequest(IN.args, out);
}else if(namesp=="sysadm" && name=="sourcectl"){
return EvaluateSysadmSourceCTLRequest(IN.args, out);
}else{ }else{
return RestOutputStruct::BADREQUEST; return RestOutputStruct::BADREQUEST;
} }
@@ -187,7 +193,7 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmSettingsRequest(const QJsonV
pub_key = argsO.value("pub_key").toString(); pub_key = argsO.value("pub_key").toString();
if(keys.contains("nickname")){ nickname = argsO.value("nickname").toString(); } if(keys.contains("nickname")){ nickname = argsO.value("nickname").toString(); }
if(keys.contains("email")){ email = argsO.value("email").toString(); } if(keys.contains("email")){ email = argsO.value("email").toString(); }
if(!pub_key.isEmpty()){ if(!pub_key.isEmpty()){
ok = AUTHSYSTEM->RegisterCertificate(SockAuthToken, pub_key, nickname, email); ok = AUTHSYSTEM->RegisterCertificate(SockAuthToken, pub_key, nickname, email);
if(!ok){ return RestOutputStruct::FORBIDDEN; } 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-dispatcher"){ log = 2; }
else if(logs[i]=="events-lifepreserver"){ log = 3; } else if(logs[i]=="events-lifepreserver"){ log = 3; }
else if(logs[i]=="events-state"){ log = 4; } else if(logs[i]=="events-state"){ log = 4; }
if(log>=0){ if(log>=0){
QStringList info = LogManager::readLog( (LogManager::LOG_FILE)(log), starttime, endtime); QStringList info = LogManager::readLog( (LogManager::LOG_FILE)(log), starttime, endtime);
//REMINDER of format: "[datetime]<message>" //REMINDER of format: "[datetime]<message>"
@@ -366,7 +372,7 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmBEADMRequest(const QJsonValu
}else if(act=="umountbe"){ }else if(act=="umountbe"){
ok = true; ok = true;
out->insert("umountbe", sysadm::BEADM::umountBE(in_args.toObject())); out->insert("umountbe", sysadm::BEADM::umountBE(in_args.toObject()));
} }
} //end of "action" key usage } //end of "action" key usage
//If nothing done - return the proper code //If nothing done - return the proper code
@@ -409,9 +415,9 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmNetworkRequest(const QJsonVa
bool ok = false; bool ok = false;
if(keys.contains("action")){ if(keys.contains("action")){
QString act = JsonValueToString(in_args.toObject().value("action")); QString act = JsonValueToString(in_args.toObject().value("action"));
QStringList devs = sysadm::NetDevice::listNetDevices();
if(act=="list-devices"){ if(act=="list-devices"){
ok = true; ok = true;
QStringList devs = sysadm::NetDevice::listNetDevices();
for(int i=0; i<devs.length(); i++){ for(int i=0; i<devs.length(); i++){
sysadm::NetDevice D(devs[i]); sysadm::NetDevice D(devs[i]);
QJsonObject obj; QJsonObject obj;
@@ -428,6 +434,31 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmNetworkRequest(const QJsonVa
//Add this device info to the main output structure //Add this device info to the main output structure
out->insert(devs[i], obj); 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 } //end of "action" key usage
@@ -529,50 +560,58 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmSystemMgmtRequest(const QJso
ok = true; ok = true;
out->insert("batteryinfo", sysadm::SysMgmt::batteryInfo()); out->insert("batteryinfo", sysadm::SysMgmt::batteryInfo());
} }
if(act=="cpupercentage"){ else if(act=="cpupercentage"){
ok = true; ok = true;
out->insert("cpupercentage", sysadm::SysMgmt::cpuPercentage()); out->insert("cpupercentage", sysadm::SysMgmt::cpuPercentage());
} }
if(act=="cputemps"){ else if(act=="cputemps"){
ok = true; ok = true;
out->insert("cputemps", sysadm::SysMgmt::cpuTemps()); out->insert("cputemps", sysadm::SysMgmt::cpuTemps());
} }
if(act=="externalmounts"){ else if(act=="externalmounts"){
ok = true; ok = true;
out->insert("externalmounts", sysadm::SysMgmt::externalDevicePaths()); out->insert("externalmounts", sysadm::SysMgmt::externalDevicePaths());
} }
if(act=="halt"){ else if(act=="halt"){
ok = true; ok = true;
out->insert("halt", sysadm::SysMgmt::systemHalt()); out->insert("halt", sysadm::SysMgmt::systemHalt());
} }
if(act=="killproc"){ else if(act=="killproc"){
ok = true; ok = true;
out->insert("killproc", sysadm::SysMgmt::killProc(in_args.toObject())); out->insert("killproc", sysadm::SysMgmt::killProc(in_args.toObject()));
} }
if(act=="memorystats"){ else if(act=="memorystats"){
ok = true; ok = true;
out->insert("memorystats", sysadm::SysMgmt::memoryStats()); out->insert("memorystats", sysadm::SysMgmt::memoryStats());
} }
if(act=="procinfo"){ else if(act=="procinfo"){
ok = true; ok = true;
out->insert("procinfo", sysadm::SysMgmt::procInfo()); out->insert("procinfo", sysadm::SysMgmt::procInfo());
} }
if(act=="reboot"){ else if(act=="reboot"){
ok = true; ok = true;
out->insert("reboot", sysadm::SysMgmt::systemReboot()); 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; ok = true;
out->insert("setsysctl", sysadm::SysMgmt::setSysctl(in_args.toObject())); out->insert("setsysctl", sysadm::SysMgmt::setSysctl(in_args.toObject()));
} }
if(act=="sysctllist"){ else if(act=="sysctllist"){
ok = true; ok = true;
out->insert("sysctllist", sysadm::SysMgmt::sysctlList()); out->insert("sysctllist", sysadm::SysMgmt::sysctlList());
} }
if(act=="systeminfo"){ else if(act=="systeminfo"){
ok = true; ok = true;
out->insert("systeminfo", sysadm::SysMgmt::systemInfo()); out->insert("systeminfo", sysadm::SysMgmt::systemInfo());
} }
else if(act=="deviceinfo"){
ok = true;
out->insert("deviceinfo", sysadm::SysMgmt::systemDevices());
}
} //end of "action" key usage } //end of "action" key usage
@@ -598,11 +637,11 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmUpdateRequest(const QJsonVal
bool fastcheck = true; bool fastcheck = true;
fastcheck = in_args.toObject().value("force").toString().toLower()!="true"; fastcheck = in_args.toObject().value("force").toString().toLower()!="true";
out->insert("checkupdates", sysadm::Update::checkUpdates(fastcheck)); out->insert("checkupdates", sysadm::Update::checkUpdates(fastcheck));
}else if(act=="listbranches"){ }else if(act=="listbranches"){
ok = true; ok = true;
out->insert("listbranches", sysadm::Update::listBranches()); out->insert("listbranches", sysadm::Update::listBranches());
}else if(act=="startupdate"){ }else if(act=="startupdate"){
ok = true; ok = true;
out->insert("startupdate", sysadm::Update::startUpdate(in_args.toObject()) ); out->insert("startupdate", sysadm::Update::startUpdate(in_args.toObject()) );
@@ -611,6 +650,10 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmUpdateRequest(const QJsonVal
ok = true; ok = true;
out->insert("stopupdate", sysadm::Update::stopUpdate() ); out->insert("stopupdate", sysadm::Update::stopUpdate() );
}else if(act=="applyupdate"){
ok = true;
out->insert("applyupdate", sysadm::Update::applyUpdates() );
}else if(act=="listsettings"){ }else if(act=="listsettings"){
ok = true; ok = true;
out->insert("listsettings", sysadm::Update::readSettings() ); out->insert("listsettings", sysadm::Update::readSettings() );
@@ -650,7 +693,7 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmIocageRequest(const QJsonVal
QJsonObject retObj; QJsonObject retObj;
if(act=="activatepool"){ retObj = sysadm::Iocage::activatePool(in_args.toObject()); } if(act=="activatepool"){ retObj = sysadm::Iocage::activatePool(in_args.toObject()); }
else if(act=="deactivatepool"){retObj = sysadm::Iocage::deactivatePool(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"){ /*if(act=="execjail"){
ok = true; ok = true;
out->insert("execjail", sysadm::Iocage::execJail(in_args.toObject())); out->insert("execjail", sysadm::Iocage::execJail(in_args.toObject()));
@@ -675,14 +718,6 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmIocageRequest(const QJsonVal
ok = true; ok = true;
out->insert("cleanall", sysadm::Iocage::cleanAll()); 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"){ else if(act=="cleanjails"){
ok = true; ok = true;
out->insert("cleanjails", sysadm::Iocage::cleanJails()); out->insert("cleanjails", sysadm::Iocage::cleanJails());
@@ -702,19 +737,21 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmIocageRequest(const QJsonVal
else if(act=="getjailsettings"){ else if(act=="getjailsettings"){
ok = true; ok = true;
out->insert("getjailsettings", sysadm::Iocage::getJailSettings(in_args.toObject())); 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(); ok = !retObj.keys().isEmpty();
if(ok){ out->insert(act,retObj); } if(ok){ out->insert(act,retObj); }
} //end of "action" key usage } //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(); } 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()); } else if(in_args.toObject().value("pkg_origins").isArray()){ pkgs = JsonArrayToStringList(in_args.toObject().value("pkg_origins").toArray()); }
} }
//Parse the action and perform accordingly //Parse the action and perform accordingly
if(act=="pkg_info"){ if(act=="pkg_info"){
//OPTIONAL: "pkg_origins" OR "category" //OPTIONAL: "pkg_origins" OR "category"
//OPTIONAL: "repo" //OPTIONAL: "repo"
//OPTIONAL: "result" = "full" or "simple" (Default: "simple") //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"); } if(in_args.toObject().contains("result")){ fullresults = (in_args.toObject().value("result").toString()=="full"); }
//Now run the info fetch routine //Now run the info fetch routine
QJsonObject info = sysadm::PKG::pkg_info(pkgs, repo, cat, fullresults); QJsonObject info = sysadm::PKG::pkg_info(pkgs, repo, cat, fullresults);
if(!info.isEmpty()){ out->insert("pkg_info",info); } if(!info.isEmpty()){ out->insert("pkg_info",info); }
else{ return RestOutputStruct::NOCONTENT; } else{ return RestOutputStruct::NOCONTENT; }
}else if(act=="pkg_search" && in_args.toObject().contains("search_term")){ }else if(act=="pkg_search" && in_args.toObject().contains("search_term")){
//REQUIRED: "search_term" (string to search for) //REQUIRED: "search_term" (string to search for)
//OPTIONAL: "repo" //OPTIONAL: "repo"
@@ -897,22 +934,26 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmPkgRequest(const QJsonValue
}else{ }else{
return RestOutputStruct::NOCONTENT; return RestOutputStruct::NOCONTENT;
} }
}else if(act=="list_categories"){ }else if(act=="list_categories"){
//OPTIONAL: "repo" //OPTIONAL: "repo"
QJsonArray cats = sysadm::PKG::list_categories(repo); QJsonArray cats = sysadm::PKG::list_categories(repo);
if(!cats.isEmpty()){ out->insert("list_categories", cats); } if(!cats.isEmpty()){ out->insert("list_categories", cats); }
else{ return RestOutputStruct::NOCONTENT; } else{ return RestOutputStruct::NOCONTENT; }
}else if(act=="list_repos"){ }else if(act=="list_repos"){
QJsonArray repos = sysadm::PKG::list_repos(); QJsonArray repos = sysadm::PKG::list_repos();
if(!repos.isEmpty()){ out->insert("list_repos", repos); } if(!repos.isEmpty()){ out->insert("list_repos", repos); }
else{ return RestOutputStruct::NOCONTENT; } else{ return RestOutputStruct::NOCONTENT; }
}else if(act=="pkg_install" && !pkgs.isEmpty() ){ }else if(act=="pkg_install" && !pkgs.isEmpty() ){
//REQUIRED: "pkg_origins" //REQUIRED: "pkg_origins"
//OPTIONAL: "repo" (pkg will determine the best repo to use if not supplied) //OPTIONAL: "repo" (pkg will determine the best repo to use if not supplied)
out->insert("pkg_install", sysadm::PKG::pkg_install(pkgs,repo)); 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() ){ }else if(act=="pkg_remove" && !pkgs.isEmpty() ){
//REQUIRED: "pkg_origins" //REQUIRED: "pkg_origins"
//OPTIONAL: "recursive"="true" or "false" (default: "true") //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)); out->insert("pkg_remove", sysadm::PKG::pkg_remove(pkgs, recursive));
}else if(act=="pkg_lock" && !pkgs.isEmpty() ){ }else if(act=="pkg_lock" && !pkgs.isEmpty() ){
//REQUIRED: "pkg_origins" //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() ){ }else if(act=="pkg_unlock" && !pkgs.isEmpty() ){
//REQUIRED: "pkg_origins" //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"){ }else if(act=="pkg_update"){
//OPTIONAL: "force" = ["true"/"false"] (default: "false") //OPTIONAL: "force" = ["true"/"false"] (default: "false")
bool force = false; bool force = false;
@@ -942,7 +983,7 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmPkgRequest(const QJsonValue
//unknown action //unknown action
return RestOutputStruct::BADREQUEST; return RestOutputStruct::BADREQUEST;
} }
return RestOutputStruct::OK; return RestOutputStruct::OK;
} }
@@ -1245,6 +1286,12 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmMousedRequest(const QJsonVal
outobj = sysadm::moused::enableDevice(in_args.toObject()); outobj = sysadm::moused::enableDevice(in_args.toObject());
}else if(action == "set_device_inactive"){ }else if(action == "set_device_inactive"){
outobj = sysadm::moused::disableDevice(in_args.toObject()); 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 //check return structure for validity
@@ -1286,3 +1333,33 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmPowerdRequest(const QJsonVal
return RestOutputStruct::BADREQUEST; 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; 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("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")); } if(out.in_struct.args.toObject().contains("password")){ pass = JsonValueToString(out.in_struct.args.toObject().value("password")); }
//Use the given password //Use the given password
cur_auth_tok = AUTHSYSTEM->LoginUP(host, user, pass); cur_auth_tok = AUTHSYSTEM->LoginUP(host, user, pass);
}else if(out.in_struct.name=="auth_ssl"){ }else if(out.in_struct.name=="auth_ssl"){
@@ -294,7 +294,7 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){
out.CODE = RestOutputStruct::OK; out.CODE = RestOutputStruct::OK;
QString msg = out.assembleMessage(); QString msg = out.assembleMessage();
if(SOCKET!=0 && !REQ.bridgeID.isEmpty()){ 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) //Note that the Stage 1 SSL auth reply is only partially encrypted (specific variables only, not bulk message encryption)
//Now add the destination ID //Now add the destination ID
msg.prepend( REQ.bridgeID+"\n"); msg.prepend( REQ.bridgeID+"\n");
@@ -307,10 +307,10 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){
}else if(out.in_struct.name == "auth_clear"){ }else if(out.in_struct.name == "auth_clear"){
return; //don't send a return message after clearing an auth (already done) return; //don't send a return message after clearing an auth (already done)
} }
//Now check the auth and respond appropriately //Now check the auth and respond appropriately
if(AUTHSYSTEM->checkAuth(cur_auth_tok)){ if(AUTHSYSTEM->checkAuth(cur_auth_tok)){
//Good Authentication - return the new token //Good Authentication - return the new token
QJsonArray array; QJsonArray array;
array.append(cur_auth_tok); array.append(cur_auth_tok);
array.append(AUTHSYSTEM->checkAuthTimeoutSecs(cur_auth_tok)); array.append(AUTHSYSTEM->checkAuthTimeoutSecs(cur_auth_tok));
@@ -325,25 +325,25 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){
//Bad Authentication - return error //Bad Authentication - return error
out.CODE = RestOutputStruct::UNAUTHORIZED; 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 //Now provide access to the various subsystems
// First get/set the permissions flag into the input structure // First get/set the permissions flag into the input structure
out.in_struct.fullaccess = AUTHSYSTEM->hasFullAccess(cur_auth_tok); out.in_struct.fullaccess = AUTHSYSTEM->hasFullAccess(cur_auth_tok);
//Pre-set any output fields //Pre-set any output fields
QJsonObject outargs; QJsonObject outargs;
out.CODE = EvaluateBackendRequest(out.in_struct, &outargs); out.CODE = EvaluateBackendRequest(out.in_struct, &outargs);
out.out_args = outargs; out.out_args = outargs;
}else{ }else{
//Bad/No authentication //Bad/No authentication
out.CODE = RestOutputStruct::UNAUTHORIZED; out.CODE = RestOutputStruct::UNAUTHORIZED;
} }
}else if(out.in_struct.namesp.toLower() == "events"){ }else if(out.in_struct.namesp.toLower() == "events"){
//qDebug() << "Got Event subsytem request" << out.in_struct.args; //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 //Pre-set any output fields
QJsonObject outargs; QJsonObject outargs;
//Assemble the list of input events //Assemble the list of input events
QStringList evlist; QStringList evlist;
if(out.in_struct.args.isString()){ evlist << JsonValueToString(out.in_struct.args); } 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++){ for(int i=0; i<evlist.length(); i++){
EventWatcher::EVENT_TYPE type = EventWatcher::typeFromString(evlist[i]); EventWatcher::EVENT_TYPE type = EventWatcher::typeFromString(evlist[i]);
//qDebug() << " - type:" << type; //qDebug() << " - type:" << type;
if(isBridge){ if(isBridge){
ForwardEvents.clear(); ForwardEvents.clear();
if(!REQ.bridgeID.isEmpty()){ ForwardEvents = BRIDGE[REQ.bridgeID].sendEvents; } if(!REQ.bridgeID.isEmpty()){ ForwardEvents = BRIDGE[REQ.bridgeID].sendEvents; }
} }
if(type==EventWatcher::BADEVENT){ continue; } if(type==EventWatcher::BADEVENT){ continue; }
outargs.insert(out.in_struct.name,QJsonValue(evlist[i])); outargs.insert(out.in_struct.name,QJsonValue(evlist[i]));
if(sub==1){ if(sub==1){
ForwardEvents << type; ForwardEvents << type;
EventUpdate(type); EventUpdate(type);
}else{ }else{
ForwardEvents.removeAll(type); ForwardEvents.removeAll(type);
@@ -375,7 +375,7 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){
out.CODE = RestOutputStruct::OK; out.CODE = RestOutputStruct::OK;
}else{ }else{
//Bad/No authentication //Bad/No authentication
out.CODE = RestOutputStruct::BADREQUEST; out.CODE = RestOutputStruct::BADREQUEST;
} }
}else{ }else{
//Bad/No authentication //Bad/No authentication
@@ -386,7 +386,7 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){
//qDebug() << "Within special bridge section"; //qDebug() << "Within special bridge section";
out.in_struct.fullaccess = false; out.in_struct.fullaccess = false;
//Pre-set any output fields //Pre-set any output fields
QJsonObject outargs; QJsonObject outargs;
out.CODE = EvaluateBackendRequest(out.in_struct, &outargs); out.CODE = EvaluateBackendRequest(out.in_struct, &outargs);
out.out_args = outargs; out.out_args = outargs;
}else if( AUTHSYSTEM->checkAuth(cur_auth_tok) ){ //validate current Authentication token }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 // First get/set the permissions flag into the input structure
out.in_struct.fullaccess = AUTHSYSTEM->hasFullAccess(cur_auth_tok); out.in_struct.fullaccess = AUTHSYSTEM->hasFullAccess(cur_auth_tok);
//Pre-set any output fields //Pre-set any output fields
QJsonObject outargs; QJsonObject outargs;
out.CODE = EvaluateBackendRequest(out.in_struct, &outargs); out.CODE = EvaluateBackendRequest(out.in_struct, &outargs);
out.out_args = outargs; out.out_args = outargs;
}else{ }else{
@@ -413,7 +413,7 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){
QString msg = out.assembleMessage(); QString msg = out.assembleMessage();
if(SOCKET!=0 && !REQ.bridgeID.isEmpty()){ if(SOCKET!=0 && !REQ.bridgeID.isEmpty()){
//BRIDGE RELAY - alternate format //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 //Now add the destination ID
msg.prepend( REQ.bridgeID+"\n"); msg.prepend( REQ.bridgeID+"\n");
} }
@@ -421,7 +421,7 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){
this->sendReply(msg); this->sendReply(msg);
SOCKET->close(QWebSocketProtocol::CloseCodeNormal, "Too Many Authorization Failures - Try again later"); SOCKET->close(QWebSocketProtocol::CloseCodeNormal, "Too Many Authorization Failures - Try again later");
}else{ }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++){ for(int i=0; i<array.count(); i++){
out << JsonValueToString(array.at(i)); out << JsonValueToString(array.at(i));
} }
return out; return out;
} }
// ===================== // =====================
@@ -532,10 +532,10 @@ void WebSocket::checkAuth(){
void WebSocket::SocketClosing(){ void WebSocket::SocketClosing(){
LogManager::log(LogManager::HOST,"Connection Closing: "+SockPeerIP); 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 //This means the client deliberately closed the connection - not the idle timer
//qDebug() << " - Client Closed Connection"; //qDebug() << " - Client Closed Connection";
idletimer->stop(); idletimer->stop();
}else{ }else{
//qDebug() << "idleTimer not running"; //qDebug() << "idleTimer not running";
} }
@@ -544,23 +544,23 @@ void WebSocket::SocketClosing(){
//Reset the pointer //Reset the pointer
if(SOCKET!=0){ SOCKET = 0; } if(SOCKET!=0){ SOCKET = 0; }
if(TSOCKET!=0){ TSOCKET = 0; } if(TSOCKET!=0){ TSOCKET = 0; }
emit SocketClosed(SockID); emit SocketClosed(SockID);
} }
void WebSocket::EvaluateMessage(const QByteArray &msg){ void WebSocket::EvaluateMessage(const QByteArray &msg){
//qDebug() << "New Binary Message:"; //qDebug() << "New Binary Message:";
if(idletimer->isActive()){ idletimer->stop(); } if(idletimer->isActive()){ idletimer->stop(); }
idletimer->start(); idletimer->start();
EvaluateREST( QString(msg) ); EvaluateREST( QString(msg) );
//qDebug() << " - Done with Binary Message"; //qDebug() << " - Done with Binary Message";
} }
void WebSocket::EvaluateMessage(const QString &msg){ void WebSocket::EvaluateMessage(const QString &msg){
//qDebug() << "New Text Message:" << msg; //qDebug() << "New Text Message:" << msg;
if(idletimer->isActive()){ idletimer->stop(); } if(idletimer->isActive()){ idletimer->stop(); }
idletimer->start(); idletimer->start();
EvaluateREST(msg); EvaluateREST(msg);
//qDebug() << " - Done with Text Message"; //qDebug() << " - Done with Text Message";
} }
@@ -609,8 +609,8 @@ void WebSocket::EvaluateTcpMessage(){
// Check for JSON in this incoming data // Check for JSON in this incoming data
ParseIncoming(); ParseIncoming();
idletimer->start(); idletimer->start();
//qDebug() << " - Done with TCP Message"; //qDebug() << " - Done with TCP Message";
} }
//SSL signal handling //SSL signal handling

View File

@@ -100,6 +100,8 @@ private:
RestOutputStruct::ExitCode EvaluateSysadmMousedRequest(const QJsonValue in_args, QJsonObject *out); RestOutputStruct::ExitCode EvaluateSysadmMousedRequest(const QJsonValue in_args, QJsonObject *out);
// -- sysadm powerd API // -- sysadm powerd API
RestOutputStruct::ExitCode EvaluateSysadmPowerdRequest(const QJsonValue in_args, QJsonObject *out); RestOutputStruct::ExitCode EvaluateSysadmPowerdRequest(const QJsonValue in_args, QJsonObject *out);
// -- sysadm sourcectl API
RestOutputStruct::ExitCode EvaluateSysadmSourceCTLRequest(const QJsonValue in_args, QJsonObject *out);
private slots: private slots:
void sendReply(QString msg); void sendReply(QString msg);

View File

@@ -4,6 +4,11 @@
// Available under the 3-clause BSD license // Available under the 3-clause BSD license
// See the LICENSE file for full details // 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 // 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 // the first PC-BSD library, and copied here by Ken Moore in 2015
//=========================================== //===========================================
@@ -15,7 +20,7 @@
using namespace sysadm; using namespace sysadm;
//==================== //====================
// STATIC LISTING FUNCTION // STATIC LISTING FUNCTION
//==================== //====================
QStringList NetDevice::listNetDevices(){ QStringList NetDevice::listNetDevices(){
QStringList result; QStringList result;
@@ -28,7 +33,7 @@ QStringList NetDevice::listNetDevices(){
if (result.contains(ifName) == 0) result += ifName; if (result.contains(ifName) == 0) result += ifName;
ifap = ifap->ifa_next; ifap = ifap->ifa_next;
} }
//Close the //Close the structure
freeifaddrs(ifap); freeifaddrs(ifap);
return result; return result;
} }
@@ -57,10 +62,10 @@ QString NetDevice::ipAsString(){
strncpy(ifr.ifr_name, name.toLocal8Bit(), IFNAMSIZ); strncpy(ifr.ifr_name, name.toLocal8Bit(), IFNAMSIZ);
int s = socket(PF_INET, SOCK_DGRAM, 0); int s = socket(PF_INET, SOCK_DGRAM, 0);
ioctl(s, SIOCGIFADDR, &ifr); ioctl(s, SIOCGIFADDR, &ifr);
struct in_addr in = ((sockaddr_in *) &ifr.ifr_addr)->sin_addr; struct in_addr in = ((sockaddr_in *) &ifr.ifr_addr)->sin_addr;
close(s); //close the file descriptor
return QString(inet_ntoa(in)); return QString(inet_ntoa(in));
} }
@@ -86,7 +91,7 @@ QString NetDevice::ipv6AsString(){
//Now get the IPv6 address in string form //Now get the IPv6 address in string form
char straddr[INET6_ADDRSTRLEN]; char straddr[INET6_ADDRSTRLEN];
int err = getnameinfo(sadd, sadd->sa_len, straddr, sizeof(straddr),NULL, 0, NI_NUMERICHOST); 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); qDebug() << "getnameinfo error:" << gai_strerror(err);
return ""; return "";
}else{ }else{
@@ -102,16 +107,28 @@ QString NetDevice::netmaskAsString(){
strncpy(ifr.ifr_name, name.toLocal8Bit(), IFNAMSIZ); strncpy(ifr.ifr_name, name.toLocal8Bit(), IFNAMSIZ);
int s = socket(PF_INET, SOCK_DGRAM, 0); int s = socket(PF_INET, SOCK_DGRAM, 0);
ioctl(s, SIOCGIFNETMASK, &ifr); ioctl(s, SIOCGIFNETMASK, &ifr);
struct in_addr in = ((sockaddr_in *) &ifr.ifr_addr)->sin_addr; struct in_addr in = ((sockaddr_in *) &ifr.ifr_addr)->sin_addr;
close(s); //close the file descriptor
return QString(inet_ntoa(in)); return QString(inet_ntoa(in));
} }
//Returns the description string for the device //Returns the description string for the device
QString NetDevice::desc(){ 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 //Fetch the mac address as a QString
@@ -136,7 +153,7 @@ QString NetDevice::macAsString(){
sdl = (sockaddr_dl *)(((if_msghdr *)buf)+1); sdl = (sockaddr_dl *)(((if_msghdr *)buf)+1);
ptr = (char *) LLADDR(sdl); ptr = (char *) LLADDR(sdl);
QString mac; QString mac;
for (uint i=0; i < 6; i++){ for (uint i=0; i < 6; i++){
mac += QString::number(*(ptr+i), 16).right(2).rightJustified(2, '0'); mac += QString::number(*(ptr+i), 16).right(2).rightJustified(2, '0');
@@ -157,11 +174,6 @@ QString NetDevice::mediaStatusAsString(){
QString status; QString status;
switch (IFM_TYPE(ifm.ifm_active)){ 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: case IFM_IEEE80211:
if (ifm.ifm_status & IFM_ACTIVE) status = "associated"; if (ifm.ifm_status & IFM_ACTIVE) status = "associated";
else status = "no carrier"; else status = "no carrier";
@@ -170,6 +182,7 @@ QString NetDevice::mediaStatusAsString(){
if (ifm.ifm_status & IFM_ACTIVE) status = "active"; if (ifm.ifm_status & IFM_ACTIVE) status = "active";
else status = "no carrier"; else status = "no carrier";
} }
close(s); //close the file descriptor
return status; return status;
} }
@@ -190,8 +203,9 @@ bool NetDevice::isWireless(){
int s = socket(AF_INET, SOCK_DGRAM, 0); int s = socket(AF_INET, SOCK_DGRAM, 0);
ioctl(s, SIOCGIFMEDIA, &ifm); ioctl(s, SIOCGIFMEDIA, &ifm);
bool iswifi = (IFM_TYPE(ifm.ifm_active) == IFM_IEEE80211);
return 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) //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 //See if the device is setup to use DHCP
bool NetDevice::usesDHCP(){ bool NetDevice::usesDHCP(){
//The system does not keep track of how the device's address was assigned //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) // DHCP on startup (in /etc/rc.conf) (Ken Moore - 6/24/15)
return !Network::readRcConf().filter(name).filter("DHCP").isEmpty(); return !Network::readRcConf().filter(name).filter("DHCP").isEmpty();
} }
@@ -217,8 +231,9 @@ bool NetDevice::isUp(){
int s = socket(AF_INET, SOCK_DGRAM, 0); int s = socket(AF_INET, SOCK_DGRAM, 0);
ioctl(s, SIOCGIFFLAGS, &ifr); ioctl(s, SIOCGIFFLAGS, &ifr);
bool isup = (ifr.ifr_flags & IFF_UP);
return (ifr.ifr_flags & IFF_UP) ? 1 : 0; close(s); //close the file descriptor
return isup;
} }
//Determine the number of packets received by the device //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-zfs.h \
$${PWD}/sysadm-pkg.h \ $${PWD}/sysadm-pkg.h \
$${PWD}/sysadm-moused.h \ $${PWD}/sysadm-moused.h \
$${PWD}/sysadm-powerd.h $${PWD}/sysadm-powerd.h \
$${PWD}/sysadm-sourcectl.h
SOURCES += $${PWD}/NetDevice.cpp \ SOURCES += $${PWD}/NetDevice.cpp \
$${PWD}/sysadm-general.cpp \ $${PWD}/sysadm-general.cpp \
@@ -35,4 +37,5 @@ SOURCES += $${PWD}/NetDevice.cpp \
$${PWD}/sysadm-zfs.cpp \ $${PWD}/sysadm-zfs.cpp \
$${PWD}/sysadm-pkg.cpp \ $${PWD}/sysadm-pkg.cpp \
$${PWD}/sysadm-moused.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 BEADM::renameBE(QJsonObject jsin) {
QJsonObject retObject; QJsonObject retObject;
QStringList keys = jsin.keys(); QStringList keys = jsin.keys();
if (! keys.contains("source") || ! keys.contains("target") ) { if (! keys.contains("source") || ! keys.contains("target") ) {
retObject.insert("error", "Missing required key(s) 'source / target'"); retObject.insert("error", "Missing required key(s) 'source / target'");
@@ -71,7 +71,7 @@ QJsonObject BEADM::listBEs() {
return retObject; return retObject;
} }
} }
retObject.insert("source", source); retObject.insert("source", source);
retObject.insert("target", target); retObject.insert("target", target);
return retObject; return retObject;
@@ -191,7 +191,7 @@ QJsonObject BEADM::listBEs() {
QString mountpoint; QString mountpoint;
if (keys.contains("mountpoint") ) { if (keys.contains("mountpoint") ) {
mountpoint = jsin.value("mountpoint").toString(); mountpoint = jsin.value("mountpoint").toString();
} }
QStringList output = General::RunCommand("beadm mount "+ be + " " + mountpoint).split("\n"); QStringList output = General::RunCommand("beadm mount "+ be + " " + mountpoint).split("\n");
@@ -209,7 +209,7 @@ QJsonObject BEADM::listBEs() {
return retObject; return retObject;
} }
// Unmount the given boot environment immediately. Confirmation should be done through the client. // Unmount the given boot environment immediately. Confirmation should be done through the client.
QJsonObject BEADM::umountBE(QJsonObject jsin) { QJsonObject BEADM::umountBE(QJsonObject jsin) {

View File

@@ -10,6 +10,10 @@
#include "sysadm-global.h" #include "sysadm-global.h"
using namespace sysadm; using namespace sysadm;
#define PREFIX QString("/usr/local")
QString TRUEOS_ETCCONF(PREFIX + "/etc/trueos.conf"); // The default trueos.conf file
//================= //=================
// RunCommand() variations // RunCommand() variations
//================= //=================
@@ -36,7 +40,10 @@ QString General::RunCommand(bool &success, QString command, QStringList argument
if(arguments.isEmpty()){ proc.start(command); } if(arguments.isEmpty()){ proc.start(command); }
else{ proc.start(command, arguments); } else{ proc.start(command, arguments); }
//Wait for the process to finish (but don't block the event loop) //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 success = (proc.exitCode()==0); //return success/failure
return QString(proc.readAllStandardOutput()); return QString(proc.readAllStandardOutput());
} }
@@ -54,6 +61,21 @@ bool General::RunQuickCommand(QString command, QStringList arguments,QString wor
return success; 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 // 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 // Load the old file, find the oldKey, remove it and replace with newKey
QFile file( oFileTmp ); QFile file( oFileTmp );
if ( ! file.open( QIODevice::ReadOnly ) ) if ( file.open( QIODevice::ReadOnly ) ){
return false; QTextStream stream( &file );
QString line;
QTextStream stream( &file ); while ( !stream.atEnd() ) {
QString line;
while ( !stream.atEnd() ) {
line = stream.readLine(); // line of text excluding '\n' line = stream.readLine(); // line of text excluding '\n'
// Key is not found at all // Key is not found at all
@@ -214,10 +234,12 @@ bool General::setConfFileValue(QString fileName, QString oldKey, QString newKey,
continue; 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! // Didn't find the key? Write it!
if ( ! newKey.isEmpty() ) if ( ! newKey.isEmpty() )
SavedFile << newKey; SavedFile << newKey;
@@ -241,6 +263,30 @@ bool General::setConfFileValue(QString fileName, QString oldKey, QString newKey,
return true; 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) // 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; } if(0!=sysctlbyname(var.toLocal8Bit(), &result, &len, NULL, 0) ){ return 0; }
return result; 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() ); static QString RunCommand(QString command, QStringList arguments = QStringList(), QString workdir = "", QStringList env = QStringList() );
// - success output only // - success output only
static bool RunQuickCommand(QString command, QStringList arguments = QStringList(), QString workdir = "", QStringList env = QStringList() ); 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 //File Access Functions
static QStringList readTextFile(QString filename); static QStringList readTextFile(QString filename);
static bool writeTextFile(QString filename, QStringList contents, bool overwrite = true); static bool writeTextFile(QString filename, QStringList contents, bool overwrite = true);
@@ -55,6 +56,10 @@ public:
//Retrieve a number-based sysctl //Retrieve a number-based sysctl
static long long sysctlAsInt(QString var); 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 } //end of pcbsd namespace

View File

@@ -8,7 +8,7 @@
#include "sysadm-iocage.h" #include "sysadm-iocage.h"
#include "sysadm-global.h" #include "sysadm-global.h"
//need access to the global DISPATCHER object //need access to the global DISPATCHER object
#include "globals.h" #include "globals.h"
using namespace sysadm; using namespace sysadm;
@@ -17,11 +17,11 @@ using namespace sysadm;
QJsonObject Iocage::activateStatus(){ QJsonObject Iocage::activateStatus(){
QJsonObject retObject; QJsonObject retObject;
bool success = false; 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"); retObject.insert("activated", success ? "true" : "false");
if(success){ if(success){
//Grab the currently activated pool out of the return, and list that //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); retObject.insert("pool", pool);
} }
return retObject; return retObject;
@@ -87,59 +87,104 @@ QJsonObject Iocage::cleanAll() {
//================TEMPLATE MANAGEMENT=================== //================TEMPLATE MANAGEMENT===================
QJsonObject Iocage::listTemplates(){ QJsonObject Iocage::listTemplates(){
QJsonObject retObject; QJsonObject retObject;
QStringList local = General::RunCommand("iocage list -tlh ").split("\n"); bool ok = false;
for(int i=0; i<local.length(); i++){ QStringList local = General::RunCommand(ok, "iocage list -tlh ").split("\n");
QStringList info = local[i].split("\t"); //the -h flag is for scripting use (tabs as separators) if(ok){
//NOTE ABOUT FORMAT: QJsonObject temp;
// [JID, UUID, BOOT, STATE, TAG, TYPE, IP4, RELEASE, TEMPLATE] for(int i=0; i<local.length(); i++){
if(info.length()!=9){ continue; } //invalid line QStringList info = local[i].split("\t"); //the -h flag is for scripting use (tabs as separators)
QJsonObject obj; //NOTE ABOUT FORMAT:
obj.insert("jid",info[0]); // [JID, UUID, BOOT, STATE, TAG, TYPE, RELEASE, IP4, IP6, TEMPLATE]
obj.insert("uuid",info[1]); if(info.length()!=10){ continue; } //invalid line
obj.insert("boot",info[2]); QJsonObject obj;
obj.insert("state",info[3]); obj.insert("jid",info[0]);
obj.insert("tag",info[4]); obj.insert("uuid",info[1]);
obj.insert("type",info[5]); obj.insert("boot",info[2]);
obj.insert("ip4",info[6]); obj.insert("state",info[3]);
obj.insert("release",info[7]); obj.insert("tag",info[4]);
obj.insert("template",info[8]); obj.insert("type",info[5]);
retObject.insert(info[8], obj); 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; return retObject;
} }
QJsonObject Iocage::listReleases(){ QJsonObject Iocage::listReleases(){
QJsonObject retObject; QJsonObject retObject;
// Locally-available releases // Locally-available releases
QStringList local = General::RunCommand("iocage list -rh").split("\n"); bool ok = false;
retObject.insert("local", QJsonArray::fromStringList(local) ); QStringList local = General::RunCommand(ok, "iocage list -rh").split("\n");
if(ok){ retObject.insert("local", QJsonArray::fromStringList(local) ); }
//Remote releases available for download //Remote releases available for download
QStringList remote = General::RunCommand("iocage list -Rh").split("\n"); QStringList remote = General::RunCommand(ok, "iocage list -rRh").split("\n");
for(int i=0; i<remote.length(); i++){ if(ok){
if(remote[i].startsWith("[")){ remote[i] = remote[i].section("]",1,-1); } for(int i=0; i<remote.length(); i++){
else{ remote.removeAt(i); 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; return retObject;
} }
QJsonObject Iocage::listPlugins(){ QJsonObject Iocage::listPlugins(){
QJsonObject retObject; 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 //Remote plugins available for download/use
QStringList remote = General::RunCommand("iocage list -Ph").split("\n"); bool ok = false;
retObject.insert("remote", QJsonArray::fromStringList(remote)); 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; return retObject;
} }
QJsonObject Iocage::fetchReleases(QJsonObject inobj){ QJsonObject Iocage::fetchReleases(QJsonObject inobj){
QJsonObject retObject; QJsonObject retObject;
if(!inobj.contains("releases")){ return retObject; } //nothing to do 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()); } if(inobj.value("releases").isArray()){ releases = General::JsonArrayToStringList(inobj.value("releases").toArray()); }
else if(inobj.value("releases").isString()){ releases << inobj.value("releases").toString(); } else if(inobj.value("releases").isString()){ releases << inobj.value("releases").toString(); }
//Now start up each of these downloads as appropriate //Now start up each of these downloads as appropriate
@@ -147,7 +192,7 @@ QJsonObject Iocage::fetchReleases(QJsonObject inobj){
QString jobprefix = "sysadm_iocage_fetch_release_"; QString jobprefix = "sysadm_iocage_fetch_release_";
QJsonArray started; QJsonArray started;
for(int i=0; i<releases.length(); i++){ 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 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]); DISPATCHER->queueProcess(jobprefix+releases[i], "iocage fetch --verify -r "+releases[i]);
started << jobprefix+releases[i]; started << jobprefix+releases[i];
@@ -156,25 +201,28 @@ QJsonObject Iocage::fetchReleases(QJsonObject inobj){
return retObject; return retObject;
} }
QJsonObject Iocage::fetchPlugins(QJsonObject inobj){ QJsonObject Iocage::fetchPlugin(QJsonObject inobj){
QJsonObject retObject; QJsonObject retObject;
if(!inobj.contains("plugins")){ return retObject; } //nothing to do if(!inobj.contains("plugin") || !inobj.contains("net_device") || ! (inobj.contains("ip4") || inobj.contains("ip6")) ){ return retObject; } //nothing to do
QStringList plugins; QString plugin = inobj.value("plugin").toString();
if(inobj.value("plugins").isArray()){ plugins = General::JsonArrayToStringList(inobj.value("plugins").toArray()); } QString dev = inobj.value("net_device").toString();
else if(inobj.value("plugins").isString()){ plugins << inobj.value("plugins").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 //Now start up each of these downloads as appropriate
QStringList cids = DISPATCHER->listJobs().value("no_queue").toObject().keys(); //all currently running/pending jobs QStringList cids = DISPATCHER->listJobs().value("no_queue").toObject().keys(); //all currently running/pending jobs
QString jobprefix = "sysadm_iocage_fetch_plugin_"; QString jobprefix = "sysadm_iocage_fetch_plugin_";
QJsonArray started; plugin = plugin.section(" ",0,0, QString::SectionSkipEmpty); //all valid releases are a single word - do not allow injection of other commands
for(int i=0; i<plugins.length(); i++){ if(cids.contains(jobprefix+plugin) ){ return QJsonObject(); } //this fetch job is already running
plugins[i] = plugins[i].section(" ",0,0, QString::SectionSkipEmpty); //all valid releases are a single word - do not allow injection of other commands DISPATCHER->queueProcess(jobprefix+plugin, "iocage fetch -P --name "+plugin+" "+inet);
if(cids.contains(jobprefix+plugins[i]) ){ continue; } //this fetch job is already running - skip it for now retObject.insert("started_dispatcher_id", jobprefix+plugin);
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); }
return retObject; return retObject;
} }
// Clean all templates on a box // Clean all templates on a box
QJsonObject Iocage::cleanTemplates() { QJsonObject Iocage::cleanTemplates() {
QJsonObject retObject; QJsonObject retObject;
@@ -205,23 +253,31 @@ QJsonObject Iocage::cleanReleases() {
// List the jails on the box // List the jails on the box
QJsonObject Iocage::listJails() { QJsonObject Iocage::listJails() {
QJsonObject retObject; QJsonObject retObject;
QStringList output = General::RunCommand("iocage list -lh").split("\n"); bool ok = false;
for(int i=0; i<output.length(); i++){ QStringList local = General::RunCommand(ok, "iocage list -lh").split("\n");
QStringList info = output[i].split("\t"); if(ok){
//FORMAT NOTE: (long output: "-l" flag) QJsonObject temp;
// [JID, UUID, BOOT, STATE, TAG, TYPE, IP4, RELEASE, TEMPLATE] for(int i=0; i<local.length(); i++){
if(info.length()!=9){ continue; } //invalid line QStringList info = local[i].split("\t"); //the -h flag is for scripting use (tabs as separators)
QJsonObject obj; //NOTE ABOUT FORMAT:
obj.insert("jid",info[0]); // [JID, UUID, BOOT, STATE, TAG, TYPE, RELEASE, IP4, IP6, TEMPLATE]
obj.insert("uuid",info[1]); if(info.length()!=10){ continue; } //invalid line
obj.insert("boot",info[2]); QJsonObject obj;
obj.insert("state",info[3]); obj.insert("jid",info[0]);
obj.insert("tag",info[4]); obj.insert("uuid",info[1]);
obj.insert("type",info[5]); obj.insert("boot",info[2]);
obj.insert("ip4",info[6]); obj.insert("state",info[3]);
obj.insert("release",info[7]); obj.insert("tag",info[4]);
obj.insert("template",info[8]); obj.insert("type",info[5]);
retObject.insert(info[1], obj); //use uuid as main id tag 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; return retObject;
} }

View File

@@ -25,7 +25,7 @@ public:
static QJsonObject listReleases(); static QJsonObject listReleases();
static QJsonObject listPlugins(); static QJsonObject listPlugins();
static QJsonObject fetchReleases(QJsonObject); static QJsonObject fetchReleases(QJsonObject);
static QJsonObject fetchPlugins(QJsonObject); static QJsonObject fetchPlugin(QJsonObject);
static QJsonObject cleanTemplates(); static QJsonObject cleanTemplates();
static QJsonObject cleanReleases(); static QJsonObject cleanReleases();
@@ -46,7 +46,7 @@ public:
static QJsonObject capJail(QJsonObject); static QJsonObject capJail(QJsonObject);
static QJsonObject getJailSettings(QJsonObject); static QJsonObject getJailSettings(QJsonObject);
}; };
} //end of namespace } //end of namespace

View File

@@ -7,12 +7,23 @@
#include "sysadm-general.h" #include "sysadm-general.h"
#include "sysadm-moused.h" #include "sysadm-moused.h"
#include "sysadm-global.h" #include "sysadm-global.h"
#include "sysadm-systemmanager.h"
#include "globals.h" #include "globals.h"
#define _MOUSED_CONF QString("/etc/conf.d/moused") #define _MOUSED_CONF QString("/etc/conf.d/moused")
#define _MOUSED_SYS_CONF QString("/etc/rc.conf") #define _MOUSED_SYS_CONF QString("/etc/rc.conf")
#define _MOUSED_DEFAULT_CONF QString("/etc/defaults/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; using namespace sysadm;
QJsonObject moused::listDevices(){ QJsonObject moused::listDevices(){
@@ -46,6 +57,7 @@ QJsonObject moused::listOptions(){
QJsonObject out; QJsonObject out;
out.insert("emulate_button_3", QJsonArray() << "true" << "false"); out.insert("emulate_button_3", QJsonArray() << "true" << "false");
out.insert("hand_mode", QJsonArray() << "left" << "right"); out.insert("hand_mode", QJsonArray() << "left" << "right");
out.insert("mouse_scroll_invert", QJsonArray() << "true" << "false");
out.insert("virtual_scrolling", QJsonArray() << "true" << "false"); out.insert("virtual_scrolling", QJsonArray() << "true" << "false");
out.insert("accel_exponential", "float min=1.0 max=2.0"); out.insert("accel_exponential", "float min=1.0 max=2.0");
out.insert("accel_linear", "float min=0.01 max=100.00"); 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"); out.insert("terminate_drift_threshold_pixels", "int min=0 max=1000");
return out; return out;
} }
QJsonObject moused::readOptions(QJsonObject obj){ QJsonObject moused::readOptions(QJsonObject obj){
QString device = obj.value("device").toString(); QString device = obj.value("device").toString();
//qDebug() << "Read Options for Device:" << device; //qDebug() << "Read Options for Device:" << device;
if(device.isEmpty()){ return QJsonObject(); } //invalid inputs if(device.isEmpty()){ return QJsonObject(); } //invalid inputs
QString val = General::getConfFileValue(_MOUSED_CONF, "moused_args_"+device+"=" ); 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_SYS_CONF, "moused_flags=" ); }
if(val.isEmpty()){ General::getConfFileValue(_MOUSED_DEFAULT_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"); out.insert("emulate_button_3", args.contains("-3") ? "true" : "false");
int index = args.indexOf("-m"); int index = args.indexOf("-m");
bool righthand = true; bool righthand = true;
bool scrollinvert = false;
while(index>=0 && args.length() > (index+1) ){ while(index>=0 && args.length() > (index+1) ){
if(args[index+1].startsWith("1=")){ righthand = (args[index+1] == "1=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); index = args.indexOf("-m", index+1);
} }
out.insert("mouse_scroll_invert", scrollinvert ? "true" : "false" );
out.insert("hand_mode", righthand ? "right" : "left"); out.insert("hand_mode", righthand ? "right" : "left");
out.insert("virtual_scrolling", args.contains("-V") ? "true" : "false" ); 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(); QString val = Cobj.value(keys[i]).toString();
if(keys[i]=="emulate_button_3" && val=="true"){ args << "-3"; } 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]=="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]=="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_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 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"); QDir dir("/var/run");
QJsonObject out; QJsonObject out;
QStringList devsactive = dir.entryList(QStringList() << "moused-*.pid", QDir::Files, QDir::Name); QStringList devsactive = dir.entryList(QStringList() << "moused-*.pid", QDir::Files, QDir::Name);
for(int i=0; i<devsactive.length(); i++){ for(int i=0; i<devsactive.length(); i++){
devsactive[i] = devsactive[i].section("-",1,-1).section(".pid",0,0); devsactive[i] = devsactive[i].section("-",1,-1).section(".pid",0,0);
} }
out.insert("active_devices", QJsonArray::fromStringList(devsactive)); out.insert("active_devices", QJsonArray::fromStringList(devsactive));
return out; return out;
@@ -167,3 +183,96 @@ QJsonObject moused::disableDevice(QJsonObject obj){
out.insert("stopped", device); out.insert("stopped", device);
return out; 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 enableDevice(QJsonObject);
static QJsonObject disableDevice(QJsonObject); static QJsonObject disableDevice(QJsonObject);
//General system input options
static QJsonObject tapToClick();
static QJsonObject setTapToClick(QJsonObject);
//Synaptics options
static QJsonObject synapticsSettings();
}; };
} //end of namespace } //end of namespace
#endif #endif

View File

@@ -19,7 +19,7 @@ using namespace sysadm;
//Copy over this data into the output structure //Copy over this data into the output structure
NetworkEntry tmp; NetworkEntry tmp;
tmp.name = QString::fromLocal8Bit(entry->n_name); 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.aliases << QString::fromLocal8Bit(entry->n_aliases[i]);
} }
tmp.netnum = entry->n_net; tmp.netnum = entry->n_net;
@@ -57,8 +57,9 @@ NetDevSettings Network::deviceRCSettings(QString dev){
if(val.startsWith("\"")){ val = val.remove(1); } if(val.startsWith("\"")){ val = val.remove(1); }
if(val.endsWith("\"")){ val.chop(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 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) ){ if( val.simplified()==dev && (var.startsWith("vlans_") || var.startsWith("wlans_") )){
set.asDevice = val; set.asDevice = var.section("_",1,-1);
if(var.startsWith("wlans_")){ set.wifihost = true; }
}else if(var==("ifconfig_"+dev)){ }else if(var==("ifconfig_"+dev)){
QStringList vals = val.split(" ",QString::SkipEmptyParts); QStringList vals = val.split(" ",QString::SkipEmptyParts);
//This is the main settings line: lots of things to look for: //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 //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); QStringList vals = val.split(" ",QString::SkipEmptyParts);
for(int v=0; v<vals.length(); v++){ for(int v=0; v<vals.length(); v++){
} }
}*/ }*/
}else{ set.useDHCP=true; } //end of DHCP check }else{ set.useDHCP=true; } //end of DHCP check
//Wifi Checks //Wifi Checks
if(vals.contains("WPA")){ set.wifisecurity=true; } if(vals.contains("WPA")){ set.wifisecurity=true; }
} //end variable checks } //end variable checks
} //end loop over rc.conf lines } //end loop over rc.conf lines
return set; return set;
@@ -119,11 +120,53 @@ NetDevSettings Network::deviceRCSettings(QString dev){
}*/ }*/
//-------------------------------------- //--------------------------------------
bool NetworkRoot::saveRCSettings(NetDevSettings){ bool NetworkRoot::saveRCSettings(NetDevSettings set){
return false; 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){ bool NetworkRoot::setIfconfigSettings(NetDevSettings){
return false; return false;
} }

View File

@@ -38,7 +38,7 @@ struct NetDevSettings{
//General data class for network devices //General data class for network devices
// Note: Sources in NetDevice.cpp // Note: Sources in NetDevice.cpp
class NetDevice{ class NetDevice{
private: private:
QString name; QString name;
public: public:
NetDevice(QString devName){ name = devName; } NetDevice(QString devName){ name = devName; }
@@ -61,7 +61,7 @@ public:
long packetsTx(); long packetsTx();
long errorsRx(); long errorsRx();
long errorsTx(); long errorsTx();
//Setting Functions (to make changes - requires root) //Setting Functions (to make changes - requires root)
void setUp(bool up); //Turn device on/off (temporary - not saved globally) void setUp(bool up); //Turn device on/off (temporary - not saved globally)
@@ -75,7 +75,6 @@ struct NetWifi{
QString BSSID, SSID; QString BSSID, SSID;
}; };
//The general-purpose class that any user/app can utilitize //The general-purpose class that any user/app can utilitize
class Network{ class Network{
public: public:
@@ -88,11 +87,11 @@ public:
//The class that requires overarching root permissions (usually for changes to system) //The class that requires overarching root permissions (usually for changes to system)
class NetworkRoot{ class NetworkRoot{
public: 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 saveRCSettings(NetDevSettings); //rc.conf settings (bootup)
static bool setIfconfigSettings(NetDevSettings); //ifconfig settings (temporary session) static bool setIfconfigSettings(NetDevSettings); //ifconfig settings (temporary session)
}; };
} //end of pcbsd namespace } //end of pcbsd namespace
#endif #endif

View File

@@ -14,6 +14,20 @@ using namespace sysadm;
// ================== // ==================
// INLINE FUNCTIONS // 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 //Get annotation variable/values
inline void annotations_from_ids(QStringList var_ids, QStringList val_ids, QJsonObject *out, QSqlDatabase DB){ 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) //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(); tot.removeDuplicates();
int index = -1; int index = -1;
QSqlQuery q("SELECT annotation, annotation_id FROM annotation WHERE annotation_id IN ('"+tot.join("', '")+"')",DB); 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(); //qDebug() << "Got query result:" << q.value("annotation_id").toString() << q.value("annotation").toString();
index = var_ids.indexOf(q.value("annotation_id").toString()); index = var_ids.indexOf(q.value("annotation_id").toString());
while(index>=0){ while(index>=0){
var_ids.replace(index, q.value("annotation").toString()); var_ids.replace(index, q.value("annotation").toString());
index = var_ids.indexOf(q.value("annotation_id").toString()); index = var_ids.indexOf(q.value("annotation_id").toString());
} }
index = val_ids.indexOf(q.value("annotation_id").toString()); index = val_ids.indexOf(q.value("annotation_id").toString());
while(index>=0){ while(index>=0){
val_ids.replace(index, q.value("annotation").toString()); val_ids.replace(index, q.value("annotation").toString());
index = val_ids.indexOf(q.value("annotation_id").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(); } while(q.next()){ out << q.value("origin").toString(); }
return out; 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){ inline QStringList names_from_ids(QStringList ids, QString db, QSqlDatabase DB){
QSqlQuery q("SELECT name FROM "+db+" WHERE id IN ('"+ids.join("', '")+"')",DB); QSqlQuery q("SELECT name FROM "+db+" WHERE id IN ('"+ids.join("', '")+"')",DB);
QStringList out; QStringList out;
@@ -68,15 +82,35 @@ inline QStringList requires_from_ids(QStringList ids, QSqlDatabase DB){
while(q.next()){ out << q.value("require").toString(); } while(q.next()){ out << q.value("require").toString(); }
return out; 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){ inline QString getRepoFile(QString repo){
if(repo=="local"){ return "/var/db/pkg/local.sqlite"; } 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){ 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) // (preventing conflict between concurrent calls)
QSqlDatabase DB = QSqlDatabase::addDatabase("QSQLITE", repo+QUuid::createUuid().toString()); QSqlDatabase DB = QSqlDatabase::addDatabase("QSQLITE", repo+QUuid::createUuid().toString());
DB.setConnectOptions("QSQLITE_OPEN_READONLY=1"); DB.setConnectOptions("QSQLITE_OPEN_READONLY=1");
DB.setHostName("localhost"); DB.setHostName("localhost");
QString path = getRepoFile(repo); QString path = getRepoFile(repo);
DB.setDatabaseName(path); //path to the database file 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; QJsonObject retObj;
//if(origins.contains("math/R")){ qDebug() << "pkg_info:" << repo << category; } //if(origins.contains("math/R")){ qDebug() << "pkg_info:" << repo << category; }
QString dbconn = openDB(repo); QString dbconn = openDB(repo);
if(!dbconn.isEmpty()){ if(!dbconn.isEmpty()){
QSqlDatabase DB = QSqlDatabase::database(dbconn); QSqlDatabase DB = QSqlDatabase::database(dbconn);
if(!DB.isOpen()){ return retObj; } //could not open DB (file missing?) if(!DB.isOpen()){ return retObj; } //could not open DB (file missing?)
//Now do all the pkg info, one pkg origin at a time //Now do all the pkg info, one pkg origin at a time
origins.removeAll(""); origins.removeAll("");
origins.removeDuplicates(); origins.removeDuplicates();
QString q_string = "SELECT * FROM packages"; QString q_string = "SELECT * FROM packages";
if(!origins.isEmpty()){ if(!origins.isEmpty()){
q_string.append(" WHERE origin IN ('"+origins.join("', '")+"')"); q_string.append(" WHERE name IN ('"+origins.join("', '")+"')");
//Also keep the ordering of the origins preserved //Also keep the ordering of the origins preserved
/*q_string.append(" ORDER BY CASE origins "); /*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)+"' "); } 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); QSqlQuery query(q_string, DB);
while(query.next()){ while(query.next()){
QString id = query.value("id").toString(); //need this pkg id for later 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(origins.contains("math/R")){ qDebug() << "Found origin:" << origin << id; }
if(id.isEmpty() || origin.isEmpty()){ continue; } if(id.isEmpty() || name.isEmpty()){ continue; }
QJsonObject info; QJsonObject info;
//General info //General info
for(int i=0; i<query.record().count(); i++){ 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 //ANNOTATIONS
QSqlQuery q2("SELECT tag_id, value_id FROM pkg_annotation WHERE package_id = '"+id+"'", DB); 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(); tags << q2.value("tag_id").toString(); vals << q2.value("value_id").toString();
} }
if(!tags.isEmpty()){ annotations_from_ids(tags, vals, &info, DB); } 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 //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); 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; QJsonObject options;
@@ -144,22 +178,29 @@ QJsonObject PKG::pkg_info(QStringList origins, QString repo, QString category, b
} }
if(!options.isEmpty()){ info.insert("options",options); } if(!options.isEmpty()){ info.insert("options",options); }
//DEPENDENCIES //DEPENDENCIES
QSqlQuery q4("SELECT origin FROM deps WHERE package_id = '"+id+"'", DB); QSqlQuery q4("SELECT origin, name FROM deps WHERE package_id = '"+id+"'", DB);
QStringList tmpList; QStringList tmpList, tmpListN;
while(q4.next()){ while(q4.next()){
tmpList << q4.value("origin").toString(); tmpList << q4.value("origin").toString();
tmpListN << q4.value("name").toString();
} //end deps query } //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 //FILES
QSqlQuery q5("SELECT path FROM files WHERE package_id = '"+id+"'", DB); QSqlQuery q5("SELECT path FROM files WHERE package_id = '"+id+"'", DB);
tmpList.clear(); tmpList.clear();
while(q5.next()){ tmpList << q5.value("path").toString(); } while(q5.next()){ tmpList << q5.value("path").toString(); }
if(!tmpList.isEmpty()){ info.insert("files", QJsonArray::fromStringList(tmpList) ); } if(!tmpList.isEmpty()){ info.insert("files", QJsonArray::fromStringList(tmpList) ); }
//REVERSE DEPENDENCIES //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(); tmpList.clear();
while(q6.next()){ tmpList << q6.value("package_id").toString(); } 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 //USERS
QSqlQuery q7("SELECT user_id FROM pkg_users WHERE package_id = '"+id+"'", DB); QSqlQuery q7("SELECT user_id FROM pkg_users WHERE package_id = '"+id+"'", DB);
tmpList.clear(); 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); QSqlQuery q15("SELECT require_id FROM pkg_requires WHERE package_id = '"+id+"'", DB);
tmpList.clear(); tmpList.clear();
while(q15.next()){ tmpList << q15.value("require_id").toString(); } 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 //Now insert this information into the main object
retObj.insert(origin,info); retObj.insert(name,info);
} //end loop over pkg matches } //end loop over pkg matches
DB.close(); DB.close();
}//end if dbconn exists (force DB out of scope now) }//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){ QStringList PKG::pkg_search(QString repo, QString searchterm, QStringList searchexcludes, QString category){
QString dbconn = openDB(repo); QString dbconn = openDB(repo);
QStringList found; QStringList found;
if(!dbconn.isEmpty()){ if(!dbconn.isEmpty()){
QSqlDatabase DB = QSqlDatabase::database(dbconn); QSqlDatabase DB = QSqlDatabase::database(dbconn);
if(!DB.isOpen()){ return QStringList(); } //could not open DB (file missing?) if(!DB.isOpen()){ return QStringList(); } //could not open DB (file missing?)
QStringList terms = searchterm.split(" ",QString::SkipEmptyParts); QStringList terms = searchterm.split(" ",QString::SkipEmptyParts);
searchexcludes.removeAll(""); searchexcludes.removeAll("");
QString q_string; QString q_string;
int numtry = 0; int numtry = 0;
while(found.isEmpty() && numtry<2){ while(found.isEmpty() && numtry<2){
if(numtry<1 && !searchterm.contains(" ")){ //single-word-search (exact names never have multiple words) 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(!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 '%")+"%'"); } if(!searchexcludes.isEmpty()){ q_string.append(" AND name NOT LIKE '%"+searchexcludes.join("%' AND name NOT LIKE '%")+"%'"); }
q_string.append(" COLLATE NOCASE"); // Case insensitive q_string.append(" COLLATE NOCASE"); // Case insensitive
QSqlQuery query(q_string, DB); QSqlQuery query(q_string, DB);
while(query.next()){ 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){ if(found.length()<60 && numtry<1){
//Expand the search to names containing the term //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(!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 '%")+"%'"); } if(!searchexcludes.isEmpty()){ q_string.append(" AND name NOT LIKE '%"+searchexcludes.join("%' AND name NOT LIKE '%")+"%'"); }
QSqlQuery q2(q_string, DB); QSqlQuery q2(q_string, DB);
while(q2.next()){ 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){ if(found.length()<60 && numtry<1){
//Expand the search to names containing the term //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(!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 '%")+"%'"); } if(!searchexcludes.isEmpty()){ q_string.append(" AND name NOT LIKE '%"+searchexcludes.join("%' AND name NOT LIKE '%")+"%'"); }
QSqlQuery q2(q_string, DB); QSqlQuery q2(q_string, DB);
while(q2.next()){ 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){ if(found.length()<60){
//Expand the search to comments //Expand the search to comments
if(terms.length()<2){ q_string = "SELECT origin FROM packages WHERE comment LIKE '%"+searchterm+"%'"; } if(terms.length()<2){ q_string = "SELECT nameFROM 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==0){ q_string = "SELECT name 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 '%")+"%'"; } 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(!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 '%")+"%'"); } if(!searchexcludes.isEmpty()){ q_string.append(" AND comment NOT LIKE '%"+searchexcludes.join("%' AND comment NOT LIKE '%")+"%'"); }
QSqlQuery q2(q_string, DB); QSqlQuery q2(q_string, DB);
while(q2.next()){ 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){ if(found.length()<100){
//Expand the search to full descriptions //Expand the search to full descriptions
if(terms.length()<2){ q_string = "SELECT origin FROM packages WHERE desc LIKE '%"+searchterm+"%'"; } if(terms.length()<2){ q_string = "SELECT name 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==0){ q_string = "SELECT name 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 '%")+"%'"; } 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(!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 '%")+"%'"); } if(!searchexcludes.isEmpty()){ q_string.append(" AND desc NOT LIKE '%"+searchexcludes.join("%' AND desc NOT LIKE '%")+"%'"); }
QSqlQuery q2(q_string, DB); QSqlQuery q2(q_string, DB);
while(q2.next()){ 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 //Now bump the try count
@@ -297,7 +338,7 @@ while(found.isEmpty() && numtry<2){
QJsonArray PKG::list_categories(QString repo){ QJsonArray PKG::list_categories(QString repo){
QString dbconn = openDB(repo); QString dbconn = openDB(repo);
QStringList found; QStringList found;
if(!dbconn.isEmpty()){ if(!dbconn.isEmpty()){
QSqlDatabase DB = QSqlDatabase::database(dbconn); QSqlDatabase DB = QSqlDatabase::database(dbconn);
if(!DB.isOpen()){ return QJsonArray(); } //could not open DB (file missing?) if(!DB.isOpen()){ return QJsonArray(); } //could not open DB (file missing?)
@@ -313,7 +354,7 @@ QJsonArray PKG::list_categories(QString repo){
while(query.next()){ while(query.next()){
found << query.value("name").toString(); //need the origin for later found << query.value("name").toString(); //need the origin for later
} }
//Now check all the categories to ensure that pkgs exist within it //Now check all the categories to ensure that pkgs exist within it
for(int i=0; i<found.length(); i++){ for(int i=0; i<found.length(); i++){
if(origins.filter(found[i]+"/").isEmpty()){ found.removeAt(i); 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){ QJsonArray PKG::list_repos(bool updated){
QString dbdir = "/var/db/pkg/repo-%1.sqlite"; QString dbdir = "/var/db/pkg/repo-%1.sqlite";
QDir confdir("/usr/local/etc/pkg/repos"); QStringList repodirs; repodirs << "/etc/pkg" << "/etc/pkg/repos" << "/usr/local/etc/pkg" << "/usr/local/etc/pkg/repos";
QStringList confs = confdir.entryList(QStringList() << "*.conf", QDir::Files);
QStringList found; QStringList found;
found << "local"; //There is always a local database (for installed pkgs) found << "local"; //There is always a local database (for installed pkgs)
for(int i=0; i<confs.length(); i++){ for(int d=0; d<repodirs.length(); d++){
QStringList repoinfo = General::readTextFile(confdir.absoluteFilePath(confs[i])).join("\n").split("}"); if(!QFile::exists(repodirs[d])){ continue; }
for(int j=0; j<repoinfo.length(); j++){ QDir confdir(repodirs[d]);
QString repo = repoinfo[j].section(":",0,0).simplified(); QStringList confs = confdir.entryList(QStringList() << "*.conf", QDir::Files);
if(QFile::exists(dbdir.arg(repo)) && repoinfo[j].section("enabled:",1,-1).section(":",0,0).contains("true")){ found << repo; } 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){ if(found.length()<2 && !updated){
//Only the local repo could be found - update the package repos and try again //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"); 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); 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) //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("status", "pending");
obj.insert("proc_cmd",cmd); obj.insert("proc_cmd",cmd);
obj.insert("proc_id",ID); obj.insert("proc_id",ID);
return obj; return obj;
} }
QJsonObject PKG::pkg_lock(QStringList origins){ QJsonObject PKG::pkg_lock(QStringList origins){
@@ -397,7 +526,7 @@ QJsonObject PKG::pkg_lock(QStringList origins){
obj.insert("status", "pending"); obj.insert("status", "pending");
obj.insert("proc_cmd",cmd); obj.insert("proc_cmd",cmd);
obj.insert("proc_id",ID); obj.insert("proc_id",ID);
return obj; return obj;
} }
QJsonObject PKG::pkg_unlock(QStringList origins){ QJsonObject PKG::pkg_unlock(QStringList origins){
@@ -412,7 +541,7 @@ QJsonObject PKG::pkg_unlock(QStringList origins){
obj.insert("status", "pending"); obj.insert("status", "pending");
obj.insert("proc_cmd",cmd); obj.insert("proc_cmd",cmd);
obj.insert("proc_id",ID); 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("status", "pending");
obj.insert("proc_cmd",cmd); obj.insert("proc_cmd",cmd);
obj.insert("proc_id",ID); obj.insert("proc_id",ID);
return obj; return obj;
} }
QJsonObject PKG::pkg_check_upgrade(){ QJsonObject PKG::pkg_check_upgrade(){
@@ -444,7 +573,7 @@ QJsonObject PKG::pkg_check_upgrade(){
obj.insert("status", "pending"); obj.insert("status", "pending");
obj.insert("proc_cmd",cmd); obj.insert("proc_cmd",cmd);
obj.insert("proc_id",ID); obj.insert("proc_id",ID);
return obj; return obj;
} }
QJsonObject PKG::pkg_upgrade(){ QJsonObject PKG::pkg_upgrade(){
@@ -472,7 +601,7 @@ QJsonObject PKG::pkg_audit(){
obj.insert("status", "pending"); obj.insert("status", "pending");
obj.insert("proc_cmd",cmd); obj.insert("proc_cmd",cmd);
obj.insert("proc_id",ID); obj.insert("proc_id",ID);
return obj; return obj;
} }
QJsonObject PKG::pkg_autoremove(){ QJsonObject PKG::pkg_autoremove(){
@@ -486,5 +615,5 @@ QJsonObject PKG::pkg_autoremove(){
obj.insert("status", "pending"); obj.insert("status", "pending");
obj.insert("proc_cmd",cmd); obj.insert("proc_cmd",cmd);
obj.insert("proc_id",ID); 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 QStringList pkg_search(QString repo, QString searchterm, QStringList searchexcludes, QString category = "");
static QJsonArray list_categories(QString repo); static QJsonArray list_categories(QString repo);
static QJsonArray list_repos(bool updated = false); 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) //pkg modification routines (dispatcher events for notifications)
static QJsonObject pkg_install(QStringList origins, QString repo); 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_upgrade(); //upgrade all pkgs (use sysadm/updates if possible instead)
static QJsonObject pkg_audit(); //List details of vulnerable packages static QJsonObject pkg_audit(); //List details of vulnerable packages
static QJsonObject pkg_autoremove(); //Autoremove orphaned packages static QJsonObject pkg_autoremove(); //Autoremove orphaned packages
}; };
} //end of sysadm namespace } //end of sysadm namespace
#endif #endif

View File

@@ -9,7 +9,8 @@ ServiceManager::ServiceManager(QString chroot, QString ip)
{ {
this->chroot = chroot; this->chroot = chroot;
this->ip = ip; this->ip = ip;
loadRCdata(); usingOpenRC = QFile::exists(chroot+"/sbin/rc-update");
loadRCdata();
//loadServices(); //loadServices();
} }
@@ -75,14 +76,14 @@ bool ServiceManager::Start(Service service)
// Start the process // Start the process
QString prog; QString prog;
QStringList args; QStringList args;
bool once = !isEnabled(service); bool once = !isEnabled(service) && !usingOpenRC;
if ( chroot.isEmpty() ) { if ( chroot.isEmpty() ) {
prog = "service"; prog = "service";
args << service.Directory; args << service.Directory;
args << "start"; args << (once ? "onestart" : "start") ;
} else { } else {
prog = "warden"; 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); return General::RunQuickCommand(prog,args);
} }
@@ -94,14 +95,14 @@ bool ServiceManager::Stop(Service service)
// Start the process // Start the process
QString prog; QString prog;
QStringList args; QStringList args;
bool once = !isEnabled(service); bool once = !isEnabled(service) && !usingOpenRC;
if ( chroot.isEmpty() ) { if ( chroot.isEmpty() ) {
prog = "service"; prog = "service";
args << service.Directory; args << service.Directory;
args << "stop"; args << (once ? "onestop" : "stop") ;
} else { } else {
prog = "warden"; 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); return General::RunQuickCommand(prog,args);
} }
@@ -111,7 +112,7 @@ bool ServiceManager::Restart(Service service)
if(service.Directory.isEmpty()){ return false; } if(service.Directory.isEmpty()){ return false; }
QString prog; QString prog;
QStringList args; QStringList args;
bool once = !isEnabled(service); bool once = !isEnabled(service) && !usingOpenRC;
if ( chroot.isEmpty() ) { if ( chroot.isEmpty() ) {
prog = "service"; prog = "service";
args << service.Directory; args << service.Directory;
@@ -152,7 +153,13 @@ Service ServiceManager::loadServices(QString name)
// OpenRC directories are /etc/init.d and /usr/local/etc/init.d // OpenRC directories are /etc/init.d and /usr/local/etc/init.d
QStringList stringDirs; 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) for ( QString dir: stringDirs)
{ {
@@ -186,7 +193,7 @@ Service ServiceManager::loadServices(QString name)
if (line.simplified().startsWith("description=") || if (line.simplified().startsWith("description=") ||
line.simplified().startsWith("desc=") || line.simplified().startsWith("desc=") ||
line.simplified().startsWith("name=")) { (line.simplified().startsWith("name=") && service.Description.isEmpty()) ) {
service.Description = line.section("=\"",1,-1).section("\"",0,0); 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 // get the services enabled for all runlevels so we can update services
void ServiceManager::loadRunlevels() { void ServiceManager::loadRunlevels() {
if(!usingOpenRC){ return; }
QStringList info = sysadm::General::RunCommand("rc-status --nocolor --all").split("\n"); QStringList info = sysadm::General::RunCommand("rc-status --nocolor --all").split("\n");
QString runlevel = "default"; QString runlevel = "default";
for (int i=0; i<info.length(); i++) { for (int i=0; i<info.length(); i++) {
@@ -264,19 +272,17 @@ void ServiceManager::loadRunlevels() {
} }
bool ServiceManager::enableDisableService(QString name, bool enable) { 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 // if not, use default
QString prog = "rc-update"; QString prog = usingOpenRC ? "rc-update" : "sysrc";
QStringList args; QStringList args;
if (enable) if(usingOpenRC){
args << "add"; if (enable){ args << "add"; }
else else{ args << "delete"; }
args << "delete"; args << name;
}else{
args << name; args << name+"_enable=\""+ (enable ? "YES" : "NO") + "\"";
args << runlevel; }
qDebug() << prog << " " << args; //qDebug() << prog << " " << args;
bool ret = sysadm::General::RunQuickCommand(prog,args); bool ret = sysadm::General::RunQuickCommand(prog,args);
loadUnusedData(); loadUnusedData();
return ret; return ret;

View File

@@ -92,9 +92,10 @@ private:
void loadRunlevels(); void loadRunlevels();
bool enableDisableService(QString name, bool enable=false); bool enableDisableService(QString name, bool enable=false);
QString chroot; QString chroot;
QString ip; QString ip;
bool usingOpenRC;
}; };
} }
#endif // SERVICEMANAGER_H #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-general.h"
#include "sysadm-systemmanager.h" #include "sysadm-systemmanager.h"
#include "sysadm-global.h" #include "sysadm-global.h"
//need access to the global DISPATCHER object
#include "globals.h"
using namespace sysadm; using namespace sysadm;
//PLEASE: Keep the functions in the same order as listed in pcbsd-general.h
//Battery Availability //Battery Availability
QJsonObject SysMgmt::batteryInfo(){ QJsonObject SysMgmt::batteryInfo(){
QJsonObject retObject; QJsonObject retObject;
bool ok; bool ok;
QString tmp; //temporary string for use later
int val = General::RunCommand("apm -l").toInt(&ok); //Battery Charging State
if ( ok && (val >= 0 && val <= 100) ) { QStringList vals = General::RunCommand("apm -alt").split("\n");
retObject.insert("battery", "true"); int state =-1;
} else { 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"); retObject.insert("battery", "false");
return retObject; return retObject;
} }
retObject.insert("battery", "true");
// We have a battery, return info about it // Charging state
//Battery Charge Level
QString tmp;
tmp.setNum(val);
retObject.insert("level", tmp);
//Battery Charging State
int state = General::RunCommand("apm -a").toInt(&ok);
if ( ok && state == 0 ) if ( ok && state == 0 )
retObject.insert("status", "offline"); retObject.insert("status", "offline");
else if ( ok && state == 1 ) else if ( ok && state == 1 )
@@ -42,8 +38,20 @@ QJsonObject SysMgmt::batteryInfo(){
retObject.insert("status", "backup"); retObject.insert("status", "backup");
else else
retObject.insert("status", "unknown"); retObject.insert("status", "unknown");
// Charging Level
int timeleft = General::RunCommand("apm -t").toInt(&ok); 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 ) { if ( ok ) {
tmp.setNum(timeleft); tmp.setNum(timeleft);
retObject.insert("timeleft", tmp); retObject.insert("timeleft", tmp);
@@ -65,7 +73,7 @@ QJsonObject SysMgmt::cpuPercentage() {
QStringList result = General::RunCommand("sysctl -n kern.cp_times").split(" "); QStringList result = General::RunCommand("sysctl -n kern.cp_times").split(" ");
static QStringList last = QStringList(); static QStringList last = QStringList();
if(last.isEmpty()){ if(last.isEmpty()){
//need two ticks before it works properly //need two ticks before it works properly
sleep(1); sleep(1);
last = result; last = result;
result = General::RunCommand("sysctl -n kern.cp_times").split(" "); 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 //Adjust/all the data to correspond to diffs from the previous check
for(int j=0; j<5; j++){ for(int j=0; j<5; j++){
QString tmp = result[i-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 //need the difference between last run and this one
sum += result[i-j].toLong(); sum += result[i-j].toLong();
last[i-j] = tmp; //make sure to keep the original value around for the next run 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; QString tmp;
long pageSize; long pageSize;
bool ok; bool ok;
// Get the page size // Get the page size
tmp = General::RunCommand("sysctl -n vm.stats.vm.v_page_size").simplified(); tmp = General::RunCommand("sysctl -n vm.stats.vm.v_page_size").simplified();
tmp.toLong(&ok); tmp.toLong(&ok);
@@ -262,7 +270,7 @@ QJsonObject SysMgmt::procInfo() {
for(int i=0; i<output.length(); i++){ for(int i=0; i<output.length(); i++){
if (output.at(i).contains("PID") && output.at(i).contains("USERNAME")){ if (output.at(i).contains("PID") && output.at(i).contains("USERNAME")){
inSection = true; inSection = true;
continue; continue;
} }
if (!inSection) if (!inSection)
continue; continue;
@@ -291,6 +299,26 @@ QJsonObject SysMgmt::procInfo() {
return retObject; 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 // Set a sysctl
QJsonObject SysMgmt::setSysctl(QJsonObject jsin) { QJsonObject SysMgmt::setSysctl(QJsonObject jsin) {
QJsonObject retObject; QJsonObject retObject;
@@ -317,18 +345,29 @@ QJsonObject SysMgmt::sysctlList() {
QJsonObject retObject; QJsonObject retObject;
// This can be cleaned up and not use CLI // 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++){ for(int i=0; i<output.length(); i++){
if ( output.at(i).isEmpty()) if ( output.at(i).isEmpty()){ continue; }
continue; QJsonObject tmp;
sysctl = output.at(i).section(":", 0, 0); sysctl = output.at(i).section(":", 0, 0);
value = output.at(i).section(":", 1, -1).simplified(); tmp.insert("value", output.at(i).section(":", 1, -1).simplified() );
retObject.insert(sysctl, value); 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; return retObject;
} }
@@ -391,3 +430,28 @@ QJsonObject SysMgmt::systemReboot() {
return retObject; 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 <QJsonObject>
#include "sysadm-global.h" #include "sysadm-global.h"
namespace sysadm{ namespace sysadm{
class SysMgmt{ class SysMgmt{
@@ -21,13 +22,21 @@ public:
static QJsonObject killProc(QJsonObject); static QJsonObject killProc(QJsonObject);
static QJsonObject memoryStats(); static QJsonObject memoryStats();
static QJsonObject procInfo(); static QJsonObject procInfo();
//sysctl management
static QJsonObject getSysctl(QJsonObject);
static QJsonObject setSysctl(QJsonObject); static QJsonObject setSysctl(QJsonObject);
static QJsonObject sysctlList(); static QJsonObject sysctlList();
static QJsonObject systemInfo(); static QJsonObject systemInfo();
static QJsonObject systemReboot(); static QJsonObject systemReboot();
static QJsonObject systemHalt(); 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 #endif

View File

@@ -11,8 +11,9 @@
#include "globals.h" #include "globals.h"
#define UP_PIDFILE "/tmp/.updateInProgress" #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 "/tmp/.updatesAvailable"
#define UP_UPFILE_ERR "/tmp/.updateCheckError"
#define UP_CONFFILE "/usr/local/etc/trueos.conf" #define UP_CONFFILE "/usr/local/etc/trueos.conf"
@@ -34,15 +35,23 @@ QDateTime Update::rebootRequiredSince(){
QJsonObject Update::checkUpdates(bool fast) { 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) //NOTE: The "fast" option should only be used for automated/timed checks (to prevent doing this long check too frequently)
QJsonObject retObject; QJsonObject retObject;
qDebug() << "Check for updates: fast=" << fast; //qDebug() << "Check for updates: fast=" << fast;
//Quick check to ensure the tool is available //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; return retObject;
} }
//Check if the system is waiting to reboot //Check if the system is waiting to reboot
if(QFile::exists(UP_RBFILE)){ if(QFile::exists(UP_RBFILE)){
retObject.insert("status","rebootrequired"); 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)){ 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 //Also add the information on what updates are pending
retObject.insert("details", sysadm::General::readTextFile(UP_RBFILE).join("\n") ); retObject.insert("details", sysadm::General::readTextFile(UP_RBFILE).join("\n") );
return retObject; return retObject;
@@ -57,36 +66,44 @@ QJsonObject Update::checkUpdates(bool fast) {
return retObject; 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; QStringList output;
QDateTime cdt = QDateTime::currentDateTime(); QDateTime cdt = QDateTime::currentDateTime();
QDateTime fdt = cdt.addDays(-1); //just enough to trip the checks below if needed 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(); } 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() << "Time Stamps (now/file):" << cdt << fdt;
//qDebug() << " - File earlier than now:" << (fdt<cdt); //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 (+day):" << (fdt.addDays(1)<cdt);
//qDebug() << " - File +1 day earlier than now (+secs):" << (fdt.addSecs(24*60)<cdt); //qDebug() << " - File +1 day earlier than now (+secs):" << (fdt.addSecs(24*60)<cdt);
//qDebug() << " - Seconds from File->Day time:" << fdt.secsTo(cdt); //qDebug() << " - Seconds from File->Day time:" << fdt.secsTo(cdt);
int secs = fdt.secsTo(cdt); int secs = fdt.secsTo(cdt);
if(fast && (secs<43200) ){ if(fast && (secs<43200) && !lasterr ){
//Note: The "fast" check will only be used if the last full check was less than 12 hours earlier. //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"; //qDebug() << " - UseFast Re-read";
output = General::readTextFile(UP_UPFILE); if(lasterr){output = General::readTextFile(UP_UPFILE_ERR); }
}else if(secs<600 ){ else{ output = General::readTextFile(UP_UPFILE); }
//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)"; }else if(secs<300){
output = General::readTextFile(UP_UPFILE); //Note: This will re-use the previous check if it was less than 5 minutes ago (prevent hammering servers from user checks)
}else{ //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"; //qDebug() << " - Run full check";
//output = General::RunCommand("pc-updatemanager check").split("\n"); QStringList cmds;
output.append( General::RunCommand("pc-updatemanager pkgcheck").split("\n") ); if(tool.endsWith("/pc-updatemanager")){
while(output.last().simplified()==""){ output.removeLast(); } cmds << "pc-updatemanager syncconf" << "pc-updatemanager pkgcheck";
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
} }
DISPATCHER->queueProcess("sysadm_update_checkupdates", cmds );
retObject.insert("status", "checkingforupdates");
//qDebug() << " - Done starting check";
return retObject;
} }
//qDebug() << "pc-updatemanager checks:" << output; //qDebug() << "pc-updatemanager checks:" << output;
QString nameval; QString nameval;
int pnum=1; int pnum=1;
for ( int i = 0; i < output.size(); i++) for ( int i = 0; i < output.size(); i++)
@@ -135,10 +152,28 @@ QJsonObject Update::checkUpdates(bool fast) {
retObject.insert("status", "updatesavailable"); retObject.insert("status", "updatesavailable");
} }
retObject.insert("details", output.join("\n") ); //full details of the check for updates 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; 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 // List available branches we can switch to
QJsonObject Update::listBranches() { QJsonObject Update::listBranches() {
QJsonObject retObject; QJsonObject retObject;
@@ -174,50 +209,23 @@ QJsonObject Update::listBranches() {
QJsonObject Update::startUpdate(QJsonObject jsin) { QJsonObject Update::startUpdate(QJsonObject jsin) {
QJsonObject retObject; QJsonObject retObject;
QStringList keys = jsin.keys(); //Quick check to ensure the tool is available
if (! keys.contains("target") ) { QString tool = "/usr/local/bin/pc-updatemanager";
retObject.insert("error", "Missing required key 'target'"); QStringList flags; flags << "pkgupdate";
if(!QFile::exists(tool)){
return retObject; 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 // Create a unique ID for this queued action
QString ID = QUuid::createUuid().toString(); QString ID = QUuid::createUuid().toString();
// Queue the update action // 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 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 // 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("comment", "Task Queued");
retObject.insert("queueid", ID); retObject.insert("queueid", ID);
return retObject; return retObject;
@@ -247,11 +255,19 @@ QJsonObject Update::stopUpdate() {
return ret; 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 //SETTINGS OPTIONS
QJsonObject Update::readSettings(){ QJsonObject Update::readSettings(){
QJsonObject ret; QJsonObject ret;
QStringList knownsettings; 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); QStringList info = General::readTextFile(UP_CONFFILE);
for(int i=0; i<info.length(); i++){ for(int i=0; i<info.length(); i++){
@@ -269,7 +285,7 @@ QJsonObject Update::writeSettings(QJsonObject obj){
QJsonObject ret; QJsonObject ret;
//Check inputs //Check inputs
QStringList knownsettings; 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 keys = obj.keys();
QStringList vals; QStringList vals;
bool clearlastCheck = false; bool clearlastCheck = false;
@@ -306,6 +322,7 @@ QJsonObject Update::writeSettings(QJsonObject obj){
ret.insert("result","success"); ret.insert("result","success");
if(clearlastCheck){ 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)){ 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{ }else{
ret.insert("result","error"); ret.insert("result","error");

View File

@@ -19,10 +19,13 @@ public:
static QDateTime rebootRequiredSince(); static QDateTime rebootRequiredSince();
//Listing routines //Listing routines
static QJsonObject checkUpdates(bool fast = false); static QJsonObject checkUpdates(bool fast = false);
static void saveCheckUpdateLog(QString); //Internal for Dispatcher process usage - do not expose to public API
static QJsonObject listBranches(); static QJsonObject listBranches();
//Start/stop update routine //Start/stop update routine
static QJsonObject startUpdate(QJsonObject); static QJsonObject startUpdate(QJsonObject);
static QJsonObject stopUpdate(); static QJsonObject stopUpdate();
static QJsonObject applyUpdates();
//Read/write update settings //Read/write update settings
static QJsonObject readSettings(); static QJsonObject readSettings();
static QJsonObject writeSettings(QJsonObject); static QJsonObject writeSettings(QJsonObject);
@@ -31,7 +34,7 @@ public:
static QJsonObject readLog(QJsonObject); static QJsonObject readLog(QJsonObject);
}; };
} }
#endif #endif

View File

@@ -37,18 +37,17 @@ void MessageOutput(QtMsgType type, const QMessageLogContext &context, const QStr
break; break;
case QtWarningMsg: case QtWarningMsg:
txt = QString("WARNING: %1").arg(msg); txt = QString("WARNING: %1").arg(msg);
txt += "\n Context: "+QString(context.file)+" Line: "+QString(context.line)+" Function: "+QString(context.function);
break; break;
case QtCriticalMsg: case QtCriticalMsg:
txt = QString("CRITICAL: %1").arg(msg); txt = QString("CRITICAL: %1").arg(msg);
txt += "\n Context: "+QString(context.file)+" Line: "+QString(context.line)+" Function: "+QString(context.function);
break; break;
case QtFatalMsg: case QtFatalMsg:
txt = QString("FATAL: %1").arg(msg); txt = QString("FATAL: %1").arg(msg);
txt += "\n Context: "+QString(context.file)+" Line: "+QString(context.line)+" Function: "+QString(context.function);
break; 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); QTextStream out(&logfile);
out << txt; out << txt;
if(!txt.endsWith("\n")){ out << "\n"; } 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 ) int main( int argc, char ** argv )
{ {
//Check whether running as root //Check whether running as root
if( getuid() != 0){ if( getuid() != 0){
qDebug() << "sysadm-server must be started as root!"; qDebug() << "sysadm-server must be started as root!";
@@ -121,7 +119,7 @@ int main( int argc, char ** argv )
QSslCertificate cert(&cfile); QSslCertificate cert(&cfile);
cfile.close(); cfile.close();
if(!cert.isNull()){ if(!cert.isNull()){
if(i+1<argc){ if(i+1<argc){
i++; QString filepath = argv[i]; i++; QString filepath = argv[i];
QFile outfile(filepath); QFile outfile(filepath);
outfile.open(QIODevice::WriteOnly | QIODevice::Truncate); 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(); } if(!file.open(QIODevice::ReadOnly)){ qDebug() << "Could not open file:" << file.fileName(); }
else{ else{
QByteArray enc_key; QByteArray enc_key;
if(file.fileName().endsWith(".crt")){ if(file.fileName().endsWith(".crt")){
QSslCertificate cert(&file, QSsl::Pem); QSslCertificate cert(&file, QSsl::Pem);
if(!cert.isNull()){ enc_key = cert.publicKey().toPem(); } if(!cert.isNull()){ enc_key = cert.publicKey().toPem(); }
}else if(file.fileName().endsWith(".key")){ }else if(file.fileName().endsWith(".key")){
QSslKey key( &file, QSsl::Rsa, QSsl::Pem, QSsl::PublicKey); QSslKey key( &file, QSsl::Rsa, QSsl::Pem, QSsl::PublicKey);
if(!key.isNull()){ enc_key = key.toPem(); } if(!key.isNull()){ enc_key = key.toPem(); }
}else{ }else{
qDebug() << "Error: Unknown file type (need .crt or .key file)"; qDebug() << "Error: Unknown file type (need .crt or .key file)";
@@ -172,13 +170,13 @@ int main( int argc, char ** argv )
if(QFile::exists(key)){ if(QFile::exists(key)){
QFile file(key); QFile file(key);
QByteArray pubkey; QByteArray pubkey;
if(file.open(QIODevice::ReadOnly)){ if(file.open(QIODevice::ReadOnly)){
QSslKey sslkey( &file, QSsl::Rsa, QSsl::Pem, QSsl::PublicKey); QSslKey sslkey( &file, QSsl::Rsa, QSsl::Pem, QSsl::PublicKey);
if(!key.isNull()){ pubkey = sslkey.toPem(); } if(!key.isNull()){ pubkey = sslkey.toPem(); }
else{ qDebug() << "Invalid Key file:" << file.fileName(); ok = false; } else{ qDebug() << "Invalid Key file:" << file.fileName(); ok = false; }
file.close(); file.close();
}else{ qDebug() << "Could not open file:" << file.fileName(); ok = false; } }else{ qDebug() << "Could not open file:" << file.fileName(); ok = false; }
} }
if(ok){ ok = AuthorizationManager::RegisterCertificateInternal(user, key, nickname, email); } if(ok){ ok = AuthorizationManager::RegisterCertificateInternal(user, key, nickname, email); }
if(ok){ qDebug() << "Key Added" << user << nickname; } if(ok){ qDebug() << "Key Added" << user << nickname; }
else{ qDebug() << "Could not add key"; } */ else{ qDebug() << "Could not add key"; } */
@@ -193,18 +191,20 @@ int main( int argc, char ** argv )
QCoreApplication a(argc, argv); QCoreApplication a(argc, argv);
//Now load the config file //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()){ if(!conf.filter("[internal]").isEmpty()){
//Older QSettings file - move it to the new location //Older QSettings file - move it to the new location
if(QFile::exists(SETTINGSFILE)){ QFile::remove(SETTINGSFILE); } //remove the new/empty settings file 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 CONFIG->sync(); //re-sync settings structure
conf.clear(); //No config yet conf.clear(); //No config yet
} }
//Load the settings from the config file //Load the settings from the config file
// - port number // - port number
if(port==0){ if(port==0){
if(websocket){ if(websocket){
int index = conf.indexOf(QRegExp("PORT=*",Qt::CaseSensitive,QRegExp::Wildcard)); int index = conf.indexOf(QRegExp("PORT=*",Qt::CaseSensitive,QRegExp::Wildcard));
bool ok = false; bool ok = false;
if(index>=0){ port = conf[index].section("=",1,1).toInt(&ok); } 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)); int index = conf.indexOf(QRegExp("PORT_REST=*",Qt::CaseSensitive,QRegExp::Wildcard));
bool ok = false; bool ok = false;
if(index>=0){ port = conf[index].section("=",1,1).toInt(&ok); } 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 // - Blacklist options
@@ -238,7 +238,7 @@ int main( int argc, char ** argv )
rg = QRegExp("BRIDGE_CONNECTIONS_ONLY=*",Qt::CaseSensitive,QRegExp::Wildcard); rg = QRegExp("BRIDGE_CONNECTIONS_ONLY=*",Qt::CaseSensitive,QRegExp::Wildcard);
if(!conf.filter(rg).isEmpty()){ if(!conf.filter(rg).isEmpty()){
BRIDGE_ONLY = conf.filter(rg).first().section("=",1,1).simplified().toLower()=="true"; BRIDGE_ONLY = conf.filter(rg).first().section("=",1,1).simplified().toLower()=="true";
} }
//Setup the log file //Setup the log file
LogManager::checkLogDir(); //ensure the logging directory exists LogManager::checkLogDir(); //ensure the logging directory exists
@@ -254,18 +254,17 @@ int main( int argc, char ** argv )
} }
logfile.open(QIODevice::WriteOnly | QIODevice::Append); logfile.open(QIODevice::WriteOnly | QIODevice::Append);
qInstallMessageHandler(MessageOutput); qInstallMessageHandler(MessageOutput);
//Connect the background classes //Connect the background classes
QObject::connect(DISPATCHER, SIGNAL(DispatchEvent(QJsonObject)), EVENTS, SLOT(DispatchEvent(QJsonObject)) ); QObject::connect(DISPATCHER, SIGNAL(DispatchEvent(QJsonObject)), EVENTS, SLOT(DispatchEvent(QJsonObject)) );
QObject::connect(DISPATCHER, SIGNAL(DispatchStarting(QString)), EVENTS, SLOT(DispatchStarting(QString)) ); QObject::connect(DISPATCHER, SIGNAL(DispatchStarting(QString)), EVENTS, SLOT(DispatchStarting(QString)) );
//Create the daemon //Create the daemon
qDebug() << "Starting the PC-BSD sysadm server...." << (websocket ? "(WebSocket)" : "(TCP)"); qDebug() << "Starting the sysadm server...." << (websocket ? "(WebSocket)" : "(TCP)");
WebServer *w = new WebServer(); WebServer *w = new WebServer();
//Start the daemon //Start the daemon
int ret = 1; //error return value int ret = 1; //error return value
if( w->startServer(port, websocket) ){ if( w->startServer(port, websocket) ){
qDebug() << " - Configuration File:" << CONFIG->fileName(); //qDebug() << " - Configuration File:" << CONFIG->fileName();
QThread TBACK, TBACK2; QThread TBACK, TBACK2;
EVENTS->moveToThread(&TBACK); EVENTS->moveToThread(&TBACK);
DISPATCHER->moveToThread(&TBACK2); DISPATCHER->moveToThread(&TBACK2);
@@ -283,7 +282,7 @@ int main( int argc, char ** argv )
//Cleanup any globals //Cleanup any globals
delete CONFIG; delete CONFIG;
logfile.close(); logfile.close();
//Return //Return
return ret; return ret;
} }

View File

@@ -40,3 +40,10 @@ INSTALLS += target scripts
QMAKE_LIBDIR = /usr/local/lib/qt5 /usr/local/lib QMAKE_LIBDIR = /usr/local/lib/qt5 /usr/local/lib
INCLUDEPATH += /usr/local/include INCLUDEPATH += /usr/local/include
LIBS += -L/usr/local/lib -lpam -lutil -lssl -lcrypto 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