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

@@ -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();
} }
// ================================ // ================================
@@ -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,31 +214,32 @@ 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;
} }
@@ -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);
} }

View File

@@ -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;
@@ -63,6 +63,7 @@ public:
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,10 +71,10 @@ 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
@@ -83,8 +84,8 @@ private:
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

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
@@ -40,10 +45,14 @@ QJsonObject Dispatcher::CreateDispatcherEventNotification(QString ID, QJsonObjec
// == 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")){
@@ -72,10 +81,20 @@ QJsonObject Dispatcher::CreateDispatcherEventNotification(QString ID, QJsonObjec
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

@@ -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 ===
@@ -127,7 +127,7 @@ void EventWatcher::DispatchEvent(QJsonObject 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,6 +159,7 @@ 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();
@@ -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();
@@ -247,7 +248,6 @@ void EventWatcher::ReadLPLogFile(){
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);
} }
} }
} }
@@ -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();
@@ -369,6 +370,7 @@ 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;
@@ -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));
} }

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");
@@ -77,15 +77,15 @@ RestOutputStruct::ExitCode WebSocket::AvailableSubsystems(bool allaccess, QJsonO
} }
// - 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;
} }
@@ -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
@@ -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
@@ -913,6 +950,10 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmPkgRequest(const QJsonValue
//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")
@@ -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

@@ -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
//=========================================== //===========================================
@@ -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;
} }
@@ -60,7 +65,7 @@ QString NetDevice::ipAsString(){
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));
} }
@@ -105,13 +110,25 @@ QString NetDevice::netmaskAsString(){
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
@@ -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)
@@ -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

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

@@ -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,52 +87,97 @@ 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;
} }
@@ -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();

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");
@@ -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
@@ -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,6 +26,13 @@ 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

View File

@@ -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:
@@ -119,8 +120,50 @@ 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);
} }
//-------------------------------------- //--------------------------------------

View File

@@ -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,7 +87,7 @@ 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)
}; };

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)
@@ -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,6 +82,26 @@ 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"); }
@@ -108,7 +142,7 @@ QJsonObject PKG::pkg_info(QStringList origins, QString repo, QString category, b
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,9 +153,9 @@ 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++){
@@ -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)
@@ -228,57 +269,57 @@ QStringList PKG::pkg_search(QString repo, QString searchterm, QStringList search
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
@@ -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)
//================= //=================

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

View File

@@ -9,6 +9,7 @@ ServiceManager::ServiceManager(QString chroot, QString ip)
{ {
this->chroot = chroot; this->chroot = chroot;
this->ip = ip; this->ip = ip;
usingOpenRC = QFile::exists(chroot+"/sbin/rc-update");
loadRCdata(); 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

@@ -95,6 +95,7 @@ private:
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);
@@ -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,33 +66,41 @@ 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)
//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{ }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;
@@ -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,37 +209,10 @@ 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";
return retObject; if(!QFile::exists(tool)){
}
// 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; return retObject;
} }
@@ -212,12 +220,12 @@ QJsonObject Update::startUpdate(QJsonObject jsin) {
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);

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!";
@@ -193,11 +191,13 @@ 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
} }
@@ -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);

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