Build and package ogagent py3/qt6 for windows and linux #2
|
@ -11,10 +11,20 @@ linux/debian/ogagent.postinst.debhelper
|
||||||
linux/debian/ogagent.postrm.debhelper
|
linux/debian/ogagent.postrm.debhelper
|
||||||
linux/configure-stamp
|
linux/configure-stamp
|
||||||
linux/build-stamp
|
linux/build-stamp
|
||||||
|
macos/build
|
||||||
|
windows/vc_redist.x64.exe
|
||||||
ogagent_*_all.deb
|
ogagent_*_all.deb
|
||||||
ogagent_*_amd64.buildinfo
|
ogagent_*_amd64.buildinfo
|
||||||
ogagent_*_amd64.changes
|
ogagent_*_amd64.changes
|
||||||
ogagent_*_amd64.build
|
ogagent_*_amd64.build
|
||||||
|
OGAgentInstaller-*.pkg
|
||||||
|
OGAgentSetup-*.exe
|
||||||
|
bin
|
||||||
|
src/build
|
||||||
|
src/dist
|
||||||
src/about_dialog_ui.py
|
src/about_dialog_ui.py
|
||||||
src/message_dialog_ui.py
|
src/message_dialog_ui.py
|
||||||
src/OGAgent_rc.py
|
src/dist
|
||||||
|
src/build
|
||||||
|
windows/VERSION
|
||||||
|
windows/VC_redist.x64.exe
|
||||||
|
|
|
@ -39,7 +39,6 @@ install-ogagent:
|
||||||
cp $(SOURCEDIR)/OGAgentUser.py $(LIBDIR)
|
cp $(SOURCEDIR)/OGAgentUser.py $(LIBDIR)
|
||||||
# QT Dialogs & resources
|
# QT Dialogs & resources
|
||||||
cp $(SOURCEDIR)/*_ui.py $(LIBDIR)
|
cp $(SOURCEDIR)/*_ui.py $(LIBDIR)
|
||||||
cp $(SOURCEDIR)/OGAgent_rc.py $(LIBDIR)
|
|
||||||
# Version file
|
# Version file
|
||||||
cp $(SOURCEDIR)/VERSION $(LIBDIR)
|
cp $(SOURCEDIR)/VERSION $(LIBDIR)
|
||||||
|
|
||||||
|
|
|
@ -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
|
ogagent (1.3.0-2) stable; urgency=medium
|
||||||
|
|
||||||
* Add missing dependency on zenity
|
* Add missing dependency on zenity
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Create macOS installation packages.
|
# 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)
|
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"
|
AUTHOR="OpenGnsys Project"
|
||||||
|
|
||||||
# Create empty directories.
|
# Create empty directories.
|
||||||
|
@ -12,8 +12,8 @@ mkdir -p build && cd build
|
||||||
mkdir -p flat/base.pkg flat/Resources/en.lproj
|
mkdir -p flat/base.pkg flat/Resources/en.lproj
|
||||||
mkdir -p root/Applications
|
mkdir -p root/Applications
|
||||||
|
|
||||||
# Copy application and script files.
|
# Copy application and script files. Exclude 'test_modules'
|
||||||
cp -a ../../src root/Applications/OGAgent.app
|
cp -a ../../src root/Applications/OGAgent.app; rm -rf root/Applications/OGAgent.app/test_modules
|
||||||
cp -a ../scripts .
|
cp -a ../scripts .
|
||||||
|
|
||||||
# Create plist file.
|
# Create plist file.
|
||||||
|
@ -84,4 +84,3 @@ EOT
|
||||||
# Create new Xar application archive.
|
# Create new Xar application archive.
|
||||||
rm -f ../../../OGAgentInstaller-$VERSION.pkg
|
rm -f ../../../OGAgentInstaller-$VERSION.pkg
|
||||||
( cd flat && xar --compression none -cf "../../../OGAgentInstaller-$VERSION.pkg" * )
|
( cd flat && xar --compression none -cf "../../../OGAgentInstaller-$VERSION.pkg" * )
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>Label</key>
|
<key>Label</key>
|
||||||
<string>es.opengnsys.ogagent</string>
|
<string>es.opengnsys.agent.system</string>
|
||||||
<key>ProgramArguments</key>
|
<key>ProgramArguments</key>
|
||||||
<array>
|
<array>
|
||||||
<string>/usr/bin/ogagent</string>
|
<string>/usr/local/bin/ogagent</string>
|
||||||
<string>start</string>
|
<string>start</string>
|
||||||
</array>
|
</array>
|
||||||
<key>RunAtLoad</key>
|
<key>RunAtLoad</key>
|
|
@ -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>
|
|
@ -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:])
|
|
@ -3,4 +3,4 @@
|
||||||
FOLDER=/Applications/OGAgent.app
|
FOLDER=/Applications/OGAgent.app
|
||||||
|
|
||||||
cd $FOLDER
|
cd $FOLDER
|
||||||
python -m opengnsys.linux.OGAgentService $@
|
/usr/local/bin/python3 -m opengnsys.linux.OGAgentService $@
|
||||||
|
|
|
@ -1,17 +1,11 @@
|
||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
# Directories
|
|
||||||
SRCDIR=$(dirname "$0")
|
SRCDIR=$(dirname "$0")
|
||||||
BINDIR=/usr/bin
|
BINDIR=/usr/local/bin
|
||||||
INITDIR=/Library/LaunchDaemons
|
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/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
|
||||||
|
|
|
@ -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))
|
|
@ -34,6 +34,7 @@ import base64
|
||||||
import json
|
import json
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
import os
|
||||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
from about_dialog_ui import Ui_OGAAboutDialog
|
from about_dialog_ui import Ui_OGAAboutDialog
|
||||||
|
@ -159,7 +160,8 @@ class OGASystemTray(QtWidgets.QSystemTrayIcon):
|
||||||
|
|
||||||
self.ipcport = int(cfg.get('ipc_port', IPC_PORT))
|
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)
|
QtWidgets.QSystemTrayIcon.__init__(self, icon, parent)
|
||||||
self.menu = QtWidgets.QMenu(parent)
|
self.menu = QtWidgets.QMenu(parent)
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
1.3.0
|
1.3.3
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
# pylint: disable=unused-wildcard-import, wildcard-import
|
# pylint: disable=unused-wildcard-import, wildcard-import
|
||||||
|
|
||||||
|
|
||||||
from configparser import SafeConfigParser
|
from configparser import ConfigParser
|
||||||
|
|
||||||
config = None
|
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.
|
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:
|
if client is True:
|
||||||
fname = 'ogclient.cfg'
|
fname = 'ogclient.cfg'
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -57,7 +57,8 @@ class HTTPServerHandler(BaseHTTPRequestHandler):
|
||||||
return
|
return
|
||||||
|
|
||||||
def sendJsonResponse(self, data):
|
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)
|
data = json.dumps(data)
|
||||||
self.send_header('Content-type', 'application/json')
|
self.send_header('Content-type', 'application/json')
|
||||||
self.send_header('Content-Length', str(len(data)))
|
self.send_header('Content-Length', str(len(data)))
|
||||||
|
@ -117,7 +118,7 @@ class HTTPServerHandler(BaseHTTPRequestHandler):
|
||||||
logger.error('HTTP ' + fmt % args)
|
logger.error('HTTP ' + fmt % args)
|
||||||
|
|
||||||
def log_message(self, fmt, *args):
|
def log_message(self, fmt, *args):
|
||||||
logger.info('HTTP ' + fmt % args)
|
logger.debug('HTTP ' + fmt % args)
|
||||||
|
|
||||||
|
|
||||||
class HTTPThreadingServer(ThreadingMixIn, HTTPServer):
|
class HTTPThreadingServer(ThreadingMixIn, HTTPServer):
|
||||||
|
@ -132,7 +133,9 @@ class HTTPServerThread(threading.Thread):
|
||||||
|
|
||||||
self.certFile = createSelfSignedCert()
|
self.certFile = createSelfSignedCert()
|
||||||
self.server = HTTPThreadingServer(address, HTTPServerHandler)
|
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))
|
logger.debug('Initialized HTTPS Server thread on {}'.format(address))
|
||||||
|
|
||||||
|
|
|
@ -42,45 +42,69 @@ import subprocess
|
||||||
import struct
|
import struct
|
||||||
import array
|
import array
|
||||||
import six
|
import six
|
||||||
|
import json
|
||||||
from opengnsys import utils
|
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):
|
def _getMacAddr(ifname):
|
||||||
'''
|
'''
|
||||||
Returns the mac address of an interface
|
Returns the mac address of an interface
|
||||||
Mac is returned as unicode utf-8 encoded
|
Mac is returned as unicode utf-8 encoded
|
||||||
'''
|
'''
|
||||||
if isinstance(ifname, list):
|
for interface in ip_a_s:
|
||||||
return dict([(name, _getMacAddr(name)) for name in ifname])
|
if interface.get ('ifname') != ifname: continue
|
||||||
if isinstance(ifname, six.text_type):
|
return interface.get ('address')
|
||||||
ifname = ifname.encode('utf-8') # If unicode, convert to bytes (or str in python 2.7)
|
return None
|
||||||
try:
|
|
||||||
return netifaces.ifaddresses(ifname)[18][0]['addr']
|
|
||||||
except Exception:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def _getIpAddr(ifname):
|
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
|
IP is returned as unicode utf-8 encoded
|
||||||
'''
|
'''
|
||||||
if isinstance(ifname, list):
|
|
||||||
return dict([(name, _getIpAddr(name)) for name in ifname])
|
## loop and return the first IPv4 address found
|
||||||
if isinstance(ifname, six.text_type):
|
for interface in ip_a_s:
|
||||||
ifname = ifname.encode('utf-8') # If unicode, convert to bytes (or str in python 2.7)
|
if interface.get ('ifname') != ifname: continue
|
||||||
try:
|
for addr_info in interface.get ('addr_info', []):
|
||||||
return netifaces.ifaddresses(ifname)[2][0]['addr']
|
ip_address = addr_info.get ('local')
|
||||||
except Exception:
|
try:
|
||||||
return None
|
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():
|
def _getInterfaces():
|
||||||
'''
|
'''
|
||||||
Returns a list of interfaces names
|
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):
|
def _getIpAndMac(ifname):
|
||||||
|
@ -152,7 +176,7 @@ def logoff():
|
||||||
import threading
|
import threading
|
||||||
threading._DummyThread._Thread__stop = lambda x: 42
|
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)
|
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
|
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):
|
def showPopup(title, message):
|
||||||
|
|
|
@ -65,7 +65,7 @@ def check_secret(fnc):
|
||||||
else:
|
else:
|
||||||
raise Exception('Unauthorized operation')
|
raise Exception('Unauthorized operation')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(str(e))
|
||||||
raise Exception(e)
|
raise Exception(e)
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
@ -83,7 +83,7 @@ def execution_level(level):
|
||||||
else:
|
else:
|
||||||
raise Exception('Unauthorized operation')
|
raise Exception('Unauthorized operation')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(str(e))
|
||||||
raise Exception(e)
|
raise Exception(e)
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
@ -149,6 +149,7 @@ class OpenGnSysWorker(ServerWorker):
|
||||||
break
|
break
|
||||||
# Raise error after timeout
|
# Raise error after timeout
|
||||||
if not self.interface:
|
if not self.interface:
|
||||||
|
## UnboundLocalError: cannot access local variable 'e' where it is not associated with a value
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
# Loop to send initialization message
|
# Loop to send initialization message
|
||||||
|
@ -332,11 +333,23 @@ class OpenGnSysWorker(ServerWorker):
|
||||||
:return: JSON object {"op": "launched"}
|
:return: JSON object {"op": "launched"}
|
||||||
"""
|
"""
|
||||||
logger.debug('Processing script request')
|
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'))
|
script = urllib.parse.unquote(base64.b64decode(post_params.get('script')).decode('utf-8'))
|
||||||
|
logger.debug('received script {}'.format(script))
|
||||||
if operations.os_type == 'Windows':
|
if operations.os_type == 'Windows':
|
||||||
script = 'import subprocess; {0}'.format(
|
## for windows, we turn the script into utf16le, then to b64 again, and feed the blob to powershell
|
||||||
';'.join(['subprocess.check_output({0},shell=True)'.format(repr(c)) for c in script.split('\n')]))
|
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:
|
else:
|
||||||
script = 'import subprocess; subprocess.check_output("""{0}""",shell=True)'.format(script)
|
script = 'import subprocess; subprocess.check_output("""{0}""",shell=True)'.format(script)
|
||||||
# Executing script.
|
# Executing script.
|
||||||
|
|
|
@ -61,7 +61,7 @@ def exceptionToMessage(e):
|
||||||
if isinstance(arg, Exception):
|
if isinstance(arg, Exception):
|
||||||
msg = msg + exceptionToMessage(arg)
|
msg = msg + exceptionToMessage(arg)
|
||||||
else:
|
else:
|
||||||
msg = msg + toUnicode(arg) + '. '
|
msg = msg + str(arg) + '. '
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -120,5 +120,9 @@ class OGAgentSvc(win32serviceutil.ServiceFramework, CommonService):
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
if len(sys.argv) == 1:
|
||||||
win32serviceutil.HandleCommandLine(OGAgentSvc)
|
servicemanager.Initialize()
|
||||||
|
servicemanager.PrepareToHostSingle(OGAgentSvc)
|
||||||
|
servicemanager.StartServiceCtrlDispatcher()
|
||||||
|
else:
|
||||||
|
win32serviceutil.HandleCommandLine(OGAgentSvc)
|
||||||
|
|
|
@ -43,11 +43,11 @@ OTHER, DEBUG, INFO, WARN, ERROR, FATAL = (10000 * (x + 1) for x in range(6))
|
||||||
class LocalLogger(object):
|
class LocalLogger(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# tempdir is different for "user application" and "service"
|
# 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(
|
logging.basicConfig(
|
||||||
filename=os.path.join(tempfile.gettempdir(), 'opengnsys.log'),
|
filename=os.path.join(tempfile.gettempdir(), 'opengnsys.log'),
|
||||||
filemode='a',
|
filemode='a',
|
||||||
format='%(levelname)s %(asctime)s %(message)s',
|
format='%(levelname)s %(asctime)s (%(threadName)s) %(message)s',
|
||||||
level=logging.DEBUG
|
level=logging.DEBUG
|
||||||
)
|
)
|
||||||
self.logger = logging.getLogger('opengnsys')
|
self.logger = logging.getLogger('opengnsys')
|
||||||
|
@ -58,7 +58,7 @@ class LocalLogger(object):
|
||||||
# our loglevels are 10000 (other), 20000 (debug), ....
|
# our loglevels are 10000 (other), 20000 (debug), ....
|
||||||
# logging levels are 10 (debug), 20 (info)
|
# logging levels are 10 (debug), 20 (info)
|
||||||
# OTHER = logging.NOTSET
|
# 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
|
if level < INFO or self.serviceLogger is False: # Only information and above will be on event log
|
||||||
return
|
return
|
||||||
|
|
|
@ -64,7 +64,7 @@ def getNetworkInfo():
|
||||||
ip: ip of the interface
|
ip: ip of the interface
|
||||||
'''
|
'''
|
||||||
obj = win32com.client.Dispatch("WbemScripting.SWbemLocator")
|
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")
|
adapters = wmobj.ExecQuery("Select * from Win32_NetworkAdapterConfiguration where IpEnabled=True")
|
||||||
try:
|
try:
|
||||||
for obj in adapters:
|
for obj in adapters:
|
||||||
|
@ -102,12 +102,12 @@ def getWindowsVersion():
|
||||||
'''
|
'''
|
||||||
Returns Windows version.
|
Returns Windows version.
|
||||||
'''
|
'''
|
||||||
import _winreg
|
import winreg
|
||||||
reg = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion')
|
reg = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion')
|
||||||
try:
|
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:
|
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()
|
reg.Close()
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
netifaces
|
pywin32
|
||||||
|
pyqt6
|
||||||
requests
|
requests
|
||||||
urllib3
|
six
|
||||||
|
pycryptodome
|
||||||
|
pyinstaller
|
||||||
|
|
139
src/setup.py
139
src/setup.py
|
@ -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',
|
|
||||||
)
|
|
|
@ -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()
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
||||||
|
|
|
@ -88,8 +88,8 @@ Section -Main SEC0000
|
||||||
SetShellVarContext all
|
SetShellVarContext all
|
||||||
SetOutPath $INSTDIR
|
SetOutPath $INSTDIR
|
||||||
SetOverwrite on
|
SetOverwrite on
|
||||||
File /r bin\*.*
|
File /r src\dist\OGAgent\*.*
|
||||||
File vcredist_x86.exe
|
File windows\VC_redist.x64.exe
|
||||||
File src\VERSION
|
File src\VERSION
|
||||||
WriteRegStr HKLM "${REGKEY}\Components" Main 1
|
WriteRegStr HKLM "${REGKEY}\Components" Main 1
|
||||||
SectionEnd
|
SectionEnd
|
||||||
|
@ -111,7 +111,7 @@ Section -post SEC0001
|
||||||
WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" OGAgentTool $INSTDIR\OGAgentUser.exe
|
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)" NoModify 1
|
||||||
WriteRegDWORD HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoRepair 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
|
# 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::AddApplication "OpenGnsys Agent Service" "$INSTDIR\OGAgentService.exe" 0 2 "" 1
|
||||||
# SimpleFC::AdvAddRule [name] [description] [protocol] [direction]
|
# SimpleFC::AdvAddRule [name] [description] [protocol] [direction]
|
||||||
|
@ -128,8 +128,6 @@ Section -post SEC0001
|
||||||
WriteRegDWORD HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Power" HiberbootEnabled 0
|
WriteRegDWORD HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Power" HiberbootEnabled 0
|
||||||
# Install service
|
# Install service
|
||||||
nsExec::Exec /OEM "$INSTDIR\OGAgentService.exe --startup auto install" # Add service after installation
|
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 "net start ogagent"
|
||||||
Exec "$INSTDIR\OGAgentUser.exe"
|
Exec "$INSTDIR\OGAgentUser.exe"
|
||||||
SectionEnd
|
SectionEnd
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
Loading…
Reference in New Issue