Compare commits

...

66 Commits
no-tls ... main

Author SHA1 Message Date
Natalia Serrano c0483c93c1 Merge pull request 'refs #2592 fix syntax' (#53) from fix into main
Reviewed-on: #53
2025-08-04 14:38:20 +02:00
Natalia Serrano 83a25947bd refs #2592 fix syntax 2025-08-04 14:37:56 +02:00
Natalia Serrano d180917d3b Merge pull request 'logout-ejecutarscript' (#52) from logout-ejecutarscript into main
Reviewed-on: #52
2025-08-04 14:25:03 +02:00
Natalia Serrano 8b8204d10f refs #2558 #2559 #2562 #2563 add versioning stuff 2025-08-04 14:24:22 +02:00
Natalia Serrano 25b2cb6cd8 refs #2558 #2559 return job_id to ogcore 2025-08-04 14:21:04 +02:00
Natalia Serrano fc8b072860 refs #2563 log the logging-out user 2025-08-04 12:26:19 +02:00
Natalia Serrano 860fb61677 refs #2562 keep track of the logging-out user 2025-08-04 12:25:56 +02:00
Natalia Serrano 33f65a45b7 Merge pull request 'jobid-wait-zombies' (#51) from jobid-wait-zombies into main
Reviewed-on: #51
2025-07-31 10:54:26 +02:00
Natalia Serrano 75d222c425 refs #2554 wait for zombies 2025-07-31 10:36:00 +02:00
Natalia Serrano e783d7c1fa refs #2554 wait for zombies 2025-07-31 10:34:01 +02:00
Natalia Serrano ee183f6ad3 refs #2556 change "jobid" for "job_id" 2025-07-31 10:18:19 +02:00
Natalia Serrano 054071a5ab Merge pull request 'refs #2547 add missing file stop-agent.ps1' (#50) from add-file into main
Reviewed-on: #50
2025-07-30 10:21:44 +02:00
Natalia Serrano 95360a244e refs #2547 add missing file stop-agent.ps1 2025-07-30 10:21:02 +02:00
Natalia Serrano cbfcc22d8a Merge pull request 'refs #2509 use setsid, send signals to the pgrp' (#49) from setsid into main
Reviewed-on: #49
2025-07-29 14:13:56 +02:00
Natalia Serrano 9fe1b5d1d5 refs #2509 use setsid, send signals to the pgrp 2025-07-29 14:12:56 +02:00
Natalia Serrano 565299c7c0 Merge pull request 'log-inoglive' (#48) from log-inoglive into main
Reviewed-on: #48
2025-07-28 15:18:25 +02:00
Natalia Serrano c53be3f3ec refs #2537 log whether we are in ogLive or not 2025-07-28 15:17:40 +02:00
Natalia Serrano 198353b214 refs #2537 log whether we are in ogLive or not 2025-07-28 13:56:56 +02:00
Natalia Serrano 4a8fc2b469 Merge pull request 'refs #2520 don't pass the tag parameter to CrearImagenGit' (#47) from crearimagengit-no-tag into main
Reviewed-on: #47
2025-07-24 15:33:19 +02:00
Natalia Serrano 61bd7a90dd refs #2520 don't pass the tag parameter to CrearImagenGit 2025-07-24 15:32:41 +02:00
Natalia Serrano 1a00b715e0 Merge pull request 'refs #2514 run the new extension-less scripts from the cloning engine' (#46) from extensionless-scripts into main
Reviewed-on: #46
2025-07-18 14:29:38 +02:00
Natalia Serrano 82abfbdd10 refs #2514 run the new extension-less scripts from the cloning engine 2025-07-18 14:28:34 +02:00
Natalia Serrano 99e2d3b7bc Merge pull request 'configurar-check-sizes' (#45) from configurar-check-sizes into main
Reviewed-on: #45
2025-07-18 14:24:24 +02:00
Natalia Serrano fb5cc452cf refs #2513 add versioning stuff 2025-07-18 14:22:58 +02:00
Natalia Serrano f833a31a82 refs #2513 bugfix: do not add empty entries 2025-07-18 14:18:32 +02:00
Natalia Serrano de74923fd8 refs #2513 poll quicker 2025-07-18 14:18:13 +02:00
Natalia Serrano 771ed4b378 refs #2513 support new parameter for Configurar 2025-07-18 14:17:26 +02:00
Natalia Serrano 663338920e Merge pull request 'add-getgitdata' (#44) from add-getgitdata into main
Reviewed-on: #44
2025-07-18 14:12:07 +02:00
Natalia Serrano e0da448e90 refs #2504 add endpoint for GetGitData 2025-07-18 14:10:43 +02:00
Natalia Serrano 41a9f7ce21 refs #2504 add endpoint for GetGitData 2025-07-18 14:08:53 +02:00
Natalia Serrano 950b163602 Merge pull request 'refs #2329 write output of launch_browser into a file' (#43) from Popen-stdout into main
Reviewed-on: #43
2025-06-26 12:47:48 +02:00
Natalia Serrano 72b832d654 refs #2329 write output of launch_browser into a file 2025-06-26 12:46:37 +02:00
Natalia Serrano 65c07fea1e Merge pull request 'Crear endpoint de ModificarImagenGit' (#42) from oggit-tls into main
Reviewed-on: #42
2025-06-25 11:54:51 +02:00
Natalia Serrano 9cf7ec5a56 refs #2312 fix changelogs 2025-06-25 11:54:20 +02:00
Vadim Trochinsky 60bab01556 refs #2247 Eliminar parametros sin usar 2025-06-25 11:44:50 +02:00
Vadim vtroshchinskiy b4c86bb175 refs #2312 fix changelog 2025-06-25 11:32:05 +02:00
Vadim Trochinsky 2f5ac81235 refs #2247 Add ModificarImagenGit endpoint 2025-06-25 11:32:05 +02:00
Natalia Serrano b1a67b1191 Merge pull request 'unificar-endpoints' (#41) from unificar-endpoints into main
Reviewed-on: #41
2025-06-23 12:39:52 +02:00
Natalia Serrano 7be441ca99 refs #2281 last commit broke linux--fix 2025-06-23 12:37:42 +02:00
Natalia Serrano bb856e5b63 refs #2281 fix logout on windows 2025-06-23 12:27:45 +02:00
Natalia Serrano 22f7ce0bb9 refs #2257 rename some endpoints 2025-06-23 12:27:03 +02:00
Natalia Serrano 250de7a070 refs #2285 improve failure conditions in EjecutarScript 2025-06-23 12:26:20 +02:00
Natalia Serrano 2feff97a91 refs #2278 change some labels in the windows installer 2025-06-23 09:58:12 +02:00
Natalia Serrano 10349a35f1 Merge pull request 'refs #2217 merge oggit' (#40) from oggit-debian-changelog into main
Reviewed-on: #40
2025-06-16 11:13:42 +02:00
Natalia Serrano a84bed3439 refs #2217 merge oggit 2025-06-16 11:13:22 +02:00
Natalia Serrano 05d2375e21 Merge pull request 'refs #2217 merge oggit' (#39) from oggit-changelog into main
Reviewed-on: #39
2025-06-16 11:11:09 +02:00
Natalia Serrano 8fea0737f2 refs #2217 merge oggit 2025-06-16 11:10:44 +02:00
Natalia Serrano 7128f12049 refs #2217 Merge branch 'oggit' 2025-06-16 09:55:22 +02:00
Natalia Serrano 734df2749b refs #2217 merge oggit 2025-06-16 09:53:46 +02:00
Natalia Serrano 946020d728 Merge pull request 'refs #2208 improve fail condition when no network is detected' (#38) from error-no-net into main
Reviewed-on: #38
2025-06-13 10:03:54 +02:00
Natalia Serrano 1a38999aef refs #2208 improve fail condition when no network is detected 2025-06-13 10:03:24 +02:00
Natalia Serrano 2dd678737b Merge pull request 'ogagentuser-sigterm' (#37) from ogagentuser-sigterm into main
Reviewed-on: #37
2025-06-12 15:39:24 +02:00
Natalia Serrano 165746a94b refs #2211 send logout upon client disconnect 2025-06-12 15:37:17 +02:00
Natalia Serrano 3553aee8ce refs #2179 check that windows-only variable is defined 2025-06-05 12:10:22 +02:00
Natalia Serrano b77b42ec22 refs #2177 correctly handle UNIX signals 2025-06-05 12:09:07 +02:00
Natalia Serrano e20c671c1e Merge pull request 'tls-again' (#36) from tls-again into main
Reviewed-on: #36
2025-05-28 10:53:07 +02:00
Natalia Serrano 15d4f2cf6b refs #2056 #2057 look for certs in a few places 2025-05-28 10:47:40 +02:00
Natalia Serrano e920a3c681 refs #2055 make TLS checks optional for the server 2025-05-21 17:36:13 +02:00
Natalia Serrano 921706e9f0 refs #2054 use a global to specify TLS verification or not 2025-05-21 17:31:33 +02:00
Natalia Serrano 63944cef0e refs #2054 send client cert to ogcore, optionally verify ogcore's cert 2025-05-21 17:30:11 +02:00
Natalia Serrano 3ae4471d5d refs #2055 use proper server cert, demand client cert 2025-05-21 17:21:47 +02:00
Natalia Serrano d1ce3b5cc9 Merge pull request 'refs #2060 launch QT6 browser, use dbus to change URLs' (#35) from browser-nuevo into main
Reviewed-on: #35
2025-05-21 16:05:40 +02:00
Natalia Serrano 1fa2a4f0bb refs #2060 launch QT6 browser, use dbus to change URLs 2025-05-21 16:02:46 +02:00
Natalia Serrano ac9ab7e8f5 refs #2036 downgrade to qt4 browser again 2025-05-19 11:05:43 +02:00
Natalia Serrano 05e0b83b91 Merge pull request 'no-tls' (#34) from no-tls into main
Reviewed-on: #34
2025-05-19 10:52:49 +02:00
Vadim vtroshchinskiy 17ec13d77c Soporte de oggit 2025-05-16 09:57:16 +02:00
20 changed files with 752 additions and 104 deletions

View File

@ -6,6 +6,133 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [7.3.2] - 2025-08-04
### Fixed
- Fix syntax
## [7.3.1] - 2025-08-04
### Fixed
- On user logout, write the user to the log
- On EjecutarScript with client=true, return a job ID
## [7.3.0] - 2025-07-31
### Fixed
- Wait for zombies
### Changed
- Change "jobid" for "job_id" for consistency
## [7.2.2] - 2025-07-30
### Added
- Add missing file stop-agent.ps1
## [7.2.1] - 2025-07-29
### Changed
- Run process in a new POSIX session and group, send termination signals to the whole process group
## [7.2.0] - 2025-07-28
### Added
- Log whether we are in ogLive or not
## [7.1.0] - 2025-07-24
### Changed
- Don't pass the "tag" parameter to CrearImagenGit
## [7.0.0] - 2025-07-18
### Changed
- Run the new extension-less scripts from the cloning engine
## [6.3.0] - 2025-07-18
### Added
- Add new parameter for Configurar
## [6.2.0] - 2025-07-18
### Added
- Add endpoint for GetGitData
## [6.1.1] - 2025-06-26
### Changed
- Write output of launch_browser into a file
## [6.1.0] - 2025-06-25
### Added
- Added ModificarImagenGit
## [6.0.0] - 2025-06-19
### Changed
- Changed the names of some endpoints for consistency between oglive and OS
- Changed label in the windows installer
## [5.9.0] - 2025-06-16
### Added
- Added changes for oggit
## [5.8.1] - 2025-06-13
### Fixed
- Improve fail condition when no network is detected
## [5.8.0] - 2025-06-12
### Changed
- Agents aren't being sent any signals on user logout. On the server side, assume that client disconnection == logout
## [5.7.1] - 2025-06-05
### Fixed
- Correcly handle UNIX signals in the user instance of the agent
## [5.7.0] - 2025-05-27
### Changed
- Use TLS again
## [5.6.0] - 2025-05-21
### Changed
- Launch QT6 browser
- Change URLs using dbus
## [5.5.0] - 2025-05-19
### Changed
- Revert to the QT4 browser again
## [5.4.0] - 2025-05-19
### Changed

View File

@ -1,3 +1,127 @@
ogagent (7.3.2-1) stable; urgency=medium
* Fix syntax
-- OpenGnsys developers <info@opengnsys.es> Mon, 04 Aug 2025 14:36:47 +0200
ogagent (7.3.1-1) stable; urgency=medium
* On user logout, write the user to the log
* On EjecutarScript with client=true, return a job ID
-- OpenGnsys developers <info@opengnsys.es> Mon, 04 Aug 2025 14:22:52 +0200
ogagent (7.3.0-1) stable; urgency=medium
* Wait for zombies
* Change "jobid" for "job_id" for consistency
-- OpenGnsys developers <info@opengnsys.es> Thu, 31 Jul 2025 10:35:16 +0200
ogagent (7.2.2-1) stable; urgency=medium
* Add missing file stop-agent.ps1
-- OpenGnsys developers <info@opengnsys.es> Wed, 30 Jul 2025 10:20:21 +0200
ogagent (7.2.1-1) stable; urgency=medium
* Run process in a new POSIX session and group, send termination signals to the whole process group
-- OpenGnsys developers <info@opengnsys.es> Tue, 29 Jul 2025 12:52:08 +0200
ogagent (7.2.0-1) stable; urgency=medium
* Log whether we are in ogLive or not
-- OpenGnsys developers <info@opengnsys.es> Mon, 28 Jul 2025 13:55:28 +0200
ogagent (7.1.0-1) stable; urgency=medium
* Don't pass the "tag" parameter to CrearImagenGit
-- OpenGnsys developers <info@opengnsys.es> Thu, 24 Jul 2025 15:31:59 +0200
ogagent (7.0.0-1) stable; urgency=medium
* Run the new extension-less scripts from the cloning engine
-- OpenGnsys developers <info@opengnsys.es> Fri, 18 Jul 2025 14:27:04 +0200
ogagent (6.3.0-1) stable; urgency=medium
* Add new parameter for Configurar
-- OpenGnsys developers <info@opengnsys.es> Fri, 18 Jul 2025 14:20:44 +0200
ogagent (6.2.0-1) stable; urgency=medium
* Add endpoint for GetGitData
-- OpenGnsys developers <info@opengnsys.es> Fri, 18 Jul 2025 14:10:06 +0200
ogagent (6.1.1-1) stable; urgency=medium
* Write output of launch_browser into a file
-- OpenGnsys developers <info@opengnsys.es> Thu, 26 Jun 2025 12:45:19 +0200
ogagent (6.1.0-1) stable; urgency=medium
* Add ModificarImagenGit
-- OpenGnsys developers <info@opengnsys.es> Wed, 25 Jun 2025 10:26:42 +0200
ogagent (6.0.0-1) stable; urgency=medium
* Unify API methods for poweroff, reboot and run script
* Change label in the windows installer
-- OpenGnsys developers <info@opengnsys.es> Fri, 20 Jun 2025 10:03:15 +0200
ogagent (5.9.0-1) stable; urgency=medium
* Add changes for oggit
-- OpenGnsys developers <info@opengnsys.es> Mon, 16 Jun 2025 11:12:55 +0200
ogagent (5.8.1-1) stable; urgency=medium
* Improve fail condition when no network is detected
-- OpenGnsys developers <info@opengnsys.es> Fri, 13 Jun 2025 10:01:43 +0200
ogagent (5.8.0-1) stable; urgency=medium
* When client disconnect, assume that the user logged out
-- OpenGnsys developers <info@opengnsys.es> Thu, 12 Jun 2025 15:30:50 +0200
ogagent (5.7.1-1) stable; urgency=medium
* Correctly handle UNIX signals
-- OpenGnsys developers <info@opengnsys.es> Thu, 05 Jun 2025 12:07:30 +0200
ogagent (5.7.0-1) stable; urgency=medium
* Use TLS again
-- OpenGnsys developers <info@opengnsys.es> Wed, 21 May 2025 17:39:13 +0200
ogagent (5.6.0-1) stable; urgency=medium
* Execute 'launch_browser' rather than 'browser'
* Change URLs using dbus
-- OpenGnsys developers <info@opengnsys.es> Wed, 21 May 2025 15:06:52 +0200
ogagent (5.5.0-1) stable; urgency=medium
* Return to the QT4 browser again
-- OpenGnsys developers <info@opengnsys.es> Mon, 19 May 2025 10:57:37 +0200
ogagent (5.4.0-1) stable; urgency=medium
* Disable TLS on request

View File

@ -35,6 +35,8 @@ import json
import sys
import time
import os
import socket
import signal
from PyQt6 import QtCore, QtGui, QtWidgets
from about_dialog_ui import Ui_OGAAboutDialog
@ -328,6 +330,29 @@ if __name__ == '__main__':
trayIcon.quit()
sys.exit(1)
## begin SIGTERM handling
signal_socket = socket.socketpair()
signal_socket[0].setblocking(False)
signal_socket[1].setblocking(False)
signal.set_wakeup_fd(signal_socket[0].fileno())
def signal_handler(signum, frame):
#print (f"Received signal {signum}")
pass
def qt_signal_handler():
data = signal_socket[1].recv(1)
#print(f"Signal ({data}) received via socket, shutting down gracefully...")
if trayIcon:
trayIcon.quit()
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)
notifier = QtCore.QSocketNotifier(signal_socket[1].fileno(), QtCore.QSocketNotifier.Type.Read)
notifier.activated.connect(qt_signal_handler)
## end SIGTERM handling
app.aboutToQuit.connect(trayIcon.cleanup)
trayIcon.show()

View File

@ -1 +1 @@
5.4.0
7.3.2

View File

@ -7,7 +7,7 @@ port=8000
#path=test_modules/server,more_modules/server
# Remote OpenGnsys Service
remote=https://192.168.2.1/opengnsys/rest
remote=https://192.168.2.1:8443/opengnsys/rest
# Alternate OpenGnsys Service (comment out to enable this option)
#altremote=https://10.0.2.2/opengnsys/rest
@ -20,9 +20,11 @@ log=DEBUG
imgname=
# TLS
ca=C:\Program Files (x86)\OGagent\ca.crt
crt=C:\Program Files (x86)\OGagent\ogagent.crt
key=C:\Program Files (x86)\OGagent\ogagent.key
# The agent will look for these files in /opt/opengnsys/etc, /usr/share/OGAgent,
# windows "Program Files (x86)" and the current working directory
ca=ca.crt
crt=ogagent.crt
key=ogagent.key
# Module specific

View File

@ -45,6 +45,7 @@ from .log import logger
from .utils import exceptionToMessage
TIMEOUT = 5 # Connection timout, in seconds
VERIFY_TLS=True
class RESTError(Exception):
@ -95,6 +96,7 @@ class REST(object):
@param url The url of the REST API Base. The trailing '/' can be included or omitted, as desired.
"""
self.endpoint = url
global VERIFY_TLS
if self.endpoint[-1] != '/':
self.endpoint += '/'
@ -109,21 +111,47 @@ class REST(object):
logger.debug ('TLS not available: python requests library is old')
self.use_tls = url.startswith ('https')
#if self.use_tls:
# if not ca_file or not crt_file or not key_file:
# raise Exception ('missing TLS parameters in REST constructor')
#
# errs = 0
# for f in [ca_file, crt_file, key_file]:
# if not os.path.exists (f):
# logger.error (f'{f}: No such file or directory')
# errs += 1
# if errs:
# raise Exception ('TLS files not found')
#
#self.ca_file = ca_file
#self.crt_file = crt_file
#self.key_file = key_file
if self.use_tls:
if not ca_file or not crt_file or not key_file:
raise Exception ('missing TLS parameters in REST constructor')
certs_dirs = ['/opt/opengnsys/etc', '/usr/share/OGAgent']
pf = os.environ.get ('PROGRAMFILES(X86)')
if pf: certs_dirs.append (os.path.join (pf, 'OGAgent'))
certs_dirs.append (os.getcwd())
certs_dir = None
for sp in certs_dirs:
if os.path.exists (sp):
logger.debug (f'Looking for TLS files in ({sp})')
certs_dir = sp
break
if not certs_dir:
logger.debug ("Don't know where to look for TLS files")
errs = 1
else:
errs = 0
for f in [ca_file, crt_file, key_file]:
if os.path.exists (f'{certs_dir}/{f}'):
logger.debug (f'{certs_dir}/{f}: found')
else:
logger.error (f'{f}: No such file or directory')
errs += 1
if errs:
self.verify_tls = False
logger.debug ('HTTP client: using insecure TLS to talk to ogcore due to missing files')
else:
self.ca_file = f'{certs_dir}/{ca_file}'
self.crt_file = f'{certs_dir}/{crt_file}'
self.key_file = f'{certs_dir}/{key_file}'
self.verify_tls = VERIFY_TLS
if self.verify_tls:
logger.debug ('HTTP client: using TLS to talk to ogcore')
else:
logger.debug ('HTTP client: using insecure TLS as requested to talk to ogcore')
else:
logger.debug ('HTTP client: not using TLS to talk to ogcore')
# Disable logging requests messages except for errors, ...
logging.getLogger("requests").setLevel(logging.CRITICAL)
@ -156,7 +184,10 @@ class REST(object):
# Old requests version does not support verify, but it do not checks ssl certificate by default
if self.newerRequestLib:
if self.use_tls:
r = requests.get(url, verify=False, timeout=TIMEOUT)
if self.verify_tls:
r = requests.get(url, cert=(self.crt_file, self.key_file), verify=self.ca_file, timeout=TIMEOUT)
else:
r = requests.get(url, verify=False, timeout=TIMEOUT)
else:
r = requests.get(url, timeout=TIMEOUT)
else:
@ -165,7 +196,10 @@ class REST(object):
logger.debug('Requesting using POST {}, data: {}'.format(url, data))
if self.newerRequestLib:
if self.use_tls:
r = requests.post(url, data=data, headers={'content-type': 'application/json'}, verify=False, timeout=TIMEOUT)
if self.verify_tls:
r = requests.post(url, data=data, headers={'content-type': 'application/json'}, cert=(self.crt_file, self.key_file), verify=self.ca_file, timeout=TIMEOUT)
else:
r = requests.post(url, data=data, headers={'content-type': 'application/json'}, verify=False, timeout=TIMEOUT)
else:
r = requests.post(url, data=data, headers={'content-type': 'application/json'}, timeout=TIMEOUT)
else:
@ -173,7 +207,7 @@ class REST(object):
r.raise_for_status()
ct = r.headers['Content-Type']
if 'application/json' != ct:
if len(ct) < 16 or 'application/json' != ct[0:16]:
raise Exception (f'response content-type is not "application/json" but "{ct}"')
r = json.loads(r.content) # Using instead of r.json() to make compatible with old requests lib versions
except requests.exceptions.RequestException as e:

View File

@ -43,6 +43,7 @@ from .utils import exceptionToMessage
from .certs import createSelfSignedCert
from .log import logger
VERIFY_TLS=True
class HTTPServerHandler(BaseHTTPRequestHandler):
service = None
@ -153,15 +154,46 @@ class HTTPThreadingServer(ThreadingMixIn, HTTPServer):
class HTTPServerThread(threading.Thread):
def __init__(self, address, service):
super(self.__class__, self).__init__()
global VERIFY_TLS
HTTPServerHandler.service = service # Keep tracking of service so we can intercact with it
self.certFile = createSelfSignedCert()
self.server = HTTPThreadingServer(address, HTTPServerHandler)
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(certfile=self.certFile)
pf = os.environ.get ('PROGRAMFILES(X86)')
if pf: pf = os.path.join (pf, 'OGAgent')
if os.path.exists ('/opt/opengnsys/etc/ogagent.crt') and os.path.exists ('/opt/opengnsys/etc/ogagent.key') and os.path.exists ('/opt/opengnsys/etc/ca.crt'):
logger.debug ('HTTP server: using certificate/CA from /opt/opengnsys/etc')
context.load_cert_chain (certfile='/opt/opengnsys/etc/ogagent.crt', keyfile='/opt/opengnsys/etc/ogagent.key')
context.load_verify_locations (cafile='/opt/opengnsys/etc/ca.crt')
elif pf and os.path.exists (os.path.join (pf, 'ogagent.crt')) and os.path.exists (os.path.join (pf, 'ogagent.key')) and os.path.exists (os.path.join (pf, 'ca.crt')):
logger.debug (f'HTTP server: using certificate/CA from the installation path ({pf})')
context.load_cert_chain (certfile=os.path.join (pf, 'ogagent.crt'), keyfile=os.path.join (pf, 'ogagent.key'))
context.load_verify_locations (cafile=os.path.join (pf, 'ca.crt'))
elif os.path.exists ('./ogagent.crt') and os.path.exists ('./ogagent.key') and os.path.exists ('./ca.crt'):
cwd = os.getcwd()
logger.debug (f'HTTP server: using certificate/CA from the current working directory ({cwd})')
context.load_cert_chain (certfile=f'{cwd}/ogagent.crt', keyfile=f'{cwd}/ogagent.key')
context.load_verify_locations (cafile=f'{cwd}/ca.crt')
else:
logger.debug ('HTTP server: using a self-signed certificate')
self.certFile = createSelfSignedCert()
context.load_cert_chain (certfile=self.certFile)
VERIFY_TLS = False
if VERIFY_TLS:
context.verify_mode = ssl.CERT_REQUIRED
context.verify_flags &= ssl.VERIFY_X509_STRICT
else:
context.verify_mode = ssl.CERT_NONE
context.verify_flags &= ~ssl.VERIFY_X509_STRICT
s = context.cert_store_stats()
if 'x509_ca' in s: logger.debug (f'HTTP server: {s['x509_ca']} CAs loaded')
if 'x509' in s: logger.debug (f'HTTP server: {s['x509']} certs loaded')
self.server.socket = context.wrap_socket(self.server.socket, server_side=True)
logger.debug('Initialized HTTPS Server thread on {}'.format(address))
def getServerUrl(self):

View File

@ -30,6 +30,7 @@
"""
import os
import json
import queue
import socket
@ -109,6 +110,11 @@ class ClientProcessor(threading.Thread):
logger.debug('Got Client message {}={}'.format(msg, REV_DICT.get(msg)))
if self.parent.clientMessageProcessor is not None:
self.parent.clientMessageProcessor(msg, data)
if msg == REQ_LOGIN:
if b',' in data:
self.user = data.split (b',')[0]
else:
self.user = data
def run(self):
self.running = True
@ -164,6 +170,9 @@ class ClientProcessor(threading.Thread):
logger.debug('Got invalid message from request: {}, state: {}'.format(buf, state))
except socket.error as e:
# If no data is present, no problem at all, pass to check messages
if '[WinError 10054]' in str(e):
## windows: client disconnected
self.running = False
pass
except Exception as e:
tb = traceback.format_exc()
@ -193,6 +202,14 @@ class ClientProcessor(threading.Thread):
logger.error('Invalid message in queue: {}'.format(e))
logger.debug('Client processor stopped')
if os.path.exists ('/windows/temp'):
fd = open ('/windows/temp/ogagentuser_died', 'wb')
fd.write (self.user)
fd.close()
else:
fd = open ('/tmp/ogagentuser_died', 'wb')
fd.write (self.user)
fd.close()
try:
self.clientSocket.close()
except Exception:

View File

@ -23,32 +23,32 @@ class JobMgr():
logger.debug ('args "{}"'.format (args))
now = datetime.now (tz=timezone.utc)
ts = now.strftime ('%Y-%m-%d %H:%M:%S.%f%z') ## '%s' doesn't work on windows
jobid = hashlib.sha256 (now.isoformat().encode('UTF-8') + script.encode ('UTF-8')).hexdigest()[0:12]
job_id = hashlib.sha256 (now.isoformat().encode('UTF-8') + script.encode ('UTF-8')).hexdigest()[0:12]
p = subprocess.Popen (args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
self.jobs[jobid] = { 'p': p, 'pid': p.pid, 'starttime': ts, 'script': script, 'client': is_client, 'status': 'running', 'stdout': '', 'stderr': '' }
self.jobs[jobid]['t1'] = threading.Thread (target=job_readstdout, args=(self.jobs[jobid],))
self.jobs[jobid]['t2'] = threading.Thread (target=job_readstderr, args=(self.jobs[jobid],))
self.jobs[jobid]['t1'].start()
self.jobs[jobid]['t2'].start()
self.jobs[job_id] = { 'p': p, 'pid': p.pid, 'starttime': ts, 'script': script, 'client': is_client, 'status': 'running', 'stdout': '', 'stderr': '' }
self.jobs[job_id]['t1'] = threading.Thread (target=job_readstdout, args=(self.jobs[job_id],))
self.jobs[job_id]['t2'] = threading.Thread (target=job_readstderr, args=(self.jobs[job_id],))
self.jobs[job_id]['t1'].start()
self.jobs[job_id]['t2'].start()
logger.debug ('jobs "{}"'.format (self.jobs))
return jobid
return job_id
def prepare_jobs(self):
## can't return self.jobs because the Popen object at self.jobs[id]['p'] is not serializable. So, need to create a new dict to return
st = []
for jobid in self.jobs:
j = self.jobs[jobid]
for job_id in self.jobs:
j = self.jobs[job_id]
entry = dict ((k, j[k]) for k in ['pid', 'starttime', 'script', 'client', 'status', 'stdout', 'stderr'])
entry['jobid'] = jobid
entry['job_id'] = job_id
if j['p'].poll() is not None: ## process finished
entry['rc'] = j['p'].returncode
entry['status'] = 'finished'
st.append (entry)
return st
def terminate_job(self, jobid):
if jobid not in self.jobs: return {}
p = self.jobs[jobid]['p']
def terminate_job(self, job_id):
if job_id not in self.jobs: return {}
p = self.jobs[job_id]['p']
p.terminate()
time.sleep (1)
if p.poll() is not None:

View File

@ -39,6 +39,7 @@ from opengnsys.log import logger
from opengnsys.linux.daemon import Daemon
import os
import sys
import signal
import json
@ -71,6 +72,15 @@ class OGAgentSvc(Daemon, CommonService):
# example
try:
while self.isAlive:
client_died=False
if os.path.exists ('/tmp/ogagentuser_died'):
with open ('/tmp/ogagentuser_died', 'rb') as fd:
u = fd.read()
os.unlink ('/tmp/ogagentuser_died')
client_died=True
if client_died:
self.notifyLogout (u)
# In milliseconds, will break
self.doWait(1000)
except (KeyboardInterrupt, SystemExit) as e:

View File

@ -42,6 +42,8 @@ OTHER, DEBUG, INFO, WARN, ERROR, FATAL = (10000 * (x + 1) for x in range(6))
class LocalLogger(object):
def __init__(self):
self.extra = { 'in_oglive': None }
# tempdir is different for "user application" and "service"
# service wil get c:\windows\temp, while user will get c:\users\XXX\temp
# Try to open logger at /var/log path
@ -51,13 +53,13 @@ class LocalLogger(object):
for logDir in ('/var/log', os.path.expanduser('~'), tempfile.gettempdir()):
try:
fname1 = os.path.join (logDir, 'opengnsys.log')
fmt1 = logging.Formatter (fmt='%(levelname)s %(asctime)s (%(threadName)s) (%(funcName)s) %(message)s')
fmt1 = logging.Formatter (fmt='%(levelname)s %(asctime)s in_oglive=%(in_oglive)s (%(threadName)s) (%(funcName)s) %(message)s')
fh1 = logging.FileHandler (filename=fname1, mode='a')
fh1.setFormatter (fmt1)
fh1.setLevel (logging.DEBUG)
fname2 = os.path.join (logDir, 'opengnsys.json.log')
fmt2 = JsonFormatter ({"timestamp": "asctime", "severity": "levelname", "threadName": "threadName", "function": "funcName", "message": "message"}, time_format='%Y-%m-%d %H:%M:%S', msec_format='')
fmt2 = JsonFormatter ({"timestamp": "asctime", "severity": "levelname", "in_oglive": "in_oglive", "threadName": "threadName", "function": "funcName", "message": "message"}, time_format='%Y-%m-%d %H:%M:%S', msec_format='')
fh2 = logging.FileHandler (filename=fname2, mode='a')
fh2.setFormatter (fmt2)
fh2.setLevel (logging.DEBUG)
@ -77,11 +79,14 @@ class LocalLogger(object):
self.logger = None
def log(self, level, message):
if self.extra['in_oglive'] is None:
self.extra['in_oglive'] = os.path.exists ('/scripts/functions')
# Debug messages are logged to a file
# our loglevels are 10000 (other), 20000 (debug), ....
# logging levels are 10 (debug), 20 (info)
# OTHER = logging.NOTSET
self.logger.log(int(level / 1000) - 10, message, stacklevel=4)
self.logger.log(int(level / 1000) - 10, message, stacklevel=4, extra=self.extra)
def isWindows(self):
return False

View File

@ -50,13 +50,13 @@ class OpenGnSysWorker(ClientWorker):
def process_script(self, json_params):
script = json_params['code']
logger.debug('Processing message: script({})'.format(script))
self.jobmgr.launch_job (script, True)
#self.sendServerMessage('script', {'op', 'launched'})
job_id = self.jobmgr.launch_job (script, True)
self.sendServerMessage('script_launched', {'op': 'launched', 'job_id': job_id})
def process_terminatescript(self, json_params):
jobid = json_params['jobid']
logger.debug('Processing terminatescript request, jobid "{}"'.format (jobid))
self.jobmgr.terminate_job (jobid)
job_id = json_params['job_id']
logger.debug('Processing terminatescript request, job_id "{}"'.format (job_id))
self.jobmgr.terminate_job (job_id)
def process_preparescripts(self, json_params):
logger.debug('Processing preparescripts request')

View File

@ -112,7 +112,7 @@ class OpenGnSysWorker(ServerWorker):
"iph": self.interface.ip,
"timestamp": int (time.time()),
}
logger.debug (f'about to send ping ({body})')
#logger.debug (f'about to send ping ({body})')
self.REST.sendMessage ('clients/status/webhook', body)
def onActivation(self):
@ -147,7 +147,11 @@ class OpenGnSysWorker(ServerWorker):
for t in range(0, 300):
try:
# Get the first network interface
self.interface = list(operations.getNetworkInfo())[0]
nets = list (operations.getNetworkInfo())
if 0 == len (nets):
logger.error ('No network interfaces found')
raise Exception ('No network interfaces found')
self.interface = nets[0]
except Exception as e:
# Wait 1 sec. and retry
logger.warn (e)
@ -304,7 +308,7 @@ class OpenGnSysWorker(ServerWorker):
@execution_level('halt')
@check_secret
def process_reboot(self, path, get_params, post_params, server):
def process_Reiniciar(self, path, get_params, post_params, server):
"""
Launches a system reboot operation
:param path:
@ -323,7 +327,7 @@ class OpenGnSysWorker(ServerWorker):
@execution_level('halt')
@check_secret
def process_poweroff(self, path, get_params, post_params, server):
def process_Apagar(self, path, get_params, post_params, server):
"""
Launches a system power off operation
:param path:
@ -343,7 +347,7 @@ class OpenGnSysWorker(ServerWorker):
@execution_level('full')
@check_secret
def process_script(self, path, get_params, post_params, server):
def process_EjecutarScript(self, path, get_params, post_params, server):
"""
Processes an script execution (script should be encoded in base64)
:param path:
@ -354,28 +358,57 @@ class OpenGnSysWorker(ServerWorker):
"""
logger.debug('Processing script request')
# Decoding script
script = urllib.parse.unquote(base64.b64decode(post_params.get('script')).decode('utf-8'))
param_script = post_params.get('script')
if not param_script:
return {'op': 'error', 'err': 'Required parameter "script" is missing or empty'}
try:
b64decoded = base64.b64decode (param_script)
except Exception as e:
return {'op': 'error', 'err': f'Failed to decode base64: {e}'}
script = urllib.parse.unquote (b64decoded.decode ('utf-8'))
logger.debug('received script "{}"'.format(script))
if post_params.get('client', 'false') == 'false':
jobid = self.jobmgr.launch_job (script, False)
return {'op': 'launched', 'jobid': jobid}
job_id = self.jobmgr.launch_job (script, False)
return {'op': 'launched', 'job_id': job_id}
else: ## post_params.get('client') is not 'false'
## send script as-is
self.sendClientMessage('script', {'code': script})
#return {'op': 'launched', 'jobid': jobid} ## TODO obtain jobid generated at the client (can it be done?)
return {'op': 'launched'}
## wait for job_id generated at the client
job_id = None
iters = 0
while True:
time.sleep (0.2)
if os.path.exists ('/tmp/EjecutarScript-jobid'):
with open ('/tmp/EjecutarScript-jobid', 'r') as fd:
job_id = fd.read()
break
iters += 1
if iters >= 10: break
try: os.unlink ('/tmp/EjecutarScript-jobid')
except: pass
if job_id is None: return {'op': 'launched'}
else: return {'op': 'launched', 'job_id': job_id}
def process_client_script_launched(self, data):
fd = open ('/tmp/EjecutarScript-jobid', 'w')
fd.write (data['job_id'])
fd.close()
return True
@execution_level('full')
@check_secret
def process_terminatescript(self, path, get_params, post_params, server):
jobid = post_params.get('jobid', None)
logger.debug('Processing terminate_script request, jobid "{}"'.format (jobid))
if jobid is None:
job_id = post_params.get('job_id', None)
logger.debug('Processing terminate_script request, job_id "{}"'.format (job_id))
if job_id is None:
return {}
self.sendClientMessage('terminatescript', {'jobid': jobid})
self.jobmgr.terminate_job (jobid)
self.sendClientMessage('terminatescript', {'job_id': job_id})
self.jobmgr.terminate_job (job_id)
return {}
@execution_level('full')

View File

@ -360,6 +360,105 @@ class ogAdmClientWorker (ogLiveWorker):
}
return self.respuestaEjecucionComando (cmd, herror, ids)
def do_CrearImagenGit (self, post_params):
for k in ['dsk', 'par', 'nci', 'ipr', 'nfn', 'ids']:
if k not in post_params:
logger.error (f'required parameter ({k}) not in POST params')
return {}
dsk = post_params['dsk'] ## Disco
par = post_params['par'] ## Número de partición
nci = post_params['nci'] ## Nombre canónico de la imagen
ipr = post_params['ipr'] ## Ip del repositorio
nfn = post_params['nfn']
ids = post_params['ids']
self.muestraMensaje (7)
try:
res = self.InventariandoSoftware (dsk, par, 'InventarioSoftware') ## Crea inventario Software previamente
except:
logger.warning ('Error al ejecutar el comando')
return {}
if res['contents']:
self.muestraMensaje (2)
inv_sft = res['contents']
try:
self.interfaceAdmin (nfn, [dsk, par, nci, ipr])
self.muestraMensaje (9)
herror = 0
except:
logger.warning ('Error al ejecutar el comando')
self.muestraMensaje (10)
herror = 1
else:
logger.warning ('Error al ejecutar el comando')
herror = 1
inv_sft = ''
self.muestraMenu()
cmd = {
'nfn': 'RESPUESTA_CrearImagenGit',
'dsk': dsk, ## Número de disco
'par': par, ## Número de partición de donde se creó
'ipr': ipr, ## Ip del repositorio donde se alojó
'inv_sft': inv_sft
}
return self.respuestaEjecucionComando (cmd, herror, ids)
def do_ModificarImagenGit (self, post_params):
for k in ['dsk', 'par', 'nci', 'ipr', 'nfn', 'ids', 'msg']:
if k not in post_params:
logger.error (f'required parameter ({k}) not in POST params')
return {}
dsk = post_params['dsk'] ## Disco
par = post_params['par'] ## Número de partición
nci = post_params['nci'] ## Nombre canónico de la imagen
ipr = post_params['ipr'] ## Ip del repositorio
nfn = post_params['nfn']
ids = post_params['ids']
msg = post_params['msg'] ## Mensaje de commit
self.muestraMensaje (7)
try:
res = self.InventariandoSoftware (dsk, par, 'InventarioSoftware') ## Crea inventario Software previamente
except:
logger.warning ('Error al ejecutar el comando')
return {}
if res['contents']:
self.muestraMensaje (2)
inv_sft = res['contents']
try:
self.interfaceAdmin (nfn, [dsk, par, nci, msg])
self.muestraMensaje (9)
herror = 0
except:
logger.warning ('Error al ejecutar el comando')
self.muestraMensaje (10)
herror = 1
else:
logger.warning ('Error al ejecutar el comando')
herror = 1
inv_sft = ''
self.muestraMenu()
cmd = {
'nfn': 'RESPUESTA_ModificarImagenGit',
'dsk': dsk, ## Número de disco
'par': par, ## Número de partición de donde se creó
'ipr': ipr, ## Ip del repositorio donde se alojó
'inv_sft': inv_sft
}
return self.respuestaEjecucionComando (cmd, herror, ids)
def do_RestaurarImagen (self, post_params):
for k in ['dsk', 'par', 'idi', 'ipr', 'nci', 'ifs', 'ptc', 'nfn', 'ids']:
if k not in post_params:
@ -406,6 +505,50 @@ class ogAdmClientWorker (ogLiveWorker):
}
return self.respuestaEjecucionComando (cmd, herror, ids)
def do_RestaurarImagenGit (self, post_params):
for k in ['dsk', 'par', 'ipr', 'nci', 'ptc', 'nfn', 'ids', 'ref']:
if k not in post_params:
logger.error (f'required parameter ({k}) not in POST params')
return {}
dsk = post_params['dsk']
par = post_params['par']
ipr = post_params['ipr']
nci = post_params['nci']
ptc = post_params['ptc'] ## Protocolo de clonación: Unicast, Multicast, Torrent
nfn = post_params['nfn']
ids = post_params['ids']
ref = post_params['ref'] ## Referencia de git a restaurar
self.muestraMensaje (3)
try:
## the ptc.split() is useless right now, since interfaceAdmin() does ' '.join(params) in order to spawn a shell
## however we're going to need it in the future (when everything gets translated into python), plus it's harmless now. So let's do it
#self.interfaceAdmin (nfn, [dsk, par, nci, ipr, ptc])
self.interfaceAdmin (nfn, [dsk, par, nci, ipr, ref] + ptc.split())
self.muestraMensaje (11)
herror = 0
except:
logger.warning ('Error al ejecutar el comando')
self.muestraMensaje (12)
herror = 1
cfg = self.LeeConfiguracion()
if not cfg:
logger.warning ('No se ha podido recuperar la configuración de las particiones del disco')
self.muestraMenu()
cmd = {
'nfn': 'RESPUESTA_RestaurarImagenGit',
'dsk': dsk, ## Número de disco
'par': par, ## Número de partición
'cfg': self.cfg2obj(cfg), ## Configuración de discos
}
return self.respuestaEjecucionComando (cmd, herror, ids)
def do_Configurar (self, post_params):
for k in ['nfn', 'dsk', 'cfg', 'ids']:
if k not in post_params:
@ -416,8 +559,9 @@ class ogAdmClientWorker (ogLiveWorker):
dsk = post_params['dsk']
cfg = post_params['cfg']
ids = post_params['ids']
check_sizes = str ('check-sizes' in post_params and 'true' == post_params['check-sizes']).lower()
self.muestraMensaje (4)
if 'true' != check_sizes: self.muestraMensaje (4)
params = []
disk_info = cfg.pop (0)
@ -438,23 +582,24 @@ class ogAdmClientWorker (ogLiveWorker):
cfg_str = f'{disk_info_str}!{part_info_str}%'
try:
self.interfaceAdmin (nfn, ['ignored', cfg_str])
self.muestraMensaje (14)
self.interfaceAdmin (nfn, ['ignored', cfg_str, check_sizes])
if 'true' != check_sizes: self.muestraMensaje (14)
herror = 0
except:
logger.warning ('Error al ejecutar el comando')
self.muestraMensaje (13)
if 'true' != check_sizes: self.muestraMensaje (13)
herror = 1
cfg = self.LeeConfiguracion()
if not cfg:
logger.warning ('No se ha podido recuperar la configuración de las particiones del disco')
return {}
cmd = {
'nfn': 'RESPUESTA_Configurar',
'cfg': self.cfg2obj (cfg),
}
if 'true' != check_sizes:
cfg = self.LeeConfiguracion()
if not cfg:
logger.warning ('No se ha podido recuperar la configuración de las particiones del disco')
return {}
cmd['cfg'] = self.cfg2obj (cfg)
self.muestraMenu()
return self.respuestaEjecucionComando (cmd, herror, ids)
@ -816,6 +961,21 @@ class ogAdmClientWorker (ogLiveWorker):
logger.debug ('type(post_params) "{}"'.format (type (post_params)))
return self._long_running_job ('CrearImagen', self.do_CrearImagen, args=(post_params,))
def process_CrearImagenGit (self, path, get_params, post_params, server):
logger.debug ('in process_CrearImagenGit, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
logger.debug ('type(post_params) "{}"'.format (type (post_params)))
return self._long_running_job ('CrearImagenGit', self.do_CrearImagenGit, args=(post_params,))
def process_ModificarImagenGit (self, path, get_params, post_params, server):
logger.debug ('in process_ModificarImagenGit, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
logger.debug ('type(post_params) "{}"'.format (type (post_params)))
return self._long_running_job ('ModificarImagenGit', self.do_ModificarImagenGit, args=(post_params,))
def process_RestaurarImagenGit (self, path, get_params, post_params, server):
logger.debug ('in process_RestaurarImagenGit, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
logger.debug ('type(post_params) "{}"'.format (type (post_params)))
return self._long_running_job ('RestaurarImagenGit', self.do_RestaurarImagenGit, args=(post_params,))
#def process_CrearImagenBasica (self, path, get_params, post_params, server):
# logger.debug ('in process_CrearImagenBasica, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
# logger.warning ('this method has been removed')
@ -849,6 +1009,11 @@ class ogAdmClientWorker (ogLiveWorker):
@check_secret
def process_Configurar (self, path, get_params, post_params, server):
logger.debug ('in process_Configurar, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
check_sizes = str ('check-sizes' in post_params and 'true' == post_params['check-sizes']).lower()
if 'true' == check_sizes:
return self.do_Configurar (post_params)
return self._long_running_job ('Configurar', self.do_Configurar, args=(post_params,))
@execution_level('full')
@ -873,3 +1038,38 @@ class ogAdmClientWorker (ogLiveWorker):
r.update ({ 'nfn':'RESPUESTA_KillJob', 'job':jid })
logger.debug (f'r aft ({r})')
return r
@execution_level('full')
@check_secret
def process_GetGitData (self, path, get_params, post_params, server):
logger.debug ('in process_GetGitData, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
for k in ['nfn', 'dsk', 'par', 'nfn']:
if k not in post_params:
logger.error (f'required parameter ({k}) not in POST params')
return {}
tmp_gitdata = f'/tmp/gitdata-{self.IPlocal}'
nfn = post_params['nfn']
dsk = post_params['dsk']
par = post_params['par']
try:
self.interfaceAdmin (nfn, [dsk, par, tmp_gitdata])
herror = 0
except:
herror = 1
if not os.path.exists (tmp_gitdata):
return self.respuestaEjecucionComando ({'nfn':'RESPUESTA_GetGitData'}, 1)
with open (tmp_gitdata, 'r') as fd:
gitdata = fd.read().strip()
branch, repo = gitdata.split (':')
cmd = {
'nfn': 'RESPUESTA_GetGitData',
'branch': branch,
'repo': repo,
}
return self.respuestaEjecucionComando (cmd, herror)

View File

@ -30,6 +30,7 @@
@author: Adolfo Gómez, dkmaster at dkmon dot com
"""
import os
import json
import socket
import time

View File

@ -106,6 +106,15 @@ class OGAgentSvc(win32serviceutil.ServiceFramework, CommonService):
# *********************
try:
while self.isAlive:
client_died=False
if os.path.exists ('/windows/temp/ogagentuser_died'):
with open ('/windows/temp/ogagentuser_died', 'rb') as fd:
u = fd.read()
os.unlink ('/windows/temp/ogagentuser_died')
client_died=True
if client_died:
self.notifyLogout (u)
# Pumps & processes any waiting messages
pythoncom.PumpWaitingMessages()
win32event.WaitForSingleObject(self.hWaitStop, 1000)

View File

@ -44,17 +44,19 @@ OTHER, DEBUG, INFO, WARN, ERROR, FATAL = (10000 * (x + 1) for x in range(6))
class LocalLogger(object):
def __init__(self):
self.extra = { 'in_oglive': False }
# tempdir is different for "user application" and "service"
# service wil get c:\windows\temp, while user will get c:\users\XXX\appdata\local\temp
fname1 = os.path.join (tempfile.gettempdir(), 'opengnsys.log')
fmt1 = logging.Formatter (fmt='%(levelname)s %(asctime)s (%(threadName)s) (%(funcName)s) %(message)s')
fmt1 = logging.Formatter (fmt='%(levelname)s %(asctime)s in_oglive=%(in_oglive)s (%(threadName)s) (%(funcName)s) %(message)s')
fh1 = logging.FileHandler (filename=fname1, mode='a')
fh1.setFormatter (fmt1)
fh1.setLevel (logging.DEBUG)
fname2 = os.path.join (tempfile.gettempdir(), 'opengnsys.json.log')
fmt2 = JsonFormatter ({"timestamp": "asctime", "severity": "levelname", "threadName": "threadName", "function": "funcName", "message": "message"}, time_format='%Y-%m-%d %H:%M:%S', msec_format='')
fmt2 = JsonFormatter ({"timestamp": "asctime", "severity": "levelname", "in_oglive": "in_oglive", "threadName": "threadName", "function": "funcName", "message": "message"}, time_format='%Y-%m-%d %H:%M:%S', msec_format='')
fh2 = logging.FileHandler (filename=fname2, mode='a')
fh2.setFormatter (fmt2)
fh2.setLevel (logging.DEBUG)
@ -71,7 +73,7 @@ class LocalLogger(object):
# our loglevels are 10000 (other), 20000 (debug), ....
# logging levels are 10 (debug), 20 (info)
# OTHER = logging.NOTSET
self.logger.log(int(level / 1000 - 10), message, stacklevel=4)
self.logger.log(int(level / 1000 - 10), message, stacklevel=4, extra=self.extra)
if level < INFO or self.serviceLogger is False: # Only information and above will be on event log
return

View File

@ -33,6 +33,9 @@
import os
import re
import time
try: import dbus ## don't fail on windows (the worker will later refuse to load anyway)
except: pass
import select
import random
import subprocess
import threading
@ -156,10 +159,10 @@ class ogLiveWorker(ServerWorker):
if 'thread' not in self.thread_list[job_id]: return { 'res': 2, 'der': 'Job is not running' }
t = self.thread_list[job_id]['thread']
pid = self.thread_list[job_id]['child_pid']
logger.debug (f'pid ({pid})')
logger.debug (f'pid/pgid/sid ({pid})')
try_times = 8
sig = signal.SIGTERM
msg = f'could not kill pid ({pid}) after ({try_times}) tries'
msg = f'could not killpg pid ({pid}) after ({try_times}) tries'
success = 2 ## mimic cmd['res'] in respuestaEjecucionComando(): "1" means success, "2" means failed
while True:
t.join (0.05)
@ -173,10 +176,10 @@ class ogLiveWorker(ServerWorker):
## this is fine in the first iteration of the loop, before we send any signals. In the rest of iterations, after some signals were sent, msg should be 'job terminated' instead.
if pid:
if os.path.exists (f'/proc/{pid}'):
logger.debug (f'sending signal ({sig}) to pid ({pid})')
logger.debug (f'sending signal ({sig}) to process group ({pid})')
## if the process finishes just here, nothing happens: the signal is sent to the void
os.kill (pid, sig)
#subprocess.run (['kill', '--signal', str(sig), str(pid)])
os.killpg (pid, sig)
#subprocess.run (['kill', '--signal', str(sig), f'-{pid}']) ## negative PID is used for sending signals to the process group
else:
msg = f'pid ({pid}) is gone, nothing to kill'
success = 1
@ -257,12 +260,12 @@ class ogLiveWorker(ServerWorker):
self.REST.sendMessage ('clients/status/webhook', body)
def interfaceAdmin (self, method, parametros=[]):
if method in ['Apagar', 'CambiarAcceso', 'Configurar', 'CrearImagen', 'EjecutarScript', 'getConfiguration', 'getIpAddress', 'IniciarSesion', 'InventarioHardware', 'InventarioSoftware', 'Reiniciar', 'RestaurarImagen']:
if method not in ['ConsolaRemota', 'procesaCache']:
## python
logger.debug (f'({method}) is a python method')
exe = '{}/{}.py'.format (self.pathinterface, method)
exe = '{}/{}'.format (self.pathinterface, method)
proc = [exe]+parametros
else: ## ConsolaRemota procesaCache
else:
## bash
logger.debug (f'({method}) is a bash method')
exe = '{}/{}'.format (self.pathinterface, method)
@ -281,24 +284,35 @@ class ogLiveWorker(ServerWorker):
proc = ['bash', '-c', '{} {}'.format (devel_bash_prefix, exe)]
logger.debug ('subprocess.run ("{}")'.format (' '.join (proc)))
p = subprocess.Popen (proc, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p = subprocess.Popen (proc, stdout=subprocess.PIPE, stderr=subprocess.PIPE, start_new_session=True)
if self.pid_q:
self.pid_q.put (p.pid)
self.pid_q.put (p.pid) ## p.pid is also a session ID and a process group ID--we'll use it later to send signals to the whole group
else:
## esto sucede por ejemplo cuando arranca el agente, que estamos en interfaceAdmin() en el mismo hilo, sin _long_running_job ni hilo separado
#logger.debug ('no queue--not writing any PID to it')
pass
sout = serr = ''
while p.poll() is None:
for l in iter (p.stdout.readline, b''):
finished = False
while True:
try:
p.wait (0.05)
finished = True
except subprocess.TimeoutExpired:
pass
ready_to_read, _, _ = select.select ([p.stdout, p.stderr], [], [], 0.2)
if p.stdout in ready_to_read:
l = p.stdout.readline()
partial = l.decode ('utf-8', 'ignore')
if self.stdout_q: self.stdout_q.put (partial)
sout += partial
for l in iter (p.stderr.readline, b''):
if p.stderr in ready_to_read:
l = p.stderr.readline()
partial = l.decode ('utf-8', 'ignore')
serr += partial
time.sleep (1)
if finished: break
sout = sout.strip()
serr = serr.strip()
@ -369,17 +383,27 @@ class ogLiveWorker(ServerWorker):
def cargaPaginaWeb (self, url=None):
if (not url): url = self.urlMenu
os.system ('pkill -f -9 browser')
os.system ('pkill -f -9 OGBrowser')
os.system ('pkill -f -9 QtWebEngineProcess')
p = subprocess.Popen (['/usr/bin/launch_browser', url])
dbus_address = os.environ.get ('DBUS_SESSION_BUS_ADDRESS')
if not dbus_address: logger.warning ('env var DBUS_SESSION_BUS_ADDRESS not set, cargaPaginaWeb() will likely not work')
b = dbus.SystemBus()
dest = 'es.opengnsys.OGBrowser.browser'
path = '/'
interface = None
method = 'setURL'
signature = 's'
try:
p.wait (2) ## if the process dies before 2 seconds...
logger.error ('Error al ejecutar OGBrowser, return code "{}"'.format (p.returncode))
return False
except subprocess.TimeoutExpired:
pass
b.call_blocking (dest, path, interface, method, 's', [url])
except Exception as e:
if 'ServiceUnknown' in str(e):
logger.warning ('browser is not running, launching a new one')
browser_log_fd = open ('/var/log/launch_browser.log', 'a')
subprocess.Popen (['/usr/bin/launch_browser', url], stdout=browser_log_fd, stderr=subprocess.STDOUT)
browser_log_fd.close()
else:
logger.error (f'Error al cambiar URL: ({e})')
return False
return True
@ -413,7 +437,7 @@ class ogLiveWorker(ServerWorker):
k, v = item.split ('=', maxsplit=1)
elem[k] = v
obj.append (elem)
if elem: obj.append (elem)
return obj

View File

@ -0,0 +1 @@
get-process -name ogagentuser|stop-process

View File

@ -217,7 +217,7 @@ FunctionEnd
Function GetParameters
${GetOptions} $CMDLINE "/server" $SERVERIP_VALUE
${If} $SERVERIP_VALUE == ""
StrCpy $SERVERIP_VALUE "192.168.2.10"
StrCpy $SERVERIP_VALUE "192.168.2.1:8443"
${EndIf}
FunctionEnd
@ -226,10 +226,12 @@ LangString PARAMS_TITLE ${LANG_ENGLISH} "Setup parameters"
LangString PARAMS_TITLE ${LANG_SPANISH} "Parametros de configuracion"
LangString PARAMS_TITLE ${LANG_FRENCH} "Parametres de configuration"
LangString PARAMS_TITLE ${LANG_GERMAN} "Setup-Parameter"
LangString SERVER_LABEL ${LANG_ENGLISH} "OpenGnsys Server IP Address"
LangString SERVER_LABEL ${LANG_SPANISH} "Direccion IP del Servidor OpenGnsys"
LangString SERVER_LABEL ${LANG_FRENCH} "Adresse IP du Serveur OpenGnsys"
LangString SERVER_LABEL ${LANG_GERMAN} "OpenGnsys-Server-IP-Adresse"
LangString SERVER_LABEL ${LANG_ENGLISH} "OpenGnsys Server IP Address and port (eg. 192.168.98.99:8443)"
LangString SERVER_LABEL ${LANG_SPANISH} "Direccion IP y puerto del Servidor OpenGnsys (p. ej. 192.168.98.99:8443)"
LangString SERVER_LABEL ${LANG_FRENCH} "Adresse IP et port du Serveur OpenGnsys (ex. 192.168.98.99:8443)"
LangString SERVER_LABEL ${LANG_GERMAN} "OpenGnsys Server IP-Adresse und Port (z. B. 192.168.98.99:8443)"
LangString ^UninstallLink ${LANG_ENGLISH} "Uninstall $(^Name)"
LangString ^UninstallLink ${LANG_SPANISH} "Desinstalar $(^Name)"
LangString ^UninstallLink ${LANG_FRENCH} "D<>sinstaller $(^Name)"