Build and package ogagent py3/qt6 for windows and linux #2

Merged
nserrano merged 9 commits from py3-win into main 2024-07-23 09:58:23 +02:00
30 changed files with 1180 additions and 357 deletions

12
.gitignore vendored
View File

@ -11,10 +11,20 @@ linux/debian/ogagent.postinst.debhelper
linux/debian/ogagent.postrm.debhelper
linux/configure-stamp
linux/build-stamp
macos/build
windows/vc_redist.x64.exe
ogagent_*_all.deb
ogagent_*_amd64.buildinfo
ogagent_*_amd64.changes
ogagent_*_amd64.build
OGAgentInstaller-*.pkg
OGAgentSetup-*.exe
bin
src/build
src/dist
src/about_dialog_ui.py
src/message_dialog_ui.py
src/OGAgent_rc.py
src/dist
src/build
windows/VERSION
windows/VC_redist.x64.exe

View File

@ -39,7 +39,6 @@ install-ogagent:
cp $(SOURCEDIR)/OGAgentUser.py $(LIBDIR)
# QT Dialogs & resources
cp $(SOURCEDIR)/*_ui.py $(LIBDIR)
cp $(SOURCEDIR)/OGAgent_rc.py $(LIBDIR)
# Version file
cp $(SOURCEDIR)/VERSION $(LIBDIR)

View File

@ -1,3 +1,11 @@
ogagent (1.3.1-1) stable; urgency=medium
* Migrate the update script from shell to python
* pyinstaller: include the 'img' subdir
* take icons from 'img'
-- OpenGnsys developers <info@opengnsys.es> Wed, 26 Jun 2024 15:16:57 +0200
ogagent (1.3.0-2) stable; urgency=medium
* Add missing dependency on zenity

0
linux/desktop/OGAgentTool.desktop 100755 → 100644
View File

View File

@ -1,9 +1,9 @@
#!/bin/bash
# Create macOS installation packages.
# Based on bomutils tutorail: http://bomutils.dyndns.org/tutorial.html
# Based on bomutils tutorial: http://bomutils.dyndns.org/tutorial.html
cd $(dirname $0)
[ -r ../src/VERSION ] && VERSION="$(cat ../src/VERSION)" || VERSION="1.1.0"
[ -r ../src/VERSION ] && VERSION="$(cat ../src/VERSION)" || (echo "Can't get version from ../src/VERSION" 1>&2; exit 1)
AUTHOR="OpenGnsys Project"
# Create empty directories.
@ -12,8 +12,8 @@ mkdir -p build && cd build
mkdir -p flat/base.pkg flat/Resources/en.lproj
mkdir -p root/Applications
# Copy application and script files.
cp -a ../../src root/Applications/OGAgent.app
# Copy application and script files. Exclude 'test_modules'
cp -a ../../src root/Applications/OGAgent.app; rm -rf root/Applications/OGAgent.app/test_modules
cp -a ../scripts .
# Create plist file.
@ -84,4 +84,3 @@ EOT
# Create new Xar application archive.
rm -f ../../../OGAgentInstaller-$VERSION.pkg
( cd flat && xar --compression none -cf "../../../OGAgentInstaller-$VERSION.pkg" * )

View File

@ -2,10 +2,10 @@
<plist version="1.0">
<dict>
<key>Label</key>
<string>es.opengnsys.ogagent</string>
<string>es.opengnsys.agent.system</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/ogagent</string>
<string>/usr/local/bin/ogagent</string>
<string>start</string>
</array>
<key>RunAtLoad</key>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC -//Apple Computer//DTD PLIST 1.0//EN http://www.apple.com/DTDs/PropertyList-1.0.dtd>
<plist version="1.0">
<dict>
<key>Label</key>
<string>es.opengnsys.agent.user</string>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/usr/local/bin:/usr/bin:/bin</string>
</dict>
<key>WorkingDirectory</key>
<string>/Applications/OGAgent.app</string>
<key>ProgramArguments</key>
<array>
<string>/Applications/OGAgent.app/OGAgentUser.py</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>

832
macos/scripts/ip.py 100755
View File

@ -0,0 +1,832 @@
#!/usr/bin/env python3
# encoding: utf8
"""
This program is taken from https://github.com/brona/iproute2mac
When doing 'brew install iproute2mac', we get an old version (1.4.2) that doesn't support 'ip -json'
The alternative installation method recomended in the project's README file is 'curl; chmod; mv'
Therefore we make the decision of shipping this ip.py (version 1.5.0) along opengnsys, which is pretty much the same as curling it
"""
"""
iproute2mac
CLI wrapper for basic network utilites on Mac OS X.
Homepage: https://github.com/brona/iproute2mac
The MIT License (MIT)
Copyright (c) 2015 Bronislav Robenek <brona@robenek.me>
"""
import ipaddress
import json
import os
import random
import re
import socket
import string
import subprocess
import sys
import types
# Version
VERSION = "1.5.0"
# Utilities
SUDO = "/usr/bin/sudo"
IFCONFIG = "/sbin/ifconfig"
ROUTE = "/sbin/route"
NETSTAT = "/usr/sbin/netstat"
NDP = "/usr/sbin/ndp"
ARP = "/usr/sbin/arp"
NETWORKSETUP = "/usr/sbin/networksetup"
# Helper functions
def perror(*args):
sys.stderr.write(*args)
sys.stderr.write("\n")
def execute_cmd(cmd):
print("Executing: %s" % cmd)
status, output = subprocess.getstatusoutput(cmd)
if status == 0: # unix/linux commands 0 true, 1 false
print(output)
return True
else:
perror(output)
return False
def json_dump(data, pretty):
if pretty:
print(json.dumps(data, indent=4))
else:
print(json.dumps(data, separators=(",", ":")))
return True
# Classful to CIDR conversion with "default" being passed through
def cidr_from_netstat_dst(target):
if target == "default":
return target
dots = target.count(".")
if target.find("/") == -1:
addr = target
netmask = (dots + 1) * 8
else:
[addr, netmask] = target.split("/")
addr = addr + ".0" * (3 - dots)
return addr + "/" + str(netmask)
# Convert hexadecimal netmask in prefix length
def netmask_to_length(mask):
return int(mask, 16).bit_count()
def any_startswith(words, test):
for word in words:
if word.startswith(test):
return True
return False
# Handles passsing return value, error messages and program exit on error
def help_msg(help_func):
def wrapper(func):
def inner(*args, **kwargs):
if not func(*args, **kwargs):
specific = eval(help_func)
if specific:
if isinstance(specific, types.FunctionType):
if args and kwargs:
specific(*args, **kwargs)
else:
specific()
return False
else:
raise Exception("Function expected for: " + help_func)
else:
raise Exception(
"Function variant not defined: " + help_func
)
return True
return inner
return wrapper
# Generate random MAC address with XenSource Inc. OUI
# http://www.linux-kvm.com/sites/default/files/macgen.py
def randomMAC():
mac = [
0x00,
0x16,
0x3E,
random.randint(0x00, 0x7F),
random.randint(0x00, 0xFF),
random.randint(0x00, 0xFF),
]
return ":".join(["%02x" % x for x in mac])
# Decode ifconfig output
def parse_ifconfig(res, af, address):
links = []
count = 1
for r in res.split("\n"):
if re.match(r"^\w+:", r):
if count > 1:
links.append(link)
(ifname, flags, mtu, ifindex) = re.findall(r"^(\w+): flags=\d+<(.*)> mtu (\d+) index (\d+)", r)[0]
flags = flags.split(",")
link = {
"ifindex": int(ifindex),
"ifname": ifname,
"flags": flags,
"mtu": int(mtu),
"operstate": "UNKNOWN",
"link_type": "unknown"
}
if "LOOPBACK" in flags:
link["link_type"] = "loopback"
link["address"] = "00:00:00:00:00:00"
link["broadcast"] = "00:00:00:00:00:00"
elif "POINTOPOINT" in flags:
link["link_type"] = "none"
count = count + 1
else:
if re.match(r"^\s+ether ", r):
link["link_type"] = "ether"
link["address"] = re.findall(r"(\w\w:\w\w:\w\w:\w\w:\w\w:\w\w)", r)[0]
link["broadcast"] = "ff:ff:ff:ff:ff:ff"
elif address and re.match(r"^\s+inet ", r) and af != 6:
(local, netmask) = re.findall(r"inet (\d+\.\d+\.\d+\.\d+) netmask (0x[0-9a-f]+)", r)[0]
addr = {
"family": "inet",
"local": local,
"prefixlen": netmask_to_length(netmask),
}
if re.match(r"^.*broadcast", r):
addr["broadcast"] = re.findall(r"broadcast (\d+\.\d+\.\d+\.\d+)", r)[0]
link["addr_info"] = link.get("addr_info", []) + [addr]
elif address and re.match(r"^\s+inet6 ", r) and af != 4:
(local, prefixlen) = re.findall(r"inet6 ((?:[a-f0-9:]+:+)+[a-f0-9]+)%*\w* +prefixlen (\d+)", r)[0]
link["addr_info"] = link.get("addr_info", []) + [{
"family": "inet6",
"local": local,
"prefixlen": int(prefixlen)
}]
elif re.match(r"^\s+status: ", r):
match re.findall(r"status: (\w+)", r)[0]:
case "active":
link["operstate"] = "UP"
case "inactive":
link["operstate"] = "DOWN"
if count > 1:
links.append(link)
return links
def link_addr_show(argv, af, json_print, pretty_json, address):
if len(argv) > 0 and argv[0] == "dev":
argv.pop(0)
if len(argv) > 0:
param = argv[0]
else:
param = "-a"
status, res = subprocess.getstatusoutput(
IFCONFIG + " -v " + param + " 2>/dev/null"
)
if status: # unix status
if res == "":
perror(param + " not found")
else:
perror(res)
return False
links = parse_ifconfig(res, af, address)
if json_print:
return json_dump(links, pretty_json)
for l in links:
print("%d: %s: <%s> mtu %d status %s" % (
l["ifindex"], l["ifname"], ",".join(l["flags"]), l["mtu"], l["operstate"]
))
print(
" link/" + l["link_type"] +
((" " + l["address"]) if "address" in l else "") +
((" brd " + l["broadcast"]) if "broadcast" in l else "")
)
for a in l.get("addr_info", []):
print(
" %s %s/%d" % (a["family"], a["local"], a["prefixlen"]) +
((" brd " + a["broadcast"]) if "broadcast" in a else "")
)
return True
# Help
def do_help(argv=None, af=None, json_print=None, pretty_json=None):
perror("Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }")
perror("where OBJECT := { link | addr | route | neigh }")
perror(" OPTIONS := { -V[ersion] | -j[son] | -p[retty] |")
perror(" -4 | -6 }")
perror("iproute2mac")
perror("Homepage: https://github.com/brona/iproute2mac")
perror(
"This is CLI wrapper for basic network utilities on Mac OS X"
" inspired with iproute2 on Linux systems."
)
perror(
"Provided functionality is limited and command output is not"
" fully compatible with iproute2."
)
perror(
"For advanced usage use netstat, ifconfig, ndp, arp, route "
" and networksetup directly."
)
exit(255)
def do_help_route():
perror("Usage: ip route list")
perror(" ip route get ADDRESS")
perror(" ip route { add | del | replace } ROUTE")
perror(" ip route flush cache")
perror(" ip route flush table main")
perror("ROUTE := NODE_SPEC [ INFO_SPEC ]")
perror("NODE_SPEC := [ TYPE ] PREFIX")
perror("INFO_SPEC := NH")
perror("TYPE := { blackhole }")
perror("NH := { via ADDRESS | gw ADDRESS | nexthop ADDRESS | dev STRING }")
exit(255)
def do_help_addr():
perror("Usage: ip addr show [ dev STRING ]")
perror(" ip addr { add | del } PREFIX dev STRING")
exit(255)
def do_help_link():
perror("Usage: ip link show [ DEVICE ]")
perror(" ip link set dev DEVICE")
perror(" [ { up | down } ]")
perror(" [ address { LLADDR | factory | random } ]")
perror(" [ mtu MTU ]")
exit(255)
def do_help_neigh():
perror("Usage: ip neighbour show [ [ to ] PREFIX ] [ dev DEV ]")
perror(" ip neighbour flush [ dev DEV ]")
exit(255)
# Route Module
@help_msg("do_help_route")
def do_route(argv, af, json_print, pretty_json):
if not argv or (
any_startswith(["show", "lst", "list"], argv[0]) and len(argv) == 1
):
return do_route_list(af, json_print, pretty_json)
elif "get".startswith(argv[0]) and len(argv) == 2:
argv.pop(0)
return do_route_get(argv, af, json_print, pretty_json)
elif "add".startswith(argv[0]) and len(argv) >= 3:
argv.pop(0)
return do_route_add(argv, af)
elif "delete".startswith(argv[0]) and len(argv) >= 2:
argv.pop(0)
return do_route_del(argv, af)
elif "replace".startswith(argv[0]) and len(argv) >= 3:
argv.pop(0)
return do_route_del(argv, af) and do_route_add(argv, af)
elif "flush".startswith(argv[0]) and len(argv) >= 1:
argv.pop(0)
return do_route_flush(argv, af)
else:
return False
return True
def do_route_list(af, json_print, pretty_json):
# ip route prints IPv6 or IPv4, never both
inet = "inet6" if af == 6 else "inet"
status, res = subprocess.getstatusoutput(
NETSTAT + " -nr -f " + inet + " 2>/dev/null"
)
if status:
perror(res)
return False
res = res.split("\n")
res = res[4:] # Removes first 4 lines
routes = []
for r in res:
ra = r.split()
target = ra[0]
gw = ra[1]
flags = ra[2]
# macOS Mojave and earlier vs Catalina
dev = ra[5] if len(ra) >= 6 else ra[3]
if flags.find("W") != -1:
continue
if af == 6:
target = re.sub(r"%[^ ]+/", "/", target)
else:
target = cidr_from_netstat_dst(target)
if flags.find("B") != -1:
routes.append({"type": "blackhole", "dst": target, "flags": []})
continue
if re.match(r"link.+", gw):
routes.append({"dst": target, "dev": dev, "scope": "link", "flags": []})
else:
routes.append({"dst": target, "gateway": gw, "dev": dev, "flags": []})
if json_print:
return json_dump(routes, pretty_json)
for route in routes:
if "type" in route:
print("%s %s" % (route["type"], route["dst"]))
elif "scope" in route:
print("%s dev %s scope %s" % (route["dst"], route["dev"], route["scope"]))
elif "gateway" in route:
print("%s via %s dev %s" % (route["dst"], route["gateway"], route["dev"]))
return True
def do_route_add(argv, af):
options = ""
if argv[0] == "blackhole":
argv.pop(0)
if len(argv) != 1:
return False
argv.append("via")
argv.append("::1" if ":" in argv[0] or af == 6 else "127.0.0.1")
options = "-blackhole"
if len(argv) not in (3, 5):
return False
if len(argv) == 5:
perror(
"iproute2mac: Ignoring last 2 arguments, not implemented: {} {}".format(
argv[3], argv[4]
)
)
if argv[1] in ["via", "nexthop", "gw"]:
gw = argv[2]
elif argv[1] in ["dev"]:
gw = "-interface " + argv[2]
else:
do_help_route()
prefix = argv[0]
inet = "-inet6 " if ":" in prefix or af == 6 else ""
return execute_cmd(
SUDO + " " + ROUTE + " add " + inet + prefix + " " + gw + " " + options
)
def do_route_del(argv, af):
options = ""
if argv[0] == "blackhole":
argv.pop(0)
if len(argv) != 1:
return False
if ":" in argv[0] or af == 6:
options = " ::1 -blackhole"
else:
options = " 127.0.0.1 -blackhole"
prefix = argv[0]
inet = "-inet6 " if ":" in prefix or af == 6 else ""
return execute_cmd(
SUDO + " " + ROUTE + " delete " + inet + prefix + options
)
def do_route_flush(argv, af):
if not argv:
perror('"ip route flush" requires arguments.')
perror("")
return False
# https://github.com/brona/iproute2mac/issues/38
# http://linux-ip.net/html/tools-ip-route.html
if argv[0] == "cache":
print("iproute2mac: There is no route cache to flush in MacOS,")
print(" returning 0 status code for compatibility.")
return True
elif len(argv) == 2 and argv[0] == "table" and argv[1] == "main":
family = "-inet6" if af == 6 else "-inet"
print("iproute2mac: Flushing all routes")
return execute_cmd(SUDO + " " + ROUTE + " -n flush " + family)
else:
return False
def do_route_get(argv, af, json_print, pretty_json):
target = argv[0]
inet = ""
if ":" in target or af == 6:
inet = "-inet6 "
family = socket.AF_INET6
else:
family = socket.AF_INET
status, res = subprocess.getstatusoutput(
ROUTE + " -n get " + inet + target
)
if status: # unix status or not in table
perror(res)
return False
if res.find("not in table") >= 0:
perror(res)
exit(1)
res = dict(
re.findall(
r"^\W*((?:route to|destination|gateway|interface)): (.+)$",
res,
re.MULTILINE,
)
)
route = {"dst": res["route to"], "dev": res["interface"]}
if "gateway" in res:
route["gateway"] = res["gateway"]
try:
s = socket.socket(family, socket.SOCK_DGRAM)
s.connect((route["dst"], 7))
route["prefsrc"] = src_ip = s.getsockname()[0]
s.close()
except:
pass
route["flags"] = []
route["uid"] = os.getuid()
route["cache"] = []
if json_print:
return json_dump([route], pretty_json)
print(
route["dst"] +
((" via " + route["gateway"]) if "gateway" in route else "") +
" dev " + route["dev"] +
((" src " + route["prefsrc"]) if "prefsrc" in route else "") +
" uid " + str(route["uid"])
)
return True
# Addr Module
@help_msg("do_help_addr")
def do_addr(argv, af, json_print, pretty_json):
if not argv:
argv.append("show")
if any_startswith(["show", "lst", "list"], argv[0]):
argv.pop(0)
return do_addr_show(argv, af, json_print, pretty_json)
elif "add".startswith(argv[0]) and len(argv) >= 3:
argv.pop(0)
return do_addr_add(argv, af)
elif "delete".startswith(argv[0]) and len(argv) >= 3:
argv.pop(0)
return do_addr_del(argv, af)
else:
return False
return True
def do_addr_show(argv, af, json_print, pretty_json):
return link_addr_show(argv, af, json_print, pretty_json, True)
def do_addr_add(argv, af):
if len(argv) < 2:
return False
dst = ""
if argv[1] == "peer":
argv.pop(1)
dst = argv.pop(1)
if argv[1] == "dev":
argv.pop(1)
else:
return False
try:
addr = argv[0]
dev = argv[1]
except IndexError:
perror("dev not found")
exit(1)
inet = ""
if ":" in addr or af == 6:
af = 6
inet = " inet6"
return execute_cmd(
SUDO + " " + IFCONFIG + " " + dev + inet + " add " + addr + " " + dst
)
def do_addr_del(argv, af):
if len(argv) < 2:
return False
if argv[1] == "dev":
argv.pop(1)
try:
addr = argv[0]
dev = argv[1]
except IndexError:
perror("dev not found")
exit(1)
inet = "inet"
if ":" in addr or af == 6:
af = 6
inet = "inet6"
return execute_cmd(
SUDO + " " + IFCONFIG + " " + dev + " " + inet + " " + addr + " remove"
)
# Link module
@help_msg("do_help_link")
def do_link(argv, af, json_print, pretty_json):
if not argv:
argv.append("show")
if any_startswith(["show", "lst", "list"], argv[0]):
argv.pop(0)
return do_link_show(argv, af, json_print, pretty_json)
elif "set".startswith(argv[0]):
argv.pop(0)
return do_link_set(argv, af)
else:
return False
return True
def do_link_show(argv, af, json_print, pretty_json):
return link_addr_show(argv, af, json_print, pretty_json, False)
def do_link_set(argv, af):
if not argv:
return False
elif argv[0] == "dev":
argv.pop(0)
if len(argv) < 2:
return False
dev = argv[0]
IFCONFIG_DEV_CMD = SUDO + " " + IFCONFIG + " " + dev
try:
args = iter(argv)
for arg in args:
if arg == "up":
if not execute_cmd(IFCONFIG_DEV_CMD + " up"):
return False
elif arg == "down":
if not execute_cmd(IFCONFIG_DEV_CMD + " down"):
return False
elif arg in ["address", "addr", "lladdr"]:
addr = next(args)
if addr in ["random", "rand"]:
addr = randomMAC()
elif addr == "factory":
(status, res) = subprocess.getstatusoutput(
NETWORKSETUP + " -listallhardwareports"
)
if status != 0:
return False
details = re.findall(
r"^(?:Device|Ethernet Address): (.+)$",
res,
re.MULTILINE,
)
addr = details[details.index(dev) + 1]
if not execute_cmd(IFCONFIG_DEV_CMD + " lladdr " + addr):
return False
elif arg == "mtu":
mtu = int(next(args))
if not execute_cmd(IFCONFIG_DEV_CMD + " mtu " + str(mtu)):
return False
except Exception:
return False
return True
# Neigh module
@help_msg("do_help_neigh")
def do_neigh(argv, af, json_print, pretty_json):
if not argv:
argv.append("show")
if any_startswith(["show", "list", "lst"], argv[0]) and len(argv) <= 5:
argv.pop(0)
return do_neigh_show(argv, af, json_print, pretty_json)
elif "flush".startswith(argv[0]):
argv.pop(0)
return do_neigh_flush(argv, af)
else:
return False
def do_neigh_show(argv, af, json_print, pretty_json):
prefix = None
dev = None
try:
while argv:
arg = argv.pop(0)
if arg == "to":
prefix = argv.pop(0)
elif arg == "dev":
dev = argv.pop(0)
elif prefix is None:
prefix = arg
else:
return False
if prefix:
prefix = ipaddress.ip_network(prefix, strict=False)
except Exception:
return False
nd_ll_states = {
"R": "REACHABLE",
"S": "STALE",
"D": "DELAY",
"P": "PROBE",
"I": "INCOMPLETE",
"N": "INCOMPLETE",
"W": "INCOMPLETE",
}
neighs = []
if af != 4:
res = subprocess.run(
[NDP, "-an"], capture_output=True, text=True, check=True
)
for row in res.stdout.splitlines()[1:]:
cols = row.split()
entry = {"dst": re.sub(r"%.+$", "", cols[0])}
if cols[1] != "(incomplete)":
entry["lladdr"] = cols[1]
entry["dev"] = cols[2]
if dev and entry["dev"] != dev:
continue
if prefix and ipaddress.ip_address(entry["dst"]) not in prefix:
continue
if cols[1] == "(incomplete)" and cols[4] != "R":
entry["status"] = ["INCOMPLETE"]
else:
entry["status"] = [nd_ll_states[cols[4]]]
entry["router"] = len(cols) >= 6 and cols[5] == "R"
neighs.append(entry)
if af != 6:
args = [ARP, "-anl"]
if dev:
args += ["-i", dev]
res = subprocess.run(args, capture_output=True, text=True, check=True)
for row in res.stdout.splitlines()[1:]:
cols = row.split()
entry = {"dst": cols[0]}
if cols[1] != "(incomplete)":
entry["lladdr"] = cols[1]
entry["dev"] = cols[4]
if dev and entry["dev"] != dev:
continue
if prefix and ipaddress.ip_address(entry["dst"]) not in prefix:
continue
if cols[1] == "(incomplete)":
entry["status"] = ["INCOMPLETE"]
else:
entry["status"] = ["REACHABLE"]
entry["router"] = False
neighs.append(entry)
if json_print:
return json_dump(neighs, pretty_json)
for nb in neighs:
print(
nb["dst"] +
("", " dev " + nb["dev"], "")[dev == None] +
("", " router")[nb["router"]] +
" %s" % (nb["status"][0])
)
return True
def do_neigh_flush(argv, af):
if len(argv) != 2:
perror("Flush requires arguments.")
exit(1)
if argv[0] != "dev":
return False
dev = argv[1]
if af != 4:
print(
"iproute2mac: NDP doesn't support filtering by interface,"
"flushing all IPv6 entries."
)
execute_cmd(SUDO + " " + NDP + " -cn")
if af != 6:
execute_cmd(SUDO + " " + ARP + " -a -d -i " + dev)
return True
# Match iproute2 commands
# https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/ip.c#n86
cmds = [
("address", do_addr),
("route", do_route),
("neighbor", do_neigh),
("neighbour", do_neigh),
("link", do_link),
("help", do_help),
]
@help_msg("do_help")
def main(argv):
af = -1 # default / both
json_print = False
pretty_json = False
while argv and argv[0].startswith("-"):
# Turn --opt into -opt
argv[0] = argv[0][1:] if argv[0][1] == "-" else argv[0]
# Process options
if argv[0] == "-6":
af = 6
argv.pop(0)
elif argv[0] == "-4":
af = 4
argv.pop(0)
elif argv[0].startswith("-color"):
perror("iproute2mac: Color option is not implemented")
argv.pop(0)
elif "-json".startswith(argv[0]):
json_print = True
argv.pop(0)
elif "-pretty".startswith(argv[0]):
pretty_json = True
argv.pop(0)
elif "-Version".startswith(argv[0]):
print("iproute2mac, v" + VERSION)
exit(0)
elif "-help".startswith(argv[0]):
return False
else:
perror('Option "{}" is unknown, try "ip help".'.format(argv[0]))
exit(255)
if not argv:
return False
for cmd, cmd_func in cmds:
if cmd.startswith(argv[0]):
argv.pop(0)
# Functions return true or terminate with exit(255)
# See help_msg and do_help*
return cmd_func(argv, af, json_print, pretty_json)
perror('Object "{}" is unknown, try "ip help".'.format(argv[0]))
exit(1)
if __name__ == "__main__":
main(sys.argv[1:])

View File

@ -3,4 +3,4 @@
FOLDER=/Applications/OGAgent.app
cd $FOLDER
python -m opengnsys.linux.OGAgentService $@
/usr/local/bin/python3 -m opengnsys.linux.OGAgentService $@

View File

@ -1,17 +1,11 @@
#!/usr/bin/env sh
# Directories
SRCDIR=$(dirname "$0")
BINDIR=/usr/bin
INITDIR=/Library/LaunchDaemons
BINDIR=/usr/local/bin
LAUNCH_AGENTS_DIR=/Library/LaunchAgents
LAUNCH_DAEMONS_DIR=/Library/LaunchDaemons
# Check if it needs to install Python dependencies:
if ! which pip &>/dev/null; then
easy_install pip
pip install netifaces requests six
fi
# Copying files.
cp $SRCDIR/ogagent $BINDIR
cp $SRCDIR/es.opengnsys.ogagent.plist $INITDIR
cp $SRCDIR/ip.py $BINDIR/ip ## override 'ip' from iproute2mac-1.4.2 with a more recent one
cp $SRCDIR/es.opengnsys.agent.system.plist $LAUNCH_DAEMONS_DIR
cp $SRCDIR/es.opengnsys.agent.user.plist $LAUNCH_AGENTS_DIR

97
src/OGAgent.spec 100755
View File

@ -0,0 +1,97 @@
# -*- mode: python ; coding: utf-8 -*-
ogausr_a = Analysis(
['OGAgentUser.py'],
pathex=[],
binaries=[],
datas=[
# ('cfg', 'cfg'), ## add the entire directory
('img', 'img'), ## add the entire directory
],
hiddenimports=['win32timezone', 'socketserver', 'http.server', 'urllib'],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
ogasvc_a = Analysis(
['opengnsys\\windows\\OGAgentService.py'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=['win32timezone', 'socketserver', 'http.server', 'urllib'],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
MERGE(
(ogausr_a, 'OGAgentUser', 'OGAgentUser'), ## class, py name, exe name
(ogasvc_a, 'OGAgentService', 'OGAgentService')
)
ogausr_pyz = PYZ(ogausr_a.pure)
ogasvc_pyz = PYZ(ogasvc_a.pure)
ogausr_exe = EXE(
ogausr_pyz,
ogausr_a.scripts,
[],
exclude_binaries=True,
name='OGAgentUser',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon=['img\\oga.ico'],
)
ogasvc_exe = EXE(
ogasvc_pyz,
ogasvc_a.scripts,
[],
exclude_binaries=True,
name='OGAgentService',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon=['img\\oga.ico'],
manifest='OGAgent.manifest',
)
dist_name = 'OGAgent'
coll = COLLECT(
ogausr_exe,
ogausr_a.binaries,
ogausr_a.datas,
ogasvc_exe,
ogasvc_a.binaries,
ogasvc_a.datas,
strip=False,
upx=True,
upx_exclude=[],
name=dist_name,
)
import shutil
shutil.copytree ('cfg', '{}/{}/cfg'.format(DISTPATH, dist_name))

4
src/OGAgentUser.py 100644 → 100755
View File

@ -34,6 +34,7 @@ import base64
import json
import sys
import time
import os
from PyQt6 import QtCore, QtGui, QtWidgets
from about_dialog_ui import Ui_OGAAboutDialog
@ -159,7 +160,8 @@ class OGASystemTray(QtWidgets.QSystemTrayIcon):
self.ipcport = int(cfg.get('ipc_port', IPC_PORT))
icon = QtGui.QIcon(':/images/img/oga.png')
QtCore.QDir.addSearchPath('images', os.path.join(os.path.dirname(__file__), 'img'))
icon = QtGui.QIcon('images:oga.png')
QtWidgets.QSystemTrayIcon.__init__(self, icon, parent)
self.menu = QtWidgets.QMenu(parent)

View File

@ -1 +1 @@
1.3.0
1.3.3

View File

@ -32,7 +32,7 @@
# pylint: disable=unused-wildcard-import, wildcard-import
from configparser import SafeConfigParser
from configparser import ConfigParser
config = None
@ -45,7 +45,7 @@ def readConfig(client=False):
This is this way so we can protect ogagent.cfg against reading for non admin users on all platforms.
"""
cfg = SafeConfigParser()
cfg = ConfigParser()
if client is True:
fname = 'ogclient.cfg'
else:

View File

@ -57,7 +57,8 @@ class HTTPServerHandler(BaseHTTPRequestHandler):
return
def sendJsonResponse(self, data):
self.send_response(200)
try: self.send_response(200)
except Exception as e: logger.warn (str(e))
data = json.dumps(data)
self.send_header('Content-type', 'application/json')
self.send_header('Content-Length', str(len(data)))
@ -117,7 +118,7 @@ class HTTPServerHandler(BaseHTTPRequestHandler):
logger.error('HTTP ' + fmt % args)
def log_message(self, fmt, *args):
logger.info('HTTP ' + fmt % args)
logger.debug('HTTP ' + fmt % args)
class HTTPThreadingServer(ThreadingMixIn, HTTPServer):
@ -132,7 +133,9 @@ class HTTPServerThread(threading.Thread):
self.certFile = createSelfSignedCert()
self.server = HTTPThreadingServer(address, HTTPServerHandler)
self.server.socket = ssl.wrap_socket(self.server.socket, certfile=self.certFile, server_side=True)
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(certfile=self.certFile)
self.server.socket = context.wrap_socket(self.server.socket, server_side=True)
logger.debug('Initialized HTTPS Server thread on {}'.format(address))

View File

@ -42,45 +42,69 @@ import subprocess
import struct
import array
import six
import json
from opengnsys import utils
import netifaces
ip_a_s = None
## make sure /usr/local/bin is in PATH
## we need that to run /usr/local/bin/ip, which uses 'env' and pulls from PATH anyway
def check_path():
path = os.getenv ('PATH', '')
usr_local_bin = '/usr/local/bin'
if usr_local_bin not in path.split (os.pathsep):
os.environ['PATH'] = usr_local_bin + os.pathsep + path
def _getMacAddr(ifname):
'''
Returns the mac address of an interface
Mac is returned as unicode utf-8 encoded
'''
if isinstance(ifname, list):
return dict([(name, _getMacAddr(name)) for name in ifname])
if isinstance(ifname, six.text_type):
ifname = ifname.encode('utf-8') # If unicode, convert to bytes (or str in python 2.7)
try:
return netifaces.ifaddresses(ifname)[18][0]['addr']
except Exception:
return None
for interface in ip_a_s:
if interface.get ('ifname') != ifname: continue
return interface.get ('address')
return None
def _getIpAddr(ifname):
'''
Returns the IP address of an interface
Returns the first IP address of an interface
IPv4 is preferred over IPv6
IP is returned as unicode utf-8 encoded
'''
if isinstance(ifname, list):
return dict([(name, _getIpAddr(name)) for name in ifname])
if isinstance(ifname, six.text_type):
ifname = ifname.encode('utf-8') # If unicode, convert to bytes (or str in python 2.7)
try:
return netifaces.ifaddresses(ifname)[2][0]['addr']
except Exception:
return None
## loop and return the first IPv4 address found
for interface in ip_a_s:
if interface.get ('ifname') != ifname: continue
for addr_info in interface.get ('addr_info', []):
ip_address = addr_info.get ('local')
try:
ip_address.index ('.')
return ip_address
except: pass
## if nothing found, loop again and return the first IP found, which will be an IPv6
for interface in ip_a_s:
if interface.get ('ifname') != ifname: continue
for addr_info in interface.get ('addr_info', []):
return addr_info.get ('local')
return None
def _getInterfaces():
'''
Returns a list of interfaces names
'''
return netifaces.interfaces()
global ip_a_s
check_path()
result = subprocess.run (['/usr/local/bin/ip', '-json', 'address', 'show'], capture_output=True, text=True)
if result.returncode != 0: raise Exception (f'Command "ip" failed with exit code {result.returncode}')
ip_a_s = json.loads (result.stdout)
return [i.get('ifname') for i in ip_a_s]
def _getIpAndMac(ifname):
@ -152,7 +176,7 @@ def logoff():
import threading
threading._DummyThread._Thread__stop = lambda x: 42
# Exec logout using AppleSctipt
# Exec logout using AppleScript
subprocess.call('/usr/bin/osascript -e \'tell app "System Events" to «event aevtrlgo»\'', shell=True)
@ -244,7 +268,19 @@ def getSessionLanguage():
'''
Returns the user's session language
'''
return locale.getdefaultlocale()[0]
lang = locale.getdefaultlocale()[0]
if lang is None:
return 'C'
else:
return lang
def get_session_type():
"""
Minimal implementation of this required function
:return: string
"""
return 'unknown'
def showPopup(title, message):

View File

@ -65,7 +65,7 @@ def check_secret(fnc):
else:
raise Exception('Unauthorized operation')
except Exception as e:
logger.error(e)
logger.error(str(e))
raise Exception(e)
return wrapper
@ -83,7 +83,7 @@ def execution_level(level):
else:
raise Exception('Unauthorized operation')
except Exception as e:
logger.error(e)
logger.error(str(e))
raise Exception(e)
return wrapper
@ -149,6 +149,7 @@ class OpenGnSysWorker(ServerWorker):
break
# Raise error after timeout
if not self.interface:
## UnboundLocalError: cannot access local variable 'e' where it is not associated with a value
raise e
# Loop to send initialization message
@ -332,11 +333,23 @@ class OpenGnSysWorker(ServerWorker):
:return: JSON object {"op": "launched"}
"""
logger.debug('Processing script request')
# Decoding script (Windows scripts need a subprocess call per line)
# Decoding script
script = urllib.parse.unquote(base64.b64decode(post_params.get('script')).decode('utf-8'))
logger.debug('received script {}'.format(script))
if operations.os_type == 'Windows':
script = 'import subprocess; {0}'.format(
';'.join(['subprocess.check_output({0},shell=True)'.format(repr(c)) for c in script.split('\n')]))
## for windows, we turn the script into utf16le, then to b64 again, and feed the blob to powershell
u16 = script.encode ('utf-16le') ## utf16
b64 = base64.b64encode (u16).decode ('utf-8') ## b64 (which returns bytes, so we need an additional decode(utf8))
script = """
import os
import tempfile
import subprocess
cp = subprocess.run ("powershell -WindowStyle Hidden -EncodedCommand {}", capture_output=True)
subprocs_log = os.path.join (tempfile.gettempdir(), 'opengnsys-subprocs.log')
with open (subprocs_log, 'ab') as fd: ## TODO improve this logging
fd.write (cp.stdout)
fd.write (cp.stderr)
""".format (b64)
else:
script = 'import subprocess; subprocess.check_output("""{0}""",shell=True)'.format(script)
# Executing script.

View File

@ -61,7 +61,7 @@ def exceptionToMessage(e):
if isinstance(arg, Exception):
msg = msg + exceptionToMessage(arg)
else:
msg = msg + toUnicode(arg) + '. '
msg = msg + str(arg) + '. '
return msg

View File

@ -120,5 +120,9 @@ class OGAgentSvc(win32serviceutil.ServiceFramework, CommonService):
if __name__ == '__main__':
win32serviceutil.HandleCommandLine(OGAgentSvc)
if len(sys.argv) == 1:
servicemanager.Initialize()
servicemanager.PrepareToHostSingle(OGAgentSvc)
servicemanager.StartServiceCtrlDispatcher()
else:
win32serviceutil.HandleCommandLine(OGAgentSvc)

View File

@ -43,11 +43,11 @@ OTHER, DEBUG, INFO, WARN, ERROR, FATAL = (10000 * (x + 1) for x in range(6))
class LocalLogger(object):
def __init__(self):
# tempdir is different for "user application" and "service"
# service wil get c:\windows\temp, while user will get c:\users\XXX\temp
# service wil get c:\windows\temp, while user will get c:\users\XXX\appdata\local\temp
logging.basicConfig(
filename=os.path.join(tempfile.gettempdir(), 'opengnsys.log'),
filemode='a',
format='%(levelname)s %(asctime)s %(message)s',
format='%(levelname)s %(asctime)s (%(threadName)s) %(message)s',
level=logging.DEBUG
)
self.logger = logging.getLogger('opengnsys')
@ -58,7 +58,7 @@ class LocalLogger(object):
# our loglevels are 10000 (other), 20000 (debug), ....
# logging levels are 10 (debug), 20 (info)
# OTHER = logging.NOTSET
self.logger.log(level / 1000 - 10, message)
self.logger.log(int(level / 1000 - 10), message)
if level < INFO or self.serviceLogger is False: # Only information and above will be on event log
return

View File

@ -64,7 +64,7 @@ def getNetworkInfo():
ip: ip of the interface
'''
obj = win32com.client.Dispatch("WbemScripting.SWbemLocator")
wmobj = obj.ConnectServer("localhost", "root\cimv2")
wmobj = obj.ConnectServer("localhost", "root\\cimv2")
adapters = wmobj.ExecQuery("Select * from Win32_NetworkAdapterConfiguration where IpEnabled=True")
try:
for obj in adapters:
@ -102,12 +102,12 @@ def getWindowsVersion():
'''
Returns Windows version.
'''
import _winreg
reg = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion')
import winreg
reg = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion')
try:
data = '{} {}'.format(_winreg.QueryValueEx(reg, 'ProductName')[0], _winreg.QueryValueEx(reg, 'ReleaseId')[0])
data = '{} {}'.format(winreg.QueryValueEx(reg, 'ProductName')[0], winreg.QueryValueEx(reg, 'ReleaseId')[0])
except Exception:
data = '{} {}'.format(_winreg.QueryValueEx(reg, 'ProductName')[0], _winreg.QueryValueEx(reg, 'CurrentBuildNumber')[0])
data = '{} {}'.format(winreg.QueryValueEx(reg, 'ProductName')[0], winreg.QueryValueEx(reg, 'CurrentBuildNumber')[0])
reg.Close()
return data

View File

@ -1,3 +1,6 @@
netifaces
pywin32
pyqt6
requests
urllib3
six
pycryptodome
pyinstaller

View File

@ -1,139 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2014 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
@author: Adolfo Gómez, dkmaster at dkmon dot com
@author: Ramón M. Gómez, ramongomez at us dot es
"""
# ModuleFinder can't handle runtime changes to __path__, but win32com uses them
try:
# py2exe 0.6.4 introduced a replacement modulefinder.
# This means we have to add package paths there, not to the built-in
# one. If this new modulefinder gets integrated into Python, then
# we might be able to revert this some day.
# if this doesn't work, try import modulefinder
try:
import py2exe.mf as modulefinder
except ImportError:
import modulefinder
import win32com
import sys
for p in win32com.__path__[1:]:
modulefinder.AddPackagePath("win32com", p)
for extra in ["win32com.shell"]: # ,"win32com.mapi"
__import__(extra)
m = sys.modules[extra]
for p in m.__path__[1:]:
modulefinder.AddPackagePath(extra, p)
except ImportError:
# no build path setup, no worries.
pass
import os
from distutils.core import setup
import py2exe
import sys
# update.sh changes this value
VERSION='1.3.0'
sys.argv.append('py2exe')
def get_requests_cert_file():
"""Add Python requests or certifi .pem file for installers."""
import requests
f = os.path.join(os.path.dirname(requests.__file__), 'cacert.pem')
if not os.path.exists(f):
import certifi
f = os.path.join(os.path.dirname(certifi.__file__), 'cacert.pem')
return f
class Target:
def __init__(self, **kw):
self.__dict__.update(kw)
# for the versioninfo resources
self.version = VERSION
self.name = 'OGAgentService'
self.description = 'OpenGnsys Agent Service'
self.author = 'Adolfo Gomez'
self.url = 'https://opengnsys.es/'
self.company_name = "OpenGnsys Project"
self.copyright = "(c) 2014 VirtualCable S.L.U."
self.name = "OpenGnsys Agent"
# Now you need to pass arguments to setup
# windows is a list of scripts that have their own UI and
# thus don't need to run in a console.
ogaservice = Target(
description='OpenGnsys Agent Service',
modules=['opengnsys.windows.OGAgentService'],
icon_resources=[(0, 'img\\oga.ico'), (1, 'img\\oga.ico')],
cmdline_style='pywin32'
)
# Some test_modules are hidden to py2exe by six, we ensure that they appear on "includes"
HIDDEN_BY_SIX = ['SocketServer', 'SimpleHTTPServer', 'urllib']
setup(
windows=[
{
'script': 'OGAgentUser-qt4.py',
'icon_resources': [(0, 'img\\oga.ico'), (1, 'img\\oga.ico')]
},
],
console=[
{
'script': 'OGAServiceHelper.py'
}
],
service=[ogaservice],
data_files=[('', [get_requests_cert_file()]), ('cfg', ['cfg/ogagent.cfg', 'cfg/ogclient.cfg'])],
options={
'py2exe': {
'bundle_files': 3,
'compressed': True,
'optimize': 2,
'includes': ['sip', 'PyQt4', 'win32com.shell', 'requests', 'encodings', 'encodings.utf_8'] + HIDDEN_BY_SIX,
'excludes': ['doctest', 'unittest'],
'dll_excludes': ['msvcp90.dll'],
'dist_dir': '..\\bin',
}
},
name='OpenGnsys Agent',
version=VERSION,
description='OpenGnsys Agent',
author='Adolfo Gomez',
author_email='agomez@virtualcable.es',
zipfile='OGAgent.zip',
)

40
src/update.py 100755
View File

@ -0,0 +1,40 @@
#!/usr/bin/python3
import os
import re
import sys
import subprocess
import fileinput
def update_version():
if os.path.isfile ('VERSION'):
with open ('VERSION', 'r') as version_file:
version = version_file.read().strip()
pattern = r'[0-9]*\.[0-9]*\.[0-9]*'
matches = re.findall (pattern, version)
win_version = matches[0]
with fileinput.FileInput ('about-dialog.ui', inplace=True) as file:
for line in file:
print (line.replace ('Version [^<]*', f'Version {version}'), end='')
with fileinput.FileInput ('opengnsys/__init__.py', inplace=True) as file:
for line in file:
print(line.replace ('VERSION=.*', f"VERSION='{version}'"), end='')
with open ('../windows/VERSION', 'w') as outfile:
outfile.write (win_version + '\n')
else:
print ('VERSION: No such file or directory')
sys.exit (1)
def process_ui():
subprocess.run (['pyuic6', 'about-dialog.ui', '-o', 'about_dialog_ui.py', '-x'])
subprocess.run (['pyuic6', 'message-dialog.ui', '-o', 'message_dialog_ui.py', '-x'])
if __name__ == "__main__":
os.chdir (os.path.dirname (os.path.abspath (__file__)))
update_version()
process_ui()

View File

@ -1,59 +0,0 @@
#!/bin/bash
#
# Copyright (c) 2014 Virtual Cable S.L.
# Copyright (c) 2024 Qindel Formación y Servicios S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
function update_version {
if [[ -r VERSION ]]; then
V="$(cat VERSION)"
sed -i "s/Version [^<]*/Version $V/" about-dialog.ui
sed -i "s/^VERSION='.*'$/VERSION='$V'/" setup.py
sed -i "s/^VERSION='.*'$/VERSION='$V'/" opengnsys/__init__.py
else
echo 'src/VERSION: No such file or directory'
exit 1
fi
}
function process_ui {
pyuic6 about-dialog.ui -o about_dialog_ui.py -x
pyuic6 message-dialog.ui -o message_dialog_ui.py -x
}
function process_resources {
## requires a virtualenv with pyside6
## you can create it by doing 'mkvirtualenv -p python3 ogpyside6'
## this will obviously go away in the future, but we need to merge the py3/qt6 change now
~/.virtualenvs/ogpyside6/bin/pyside6-rcc -o OGAgent_rc.py OGAgent.qrc
sed -i -e '/^from PySide6 import QtCore/s/PySide6/PyQt6/' OGAgent_rc.py
}
cd $(dirname "$0")
update_version
process_ui
process_resources

View File

@ -1,7 +0,0 @@
#!/bin/bash
cd "$(dirname "$0")"
export WINEARCH=win32
export WINEPREFIX=$PWD/wine
grep -o "[0-9]*\.[0-9]*\.[0-9]*" ../src/VERSION > VERSION
wine cmd /c c:\\ogagent\\build.bat
chmod -x ../OGAgentSetup*.exe

View File

@ -1,7 +0,0 @@
C:
CD \ogagent\src
python setup.py
CD ..
RENAME bin\OGAgentUser-qt4.exe OGAgentUser.exe
"C:\Program Files\NSIS\makensis.exe" ogagent.nsi

View File

@ -88,8 +88,8 @@ Section -Main SEC0000
SetShellVarContext all
SetOutPath $INSTDIR
SetOverwrite on
File /r bin\*.*
File vcredist_x86.exe
File /r src\dist\OGAgent\*.*
File windows\VC_redist.x64.exe
File src\VERSION
WriteRegStr HKLM "${REGKEY}\Components" Main 1
SectionEnd
@ -111,7 +111,7 @@ Section -post SEC0001
WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" OGAgentTool $INSTDIR\OGAgentUser.exe
WriteRegDWORD HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoModify 1
WriteRegDWORD HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoRepair 1
ExecWait '"$INSTDIR\vcredist_x86.exe" /passive /norestart'
ExecWait '"$INSTDIR\VC_redist.x64.exe" /passive /norestart'
# Add the application to the firewall exception list - All Networks - All IP Version - Enabled
# SimpleFC::AddApplication "OpenGnsys Agent Service" "$INSTDIR\OGAgentService.exe" 0 2 "" 1
# SimpleFC::AdvAddRule [name] [description] [protocol] [direction]
@ -128,8 +128,6 @@ Section -post SEC0001
WriteRegDWORD HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Power" HiberbootEnabled 0
# Install service
nsExec::Exec /OEM "$INSTDIR\OGAgentService.exe --startup auto install" # Add service after installation
# Update recovery options
nsExec::Exec /OEM "$INSTDIR\OGAServiceHelper.exe"
Exec "net start ogagent"
Exec "$INSTDIR\OGAgentUser.exe"
SectionEnd

View File

@ -1,67 +0,0 @@
#!/bin/sh
# We need:
# * Wine (32 bit)
# * winetricks (in some distributions)
export WINEARCH=win32 WINEPREFIX=$PWD/wine WINEDEBUG=fixme-all
WINE=wine
download() {
mkdir downloads
# Get needed software
cd downloads
wget -nd https://www.python.org/ftp/python/2.7.17/python-2.7.17.msi -O python-2.7.msi
wget -nd https://download.microsoft.com/download/7/9/6/796EF2E4-801B-4FC4-AB28-B59FBF6D907B/VCForPython27.msi
wget -nd https://bootstrap.pypa.io/get-pip.py
wget -nd https://sourceforge.net/projects/pyqt/files/PyQt4/PyQt-4.11.4/PyQt4-4.11.4-gpl-Py2.7-Qt4.8.7-x32.exe/download -O pyqt-install.exe
wget -nd https://prdownloads.sourceforge.net/nsis/nsis-3.05-setup.exe?download -O nsis-install.exe
wget -nd http://nsis.sourceforge.net/mediawiki/images/d/d7/NSIS_Simple_Firewall_Plugin_1.20.zip
cd ..
}
install_python() {
if which winetricks &>/dev/null; then
echo "Setting up wine prefix (using winetricks)"
winetricks
fi
cd downloads
echo "Installing python"
$WINE msiexec /qn /i python-2.7.msi
echo "Installing vc for python"
$WINE msiexec /qn /i VCForPython27.msi
echo "Installing pyqt (needs X)"
$WINE pyqt-install.exe
echo "Installing nsis (needs X?)"
$WINE nsis-install.exe
cd ..
}
setup_pip() {
echo "Seting up pip..."
$WINE C:\\Python27\\python -m pip install --upgrade pip
}
install_packages() {
echo "Installing pywin32"
$WINE C:\\Python27\\python -m pip install pywin32
echo "Installing py2exe"
$WINE C:\\Python27\\python -m pip install py2exe_py2
echo "Installing required packages"
$WINE C:\\Python27\\python -m pip install requests six
# Using easy_install instead of pip to install pycrypto
$WINE C:\\Python27\\Scripts\\easy_install http://www.voidspace.org.uk/python/pycrypto-2.6.1/pycrypto-2.6.1.win32-py2.7.exe
# Copy nsis required NSIS_Simple_Firewall_Plugin_1
echo "Copying simple firewall plugin for nsis installer"
unzip -o downloads/NSIS_Simple_Firewall_Plugin_1.20.zip SimpleFC.dll -d $WINEPREFIX/drive_c/Program\ Files/NSIS/Plugins/x86-ansi/
unzip -o downloads/NSIS_Simple_Firewall_Plugin_1.20.zip SimpleFC.dll -d $WINEPREFIX/drive_c/Program\ Files/NSIS/Plugins/x86-unicode/
}
download
install_python
setup_pip
install_packages

43
windows/setup.bat 100644
View File

@ -0,0 +1,43 @@
C:
cd \Users\Docker\Downloads
mkdir setup
rem creamos directorio setup, nos bajamos cosas y mas tarde lo borramos
rem pero el VC_redist hace falta para el empaquetado, asi que lo descargamos fuera de setup para no borrarlo
curl https://www.python.org/ftp/python/3.12.3/amd64/core.msi --output setup\core.msi
curl https://www.python.org/ftp/python/3.12.3/amd64/dev.msi --output setup\dev.msi
curl https://www.python.org/ftp/python/3.12.3/amd64/exe.msi --output setup\exe.msi
curl https://www.python.org/ftp/python/3.12.3/amd64/lib.msi --output setup\lib.msi
curl https://www.python.org/ftp/python/3.12.3/amd64/path.msi --output setup\path.msi
curl https://www.python.org/ftp/python/3.12.3/amd64/pip.msi --output setup\pip.msi
curl https://aka.ms/vs/17/release/vc_redist.x64.exe --location --output VC_redist.x64.exe
curl https://prdownloads.sourceforge.net/nsis/nsis-3.05-setup.exe?download --location --output setup\nsis-install.exe
curl http://nsis.sourceforge.net/mediawiki/images/d/d7/NSIS_Simple_Firewall_Plugin_1.20.zip --location --output setup\NSIS_Simple_Firewall_Plugin_1.20.zip
cd setup
msiexec /i core.msi TARGETDIR=C:\Python312
msiexec /i dev.msi TARGETDIR=C:\Python312
msiexec /i exe.msi TARGETDIR=C:\Python312
msiexec /i lib.msi TARGETDIR=C:\Python312
msiexec /i path.msi TARGETDIR=C:\Python312
msiexec /i pip.msi TARGETDIR=C:\Python312
cd ..
VC_redist.x64.exe /install /quiet /passive /norestart
ftype PythonScript=C:\Python312\python.exe "%1" %*
assoc .py=PythonScript
PATH=C:\Python312;C:\Python312\Scripts;%PATH%
setx PATH "C:\Python312;C:\Python312\Scripts;%PATH%"
python -m pip install --upgrade wheel pip
pip install -r F:\src\requirements.txt
setup\nsis-install.exe /S
powershell -command "Expand-Archive setup\NSIS_Simple_Firewall_Plugin_1.20.zip nsis-fp"
copy nsis-fp\SimpleFC.dll "C:\Program Files (x86)\NSIS\Plugins\x86-ansi\"
copy nsis-fp\SimpleFC.dll "C:\Program Files (x86)\NSIS\Plugins\x86-unicode\"
rmdir nsis-fp /s /q
rmdir setup /s /q