Advanced a bit more, now actor works but need the "user space script" to

communicate login/logouts
This commit is contained in:
Adolfo Gómez García 2014-11-18 20:56:45 +01:00
parent dca366b334
commit 91a40dcc76
48 changed files with 23 additions and 3674 deletions

View File

@ -4,6 +4,7 @@
SOURCEDIR := ../src
LIBDIR := $(DESTDIR)/usr/share/pyshared/UDSActor
BINDIR := $(DESTDIR)/usr/bin
SBINDIR := $(DESTDIR)/usr/sbin
APPSDIR := $(DESTDIR)/usr/share/applications
CFGDIR := $(DESTDIR)/etc/udsactor
PYC := $(shell find $(SOURCEDIR) -name '*.py[co]')
@ -14,6 +15,8 @@ clean:
install:
mkdir -p $(LIBDIR)
mkdir -p $(BINDIR)
mkdir -p $(SBINDIR)
mkdir -p $(APPSDIR)
mkdir -p $(CFGDIR)
mkdir -p $(LIBDIR)/
@ -24,7 +27,9 @@ install:
cp $(SOURCEDIR)/UDSActorUser.py $(LIBDIR)
cp $(SOURCEDIR)/setup_dialog_ui.py $(LIBDIR)
cp UDS_Actor_Configuration.desktop $(DESTDIR)
cp UDS_Actor_Configuration.desktop $(APPSDIR)
cp udsactor-daemon $(SBINDIR)
chmod 755 $(SBINDIR)/udsactor-daemon
# chmod 0755 $(BINDIR)/udsactor
uninstall:

1
actors/linux/debian/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/udsactor/

View File

@ -10,7 +10,7 @@ Package: udsactor
Section: admin
Priority: optional
Architecture: all
Depends: python-requests (>=0.8.2), python-qt4 (>=4.9), python-six(>=1.1), python (>=2.7), ${misc:Depends}
Depends: python-requests (>=0.8.2), python-qt4 (>=4.9), python-six(>=1.1), python-prctl(>=1.1.1), python (>=2.7), ${misc:Depends}
Description: Actor for Universal Desktop Services (UDS) Broker
This package provides the required components to allow this machine to work on an environment managed by UDS Broker.
.

View File

@ -9,10 +9,12 @@
### END INIT INFO
#
. /lib/lsb/init-functions
case "$1" in
start|stop|restart)
/usr/bin/udsactor $1
;;
/usr/sbin/udsactor-daemon $1
;;
force-reload)
./actor restart
;;

View File

@ -1 +0,0 @@
/etc/init.d/udsactor

View File

@ -1,45 +0,0 @@
#!/bin/sh -e
. /usr/share/debconf/confmodule
db_version 2.0
# This conf script is capable of backing up
db_capb backup
if [ -f /etc/udsactor/udsactor.cfg ] && [ "$1" != "reconfigure" ]; then
echo "/etc/udsactor/udsactor.cfg already exists, leaving untouched."
exit 0
fi
STATE=1
while [ "$STATE" != 0 -a "$STATE" != 4 ]; do
case "$STATE" in
1)
db_input high udsactor/host || true
;;
2)
db_input high udsactor/secure || true
;;
3)
db_input high udsactor/masterKey || true
;;
esac
if db_go; then
STATE=$(($STATE + 1))
else
STATE=$(($STATE - 1))
fi
done
# If "cancelled", exit
if [ "$STATE" = 0 ]; then
exit 0
fi
# If using reconfigure, and already exists an configuration file, move it to a backup
if [ -f /etc/udsactor/udsactor.cfg ] && [ "$1" = "reconfigure" ]; then
echo "/etc/udsactor.cfg backup to /etc/udsactor.cfg.back"
mv /etc/udsactor/udsactor.cfg /etc/udsactor/udsactor.cfg.back
fi

View File

@ -1,11 +0,0 @@
Package: udsactor
Version: 1.7.0
Architecture: all
Maintainer: Adolfo Gómez García <agomez@virtualcable.es>
Installed-Size: 206
Depends: python-requests (>= 0.8.2), python-qt4 (>= 4.9), python-six (>= 1.1), python (>= 2.7), debconf (>= 0.5) | debconf-2.0
Section: admin
Priority: optional
Homepage: http://www.virtualcable.es
Description: Actor for Universal Desktop Services (UDS) Broker
This package provides the required components to allow this machine to work on an environment managed by UDS Broker.

View File

@ -1,31 +0,0 @@
bb94247d566413c9f5e15c542926e92e UDS_Actor_Configuration.desktop
ca7e77fff581f83a326087af73def0af usr/share/doc/udsactor/changelog.gz
bb1b64d1c0e2a59e0a46180df5a33edf usr/share/doc/udsactor/copyright
fc4e41285d388d33b5f149ebe4b93236 usr/share/doc/udsactor/readme.txt
86020414868d2ede37e76a911aa211c5 usr/share/pyshared/UDSActor/UDSActorConfig.py
b14dee09070d95cac6321a13442f15b8 usr/share/pyshared/UDSActor/UDSActorUser.py
db1be7dd5fec00a3c31587fe8ce8e56f usr/share/pyshared/UDSActor/setup_dialog_ui.py
5317be4587c15ce790bfa55399f4d601 usr/share/pyshared/UDSActor/udsactor/REST.py
68b329da9893e34099c7d8ad5cb9c940 usr/share/pyshared/UDSActor/udsactor/__init__.py
976710a766859591a543eebdf351537b usr/share/pyshared/UDSActor/udsactor/certs.py
bcfca2062d45ec44b77b1e65d611bcc1 usr/share/pyshared/UDSActor/udsactor/httpserver.py
0350137ae586a7babe54a75e68fa1fe7 usr/share/pyshared/UDSActor/udsactor/ipc.py
6b7afab1321512cf7884df70f86041e8 usr/share/pyshared/UDSActor/udsactor/linux/UDSActorService.py
3ef55d64ebda86651c3d882c5c649389 usr/share/pyshared/UDSActor/udsactor/linux/__init__.py
7322c53041ef99b982a6bbdb9d510c70 usr/share/pyshared/UDSActor/udsactor/linux/daemon.py
c599495230cbd3f6495ba4a6edc169d9 usr/share/pyshared/UDSActor/udsactor/linux/log.py
fe28aef338eacc56f8e76a33ac33ac2a usr/share/pyshared/UDSActor/udsactor/linux/operations.py
1234f7f854901dc53a6d67105e72404d usr/share/pyshared/UDSActor/udsactor/linux/renamer/__init__.py
e958b3a317d41f350a412992f5016e4d usr/share/pyshared/UDSActor/udsactor/linux/renamer/debian.py
93e6a3d18c34fda4a42d96b42407e52b usr/share/pyshared/UDSActor/udsactor/linux/store.py
3351055d556c481e7fb61b132ab96f90 usr/share/pyshared/UDSActor/udsactor/log.py
9bfbf09895f4319c4a5ebef858ff9faa usr/share/pyshared/UDSActor/udsactor/operations.py
8f5c4a4db6d42dbd2effd760f960b598 usr/share/pyshared/UDSActor/udsactor/service.py
c88b37b89a88734788e23b2f71a7fcd5 usr/share/pyshared/UDSActor/udsactor/store.py
68b513e35d67d3e783947ec35b60cfa7 usr/share/pyshared/UDSActor/udsactor/utils.py
517440806336069b121ea587ec0ed80f usr/share/pyshared/UDSActor/udsactor/windows/SENS.py
046cf7d994c0b635157016f3eb66d501 usr/share/pyshared/UDSActor/udsactor/windows/UDSActorService.py
3ef55d64ebda86651c3d882c5c649389 usr/share/pyshared/UDSActor/udsactor/windows/__init__.py
65bd1af1cf3744b17c35f6f77753a847 usr/share/pyshared/UDSActor/udsactor/windows/log.py
21bc9f1dc13170e181926b23f788829a usr/share/pyshared/UDSActor/udsactor/windows/operations.py
a659e2092e1abcf06155c35986707eb0 usr/share/pyshared/UDSActor/udsactor/windows/store.py

View File

@ -1,58 +0,0 @@
#!/bin/sh
. /usr/share/debconf/confmodule
set -e
case "$1" in
configure)
# If new "fresh" install or if configuration file has disappeared...
if [ "$2" = "" ] || [ ! -f /etc/udsactor/udsactor.cfg ]; then
db_get udsactor/host
host=$RET
db_get udsactor/secure
ssl=$RET
if [ "$ssl" = "true" ]; then
ssl=True;
else
ssl=False;
fi
db_get udsactor/masterKey
masterKey=$RET
# If already has a config file there
if [ -f /etc/udsactor/udsactor.cfg ]; then
cp /etc/udsactor/udsactor.cfg /etc/udsactor/udsactor.cfg.dpkg-old
fi
echo "[uds]" > /etc/udsactor/udsactor.cfg
echo "host = $host" >> /etc/udsactor/udsactor.cfg
echo "logLevel = 30000" >> /etc/udsactor/udsactor.cfg
echo "ssl = $ssl" >> /etc/udsactor/udsactor.cfg
echo "masterKey = $masterKey" >> /etc/udsactor/udsactor.cfg
fi
# Fix perms so only root can access "masterKey"
chmod 0700 /etc/udsactor
chmod 0600 /etc/udsactor/udsactor.cfg
chown root:root /etc/udsactor
chown root:root /etc/udsactor/udsactor.cfg
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
# Automatically added by dh_installinit
if [ -x "/etc/init.d/udsactor" ]; then
update-rc.d udsactor defaults >/dev/null || exit $?
fi
# End automatically added section
exit 0

View File

@ -1,6 +0,0 @@
#! /bin/bash -e
if [ "$1" = "purge" ] ; then
if [ -f /etc/udsactor/udsactor.cfg ]; then
mv /etc/udsactor/udsactor.cfg /etc/udsactor/udsactor.cfg.dpkg-backup
fi
fi

View File

@ -1 +0,0 @@
#! /bin/bash -e

View File

@ -1,20 +0,0 @@
Template: udsactor/host
Type: string
Default:
Description: UDS Server address:
The actor needs the address of the server in order to communicate with it.
Provide here full address (or i) of the UDS server
Template: udsactor/secure
Type: boolean
Default: true
Description: Use secure (https) connection to communicate with UDS server?
If selected, the communication will be done using https.
If not selected, the communication will be done using http
Template: udsactor/masterKey
Type: string
Default:
Description: Master Key:
This key is available on UDS Administration interface.
Look for it under configuration, on Security tab.

View File

@ -1,11 +0,0 @@
[Desktop Entry]
Name=UDS Actor Configuration
Version=1.0
Exec=/usr/bin/UDSActorConfig
Comment=UDS Actor Configuration Application. (Must be executed as root)
Icon=/usr/share/pyshared/UDSActor/img/uds.png
Type=Application
Terminal=false
StartupNotify=true
Encoding=UTF-8
Categories=Settings;System;

View File

@ -1,21 +0,0 @@
#!/bin/sh -e
### BEGIN INIT INFO
# Provides: uds-actor
# Required-Start: $local_fs $remote_fs $network $syslog $named
# Required-Stop: $local_fs $remote_fs $network $syslog $named
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: UDS Actor
### END INIT INFO
#
case "$1" in
start|stop|restart)
/usr/bin/udsactor $1
;;
force-reload)
./actor restart
;;
*) echo "Usage: $0 {start|stop|restart|force-reload}" >&2; exit 1 ;;
esac

View File

@ -1,26 +0,0 @@
Format-Specification: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?op=file&rev=135
Name: udsactor
Maintainer: Adolfo Gómez García
Source: http://www.udsenterprise.com/
Copyright: 2014 Virtual Cable S.L.U.
License: BSD-3-clause
License: GPL-2+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
.
On Debian systems, the full text of the GNU General Public
License version 2 can be found in the file
`/usr/share/common-licenses/GPL-2'.

View File

@ -1 +0,0 @@
This package provides the actor needed for using with UDS infrastructure.

View File

@ -1,112 +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
'''
from __future__ import unicode_literals
import sys
from PyQt4 import QtCore, QtGui
import six
from udsactor import store
from udsactor import REST
from udsactor import utils
from udsactor.log import logger
from setup_dialog_ui import Ui_UdsActorSetupDialog
class MyForm(QtGui.QDialog):
def __init__(self, data, parent=None):
QtGui.QDialog.__init__(self, parent)
self.ui = Ui_UdsActorSetupDialog()
self.ui.setupUi(self)
if data is not None:
logger.debug('Setting configuration parameters in form: {}'.format(data))
self.ui.host.setText(data.get('host', ''))
self.ui.masterKey.setText(data.get('masterKey', ''))
self.ui.useSSl.setCurrentIndex(1 if data.get('ssl', False) is True else 0)
self.ui.logLevelComboBox.setCurrentIndex(int(data.get('logLevel', '10000')) / 10000 - 1)
def _getCfg(self):
return {
'host': six.text_type(self.ui.host.text()),
'masterKey': six.text_type(self.ui.masterKey.text()),
'ssl': self.ui.useSSl.currentIndex() == 1,
'logLevel': (self.ui.logLevelComboBox.currentIndex() + 1) * 10000
}
def textChanged(self):
enableButtons = self.ui.host.text() != '' and self.ui.masterKey.text() != ''
self.ui.testButton.setEnabled(enableButtons)
self.ui.saveButton.setEnabled(enableButtons)
def cancelAndDiscard(self):
logger.debug('Cancelling changes')
self.close()
def testParameters(self):
logger.debug('Testing connection')
try:
cfg = self._getCfg()
api = REST.Api(
cfg['host'], cfg['masterKey'], cfg['ssl'], scrambledResponses=True)
api.test()
QtGui.QMessageBox.information(
self, 'Test Passed', 'The test was executed successfully', QtGui.QMessageBox.Ok)
logger.info('Test was passed successfully')
except Exception as e:
logger.info('Test error: {}'.format(utils.exceptionToMessage(e)))
QtGui.QMessageBox.critical(self, 'Test Error', utils.exceptionToMessage(e), QtGui.QMessageBox.Ok)
def acceptAndSave(self):
cfg = self._getCfg()
store.writeConfig(cfg)
self.close()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
if store.checkPermissions() is False:
QtGui.QMessageBox.critical(None, 'Notice', 'This Program must be executed as administrator', QtGui.QMessageBox.Ok)
sys.exit(1)
# Read configuration
cfg = store.readConfig()
if cfg is not None:
logger.setLevel(int(cfg.get('logLevel', 20000)))
else:
logger.setLevel(20000)
myapp = MyForm(cfg)
myapp.show()
sys.exit(app.exec_())

View File

@ -1,160 +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
'''
from __future__ import unicode_literals
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
import cPickle
from udsactor import ipc
from udsactor import utils
from udsactor.log import logger
from udsactor.service import IPC_PORT
class MessagesProcessor(QtCore.QThread):
logoff = QtCore.pyqtSignal(name='logoff')
displayMessage = QtCore.pyqtSignal(QtCore.QString, name='displayMessage')
script = QtCore.pyqtSignal(QtCore.QString, name='script')
exit = QtCore.pyqtSignal(name='exit')
information = QtCore.pyqtSignal(dict, name='information')
def __init__(self):
super(self.__class__, self).__init__()
try:
self.ipc = ipc.ClientIPC(IPC_PORT)
self.ipc.start()
except Exception:
self.ipc = None
self.running = False
def stop(self):
self.running = False
if self.ipc:
self.ipc.stop()
def requestInformation(self):
if self.ipc:
info = self.ipc.requestInformation()
logger.debug('Request information: {}'.format(info))
def run(self):
if self.ipc is None:
return
self.running = True
while self.running and self.ipc.running:
try:
msg = self.ipc.getMessage()
if msg is None:
break
msgId, data = msg
logger.debug('Got Message on User Space: {}:{}'.format(msgId, data))
if msgId == ipc.MSG_MESSAGE:
self.displayMessage.emit(QtCore.QString.fromUtf8(data))
elif msgId == ipc.MSG_LOGOFF:
self.logoff.emit()
elif msgId == ipc.MSG_SCRIPT:
self.script.emit(QtCore.QString.fromUtf8(data))
elif msgId == ipc.MSG_INFORMATION:
self.information.emit(cPickle.loads(data))
except Exception as e:
try:
logger.error('Got error on IPC thread {}'.format(utils.exceptionToMessage(e)))
except:
logger.error('Got error on IPC thread (an unicode error??)')
if self.ipc.running is False and self.running is True:
logger.warn('Lost connection with Service, closing program')
self.exit.emit()
class UDSSystemTray(QtGui.QSystemTrayIcon):
def __init__(self, app_, parent=None):
self.app = app_
style = app.style()
icon = QtGui.QIcon(style.standardPixmap(QtGui.QStyle.SP_DesktopIcon))
QtGui.QSystemTrayIcon.__init__(self, icon, parent)
self.menu = QtGui.QMenu(parent)
exitAction = self.menu.addAction("Exit")
exitAction.triggered.connect(self.quit)
self.setContextMenu(self.menu)
self.ipc = MessagesProcessor()
self.ipc.start()
self.ipc.displayMessage.connect(self.displayMessage)
self.ipc.exit.connect(self.quit)
self.ipc.script.connect(self.executeScript)
self.ipc.logoff.connect(self.loggof)
self.ipc.information.connect(self.information)
# Pre generate a request for information (general parameters) to daemon/service
self.ipc.requestInformation()
self.counter = 0
def displayMessage(self, message):
self.counter += 1
print(message.toUtf8(), '--', self.counter)
def executeScript(self, message):
self.counter += 1
print(message.toUtf8(), '--', self.counter)
def loggof(self):
self.counter += 1
print("Loggof --", self.counter)
def information(self, info):
self.counter += 1
print("Information:", info, '--', self.counter)
def quit(self):
self.ipc.stop()
self.app.quit()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
if not QtGui.QSystemTrayIcon.isSystemTrayAvailable():
QtGui.QMessageBox.critical(None, "Systray", "I couldn't detect any system tray on this system.")
sys.exit(1)
trayIcon = UDSSystemTray(app)
trayIcon.show()
sys.exit(app.exec_())

View File

@ -1,141 +0,0 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'setup-dialog.ui'
#
# Created: Wed Nov 12 04:50:26 2014
# by: PyQt4 UI code generator 4.11.2
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
try:
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)
class Ui_UdsActorSetupDialog(object):
def setupUi(self, UdsActorSetupDialog):
UdsActorSetupDialog.setObjectName(_fromUtf8("UdsActorSetupDialog"))
UdsActorSetupDialog.setWindowModality(QtCore.Qt.WindowModal)
UdsActorSetupDialog.resize(400, 243)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(UdsActorSetupDialog.sizePolicy().hasHeightForWidth())
UdsActorSetupDialog.setSizePolicy(sizePolicy)
font = QtGui.QFont()
font.setFamily(_fromUtf8("Verdana"))
font.setPointSize(9)
UdsActorSetupDialog.setFont(font)
UdsActorSetupDialog.setAutoFillBackground(False)
UdsActorSetupDialog.setLocale(QtCore.QLocale(QtCore.QLocale.English, QtCore.QLocale.UnitedStates))
UdsActorSetupDialog.setSizeGripEnabled(False)
UdsActorSetupDialog.setModal(True)
self.testButton = QtGui.QPushButton(UdsActorSetupDialog)
self.testButton.setEnabled(False)
self.testButton.setGeometry(QtCore.QRect(20, 160, 361, 23))
self.testButton.setObjectName(_fromUtf8("testButton"))
self.saveButton = QtGui.QPushButton(UdsActorSetupDialog)
self.saveButton.setEnabled(False)
self.saveButton.setGeometry(QtCore.QRect(20, 190, 101, 23))
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.saveButton.sizePolicy().hasHeightForWidth())
self.saveButton.setSizePolicy(sizePolicy)
self.saveButton.setObjectName(_fromUtf8("saveButton"))
self.cancelButton = QtGui.QPushButton(UdsActorSetupDialog)
self.cancelButton.setGeometry(QtCore.QRect(260, 190, 121, 23))
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.cancelButton.sizePolicy().hasHeightForWidth())
self.cancelButton.setSizePolicy(sizePolicy)
self.cancelButton.setObjectName(_fromUtf8("cancelButton"))
self.layoutWidget = QtGui.QWidget(UdsActorSetupDialog)
self.layoutWidget.setGeometry(QtCore.QRect(20, 20, 361, 146))
self.layoutWidget.setObjectName(_fromUtf8("layoutWidget"))
self.formLayout = QtGui.QFormLayout(self.layoutWidget)
self.formLayout.setFieldGrowthPolicy(QtGui.QFormLayout.AllNonFixedFieldsGrow)
self.formLayout.setMargin(0)
self.formLayout.setVerticalSpacing(16)
self.formLayout.setObjectName(_fromUtf8("formLayout"))
self.label = QtGui.QLabel(self.layoutWidget)
self.label.setObjectName(_fromUtf8("label"))
self.formLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.label)
self.host = QtGui.QLineEdit(self.layoutWidget)
self.host.setAcceptDrops(False)
self.host.setObjectName(_fromUtf8("host"))
self.formLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.host)
self.label_3 = QtGui.QLabel(self.layoutWidget)
self.label_3.setObjectName(_fromUtf8("label_3"))
self.formLayout.setWidget(1, QtGui.QFormLayout.LabelRole, self.label_3)
self.masterKey = QtGui.QLineEdit(self.layoutWidget)
self.masterKey.setObjectName(_fromUtf8("masterKey"))
self.formLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.masterKey)
self.label_4 = QtGui.QLabel(self.layoutWidget)
self.label_4.setObjectName(_fromUtf8("label_4"))
self.formLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.label_4)
self.useSSl = QtGui.QComboBox(self.layoutWidget)
self.useSSl.setObjectName(_fromUtf8("useSSl"))
self.useSSl.addItem(_fromUtf8(""))
self.useSSl.addItem(_fromUtf8(""))
self.formLayout.setWidget(2, QtGui.QFormLayout.FieldRole, self.useSSl)
self.logLevelLabel = QtGui.QLabel(self.layoutWidget)
self.logLevelLabel.setObjectName(_fromUtf8("logLevelLabel"))
self.formLayout.setWidget(3, QtGui.QFormLayout.LabelRole, self.logLevelLabel)
self.logLevelComboBox = QtGui.QComboBox(self.layoutWidget)
self.logLevelComboBox.setObjectName(_fromUtf8("logLevelComboBox"))
self.logLevelComboBox.addItem(_fromUtf8(""))
self.logLevelComboBox.setItemText(0, _fromUtf8("DEBUG"))
self.logLevelComboBox.addItem(_fromUtf8(""))
self.logLevelComboBox.setItemText(1, _fromUtf8("INFO"))
self.logLevelComboBox.addItem(_fromUtf8(""))
self.logLevelComboBox.setItemText(2, _fromUtf8("EROR"))
self.logLevelComboBox.addItem(_fromUtf8(""))
self.logLevelComboBox.setItemText(3, _fromUtf8("FATAL"))
self.formLayout.setWidget(3, QtGui.QFormLayout.FieldRole, self.logLevelComboBox)
self.retranslateUi(UdsActorSetupDialog)
self.logLevelComboBox.setCurrentIndex(1)
QtCore.QObject.connect(self.host, QtCore.SIGNAL(_fromUtf8("textChanged(QString)")), UdsActorSetupDialog.textChanged)
QtCore.QObject.connect(self.masterKey, QtCore.SIGNAL(_fromUtf8("textChanged(QString)")), UdsActorSetupDialog.textChanged)
QtCore.QObject.connect(self.cancelButton, QtCore.SIGNAL(_fromUtf8("pressed()")), UdsActorSetupDialog.cancelAndDiscard)
QtCore.QObject.connect(self.testButton, QtCore.SIGNAL(_fromUtf8("pressed()")), UdsActorSetupDialog.testParameters)
QtCore.QObject.connect(self.saveButton, QtCore.SIGNAL(_fromUtf8("pressed()")), UdsActorSetupDialog.acceptAndSave)
QtCore.QMetaObject.connectSlotsByName(UdsActorSetupDialog)
def retranslateUi(self, UdsActorSetupDialog):
UdsActorSetupDialog.setWindowTitle(_translate("UdsActorSetupDialog", "UDS Actor Configuration", None))
self.testButton.setToolTip(_translate("UdsActorSetupDialog", "Click to test the selecter parameters", None))
self.testButton.setWhatsThis(_translate("UdsActorSetupDialog", "<html><head/><body><p>Click on this button to test the server host and master key parameters.</p><p>A window will be displayed with results after the test is executed.</p><p><br/></p><p>This button will only be active if all parameters are filled.</p></body></html>", None))
self.testButton.setText(_translate("UdsActorSetupDialog", "Test parameters", None))
self.saveButton.setToolTip(_translate("UdsActorSetupDialog", "Accepts changes and saves them", None))
self.saveButton.setWhatsThis(_translate("UdsActorSetupDialog", "Clicking on this button will accept all changes and save them, closing the configuration window", None))
self.saveButton.setText(_translate("UdsActorSetupDialog", "Accept && Save", None))
self.cancelButton.setToolTip(_translate("UdsActorSetupDialog", "Cancel all changes and discard them", None))
self.cancelButton.setWhatsThis(_translate("UdsActorSetupDialog", "Discards all changes and closes the configuration window", None))
self.cancelButton.setText(_translate("UdsActorSetupDialog", "Cancel && Discard", None))
self.label.setText(_translate("UdsActorSetupDialog", "UDS Server Host", None))
self.host.setToolTip(_translate("UdsActorSetupDialog", "Uds Broker Server Addres. Use IP or FQDN", None))
self.host.setWhatsThis(_translate("UdsActorSetupDialog", "Enter here the UDS Broker Addres using either its IP address or its FQDN address", None))
self.label_3.setText(_translate("UdsActorSetupDialog", "UDS Master Key", None))
self.masterKey.setToolTip(_translate("UdsActorSetupDialog", "Master key to communicate with UDS Broker", None))
self.masterKey.setWhatsThis(_translate("UdsActorSetupDialog", "<html><head/><body><p>Enter the Master Key (found on<span style=\" font-weight:600;\"> UDS Configuration</span> section) of the UDS Broker to allow communication of the Actor with Broker</p></body></html>", None))
self.label_4.setText(_translate("UdsActorSetupDialog", "Security", None))
self.useSSl.setToolTip(_translate("UdsActorSetupDialog", "Select communication security with broker", None))
self.useSSl.setWhatsThis(_translate("UdsActorSetupDialog", "<html><head/><body><p>Select the security for communications with UDS Broker.</p><p>The recommended method of communication is <span style=\" font-weight:600;\">Use SSL</span>, but selection needs to be acording to your broker configuration.</p></body></html>", None))
self.useSSl.setItemText(0, _translate("UdsActorSetupDialog", "Do not use SSL", None))
self.useSSl.setItemText(1, _translate("UdsActorSetupDialog", "Use SSL", None))
self.logLevelLabel.setText(_translate("UdsActorSetupDialog", "Log Level", None))

View File

@ -1,221 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 201 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
'''
# pylint: disable-msg=E1101,W0703
from __future__ import unicode_literals
import requests
import logging
import json
import uuid
import six
import codecs
from .utils import exceptionToMessage
VERIFY_CERT = False
class RESTError(Exception):
ERRCODE = 0
class ConnectionError(RESTError):
ERRCODE = -1
# Errors ""raised"" from broker
class InvalidKeyError(RESTError):
ERRCODE = 1
class UnmanagedHostError(RESTError):
ERRCODE = 2
class UserServiceNotFoundError(RESTError):
ERRCODE = 3
class OsManagerError(RESTError):
ERRCODE = 4
# Disable warnings log messages
try:
import urllib3 # @UnusedImport
except Exception:
from requests.packages import urllib3 # @Reimport
try:
urllib3.disable_warnings() # @UndefinedVariable
except Exception:
pass # In fact, isn't too important, but wil log warns to logging file
def ensureResultIsOk(result):
if 'error' not in result:
return
for i in (InvalidKeyError, UnmanagedHostError, UserServiceNotFoundError, OsManagerError):
if result['error'] == i.ERRCODE:
raise i(result['result'])
err = RESTError(result['result'])
err.ERRCODE = result['error']
raise err
def unscramble(value):
if value is None or value == '':
return value
value = bytearray(codecs.decode(value, 'hex'))
n = 0x32
result = []
for ch in value:
c = ch ^ n
n = (n + c) & 0xFF
result.append(six.int2byte(c))
return b''.join(result)[::-1].decode('utf8')
class Api(object):
def __init__(self, host, masterKey, ssl, scrambledResponses=False):
self.host = host
self.masterKey = masterKey
self.useSSL = ssl
self.scrambledResponses = scrambledResponses
self.uuid = None
self.mac = None
self.url = "{}://{}/rest/actor/".format(('http', 'https')[ssl], self.host)
self.secretKey = six.text_type(uuid.uuid4())
self.newerRequestLib = 'verify' in requests.sessions.Session.__attrs__
# Disable logging requests messages except for errors, ...
logging.getLogger("requests").setLevel(logging.ERROR)
def _getUrl(self, method, key=None, ids=None):
url = self.url + method
params = []
if key is not None:
params.append('key=' + key)
if ids is not None:
params.append('id=' + ids)
if len(params) > 0:
url += '?' + '&'.join(params)
return url
def _request(self, url, data=None):
try:
if data is None:
# Old requests version does not support verify, but they do not checks ssl certificate
if self.newerRequestLib:
r = requests.get(url, verify=VERIFY_CERT)
else:
r = requests.get(url) # Always ignore certs??
else:
if self.newerRequestLib:
r = requests.post(url, data=data, headers={'content-type': 'application/json'}, verify=VERIFY_CERT)
else:
r = requests.post(url, data=data, headers={'content-type': 'application/json'})
r = json.loads(r.content) # Using instead of r.json() to make compatible with oooold rquests lib versions
except requests.exceptions.RequestException as e:
raise ConnectionError(e)
except Exception as e:
raise ConnectionError(exceptionToMessage(e))
ensureResultIsOk(r)
if self.scrambledResponses is True:
# test && init are not scrambled, even if rest of messages are
try:
r['result'] = unscramble(r['result'])
except Exception as e: # Can't unscramble, return result "as is"
r['warning'] = True
return r
@property
def isConnected(self):
return self.uuid is not None
def test(self):
url = self._getUrl('test', self.masterKey)
return self._request(url)['result']
def init(self, ids):
'''
Ids is a comma separated values indicating MAC=ip
'''
url = self._getUrl('init', key=self.masterKey, ids=ids)
self.uuid, self.mac = self._request(url)['result']
return self.uuid
def postMessage(self, msg, data, processData=True):
if self.uuid is None:
raise ConnectionError('REST api has not been initialized')
if processData:
data = json.dumps({'data': data})
url = self._getUrl('/'.join([self.uuid, msg]))
return self._request(url, data)['result']
def notifyComm(self, url):
return self.postMessage('notifyComms', url)
def login(self, username):
return self.postMessage('login', username)
def logout(self, username):
return self.postMessage('logout', username)
def information(self):
return self.postMessage('information', '')
def setReady(self, ipsInfo):
data = ','.join(['{}={}'.format(v[0], v[1]) for v in ipsInfo])
return self.postMessage('ready', data)
def notifyIpChanges(self, ipsInfo):
data = ','.join(['{}={}'.format(v[0], v[1]) for v in ipsInfo])
return self.postMessage('ip', data)
def log(self, logLevel, message):
data = json.dumps({'message': message, 'level': logLevel})
return self.postMessage('log', data, processData=False)

View File

@ -1,103 +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
'''
from tempfile import gettempdir
from os.path import exists, join
CERTFILE = 'UDSActor.pem'
def createSelfSignedCert(force=False):
certFile = join(gettempdir(), CERTFILE)
if exists(certFile) and not force:
return certFile
certData = '''-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCb50K3mIznNklz
yVAD7xSQOSJQ6+NPXj7U9/4zLZ+TvmbQ7RqUUsxbfxHbeRnoYTWV2nKk4+tHqmvz
ujLSS/loFhTSMqtrLn7rowSYJoQhKOUkAiQlWkqCfItWgL5pJopDpNHFul9Rn3ds
PMWQTiGeUNR4Y3RnBhr1Q1BsqAzf4m6zFUmgLPPmVLdF4uJ3Tuz8TSy2gWLs5aSr
5do4WamwUfYjRSVMJECmwjUM4rQ8SQgg0sHBeBuDUGNBvBQFac1G7qUcMReeu8Zr
DUtMsXma/l4rA8NB5CRmTrQbTBF4l+jb2BDFebDqDUK1Oqs9X35yOQfDOAFYHiix
PX0IsXOZAgMBAAECggEBAJi3000RrIUZUp6Ph0gzPMuCjDEEwWiQA7CPNX1gpb8O
dp0WhkDhUroWIaICYPSXtOwUTtVjRqivMoxPy1Thg3EIoGC/rdeSdlXRHMEGicwJ
yVyalFnatr5Xzg5wkxVh4XMd0zeDt7e3JD7s0QLo5lm1CEzd77qz6lhzFic5/1KX
bzdULtTlq60dazg2hEbcS4OmM1UMCtRVDAsOIUIZPL0M9j1C1d1iEdYnh2xshKeG
/GOfo95xsgdMlGjtv3hUT5ryKVoEsu+36rGb4VfhPfUvvoVbRx5QZpW+QvxaYh5E
Fi0JEROozFwG31Y++8El7J3yQko8cFBa1lYYUwwpNAECgYEAykT+GiM2YxJ4uVF1
OoKiE9BD53i0IG5j87lGPnWqzEwYBwnqjEKDTou+uzMGz3MDV56UEFNho7wUWh28
LpEkjJB9QgbsugjxIBr4JoL/rYk036e/6+U8I95lvYWrzb+rBMIkRDYI7kbQD/mQ
piYUpuCkTymNAu2RisK6bBzJslkCgYEAxVE23OQvkCeOV8hJNPZGpJ1mDS+TiOow
oOScMZmZpail181eYbAfMsCr7ri812lSj98NvA2GNVLpddil6LtS1cQ5p36lFBtV
xQUMZiFz4qVbEak+izL+vPaev/mXXsOcibAIQ+qI/0txFpNhJjpaaSy6vRCBYFmc
8pgSoBnBI0ECgYAUKCn2atnpp5aWSTLYgNosBU4vDA1PShD14dnJMaqyr0aZtPhF
v/8b3btFJoGgPMLxgWEZ+2U4ju6sSFhPf7FXvLJu2QfQRkHZRDbEh7t5DLpTK4Fp
va9vl6Ml7uM/HsGpOLuqfIQJUs87OFCc7iCSvMJDDU37I7ekT2GKkpfbCQKBgBrE
0NeY0WcSJrp7/oqD2sOcYurpCG/rrZs2SIZmGzUhMxaa0vIXzbO59dlWELB8pmnE
Tf20K//x9qA5OxDe0PcVPukdQlH+/1zSOYNliG44FqnHtyd1TJ/gKVtMBiAiE4uO
aSClod5Yosf4SJbCFd/s5Iyfv52NqsAyp1w3Aj/BAoGAVCnEiGUfyHlIR+UH4zZW
GXJMeqdZLfcEIszMxLePkml4gUQhoq9oIs/Kw+L1DDxUwzkXN4BNTlFbOSu9gzK1
dhuIUGfS6RPL88U+ivC3A0y2jT43oUMqe3hiRt360UQ1GXzp2dMnR9odSRB1wHoO
IOjEBZ8341/c9ZHc5PCGAG8=
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIID7zCCAtegAwIBAgIJAIrEIthCfxUCMA0GCSqGSIb3DQEBCwUAMIGNMQswCQYD
VQQGEwJFUzEPMA0GA1UECAwGTWFkcmlkMREwDwYDVQQHDAhBbGNvcmNvbjEMMAoG
A1UECgwDVURTMQ4wDAYDVQQLDAVBY3RvcjESMBAGA1UEAwwJVURTIEFjdG9yMSgw
JgYJKoZIhvcNAQkBFhlzdXBwb3J0QHVkc2VudGVycHJpc2UuY29tMB4XDTE0MTAy
NjIzNDEyNFoXDTI0MTAyMzIzNDEyNFowgY0xCzAJBgNVBAYTAkVTMQ8wDQYDVQQI
DAZNYWRyaWQxETAPBgNVBAcMCEFsY29yY29uMQwwCgYDVQQKDANVRFMxDjAMBgNV
BAsMBUFjdG9yMRIwEAYDVQQDDAlVRFMgQWN0b3IxKDAmBgkqhkiG9w0BCQEWGXN1
cHBvcnRAdWRzZW50ZXJwcmlzZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQCb50K3mIznNklzyVAD7xSQOSJQ6+NPXj7U9/4zLZ+TvmbQ7RqUUsxb
fxHbeRnoYTWV2nKk4+tHqmvzujLSS/loFhTSMqtrLn7rowSYJoQhKOUkAiQlWkqC
fItWgL5pJopDpNHFul9Rn3dsPMWQTiGeUNR4Y3RnBhr1Q1BsqAzf4m6zFUmgLPPm
VLdF4uJ3Tuz8TSy2gWLs5aSr5do4WamwUfYjRSVMJECmwjUM4rQ8SQgg0sHBeBuD
UGNBvBQFac1G7qUcMReeu8ZrDUtMsXma/l4rA8NB5CRmTrQbTBF4l+jb2BDFebDq
DUK1Oqs9X35yOQfDOAFYHiixPX0IsXOZAgMBAAGjUDBOMB0GA1UdDgQWBBRShS90
5lJTNvYPIEqP3GxWwG5iiDAfBgNVHSMEGDAWgBRShS905lJTNvYPIEqP3GxWwG5i
iDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAU0Sp4gXhQmRVzq+7+
vRFUkQuPj4Ga/d9r5Wrbg3hck3+5pwe9/7APoq0P/M0DBhQpiJKjrD6ydUevC+Y/
43ZOJPhMlNw0o6TdQxOkX6FDwQanLLs7sfvJvqtVzYn3nuRFKT3dvl7Zg44QMw2M
ay42q59fAcpB4LaDx/i7gOYSS5eca3lYW7j7YSr/+ozXK2KlgUkuCUHN95lOq+dF
trmV9mjzM4CNPZqKSE7kpHRywgrXGPCO000NvEGSYf82AtgRSFKiU8NWLQSEPdcB
k//2dsQZw2cRZ8DrC2B6Tb3M+3+CA6wVyqfqZh1SZva3LfGvq/C+u+ItguzPqNpI
xtvM
-----END CERTIFICATE-----'''
with open(certFile, "wt") as f:
f.write(certData)
return certFile
# At beginning, force certificate creation
createSelfSignedCert(force=True)

View File

@ -1,196 +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
'''
import threading
import uuid
import json
import six
from six.moves import socketserver # @UnresolvedImport, pylint: disable=import-error
from six.moves import BaseHTTPServer # @UnresolvedImport, pylint: disable=import-error
import time
from udsactor.log import logger
from udsactor import utils
from udsactor.certs import createSelfSignedCert
import ssl
startTime = time.time()
class HTTPServerHandler(BaseHTTPServer.BaseHTTPRequestHandler):
uuid = None
ipc = None
lock = threading.Lock()
def sendJsonError(self, code, message):
self.send_response(code)
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps({'error': message}))
return
def do_GET(self):
# Very simple path & params splitter
path = self.path.split('?')[0][1:].split('/')
try:
params = dict((v.split('=') for v in self.path.split('?')[1].split('&')))
except:
params = {}
if path[0] != HTTPServerHandler.uuid:
self.sendJsonError(403, 'Forbidden')
return
if len(path) != 2:
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
# Send the html message
self.wfile.write(json.dumps("UDS Actor has been running for {} seconds".format(time.time() - startTime)))
return
try:
operation = getattr(self, 'get_' + path[1])
result = operation(params) # Protect not POST methods
except AttributeError:
self.sendJsonError(404, 'Method not found')
return
except Exception as e:
logger.error('Got exception executing GET {}: {}'.format(path[1], utils.toUnicode(e.message)))
self.sendJsonError(500, str(e))
return
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
# Send the html message
self.wfile.write(json.dumps(result))
def do_POST(self):
path = self.path.split('?')[0][1:].split('/')
if path[0] != HTTPServerHandler.uuid:
self.sendJsonError(403, 'Forbidden')
return
if len(path) != 2:
self.sendJsonError(400, 'Invalid request')
return
try:
HTTPServerHandler.lock.acquire()
length = int(self.headers.getheader('content-length'))
content = self.rfile.read(length)
print(length, ">>", content, '<<')
params = json.loads(content)
operation = getattr(self, 'post_' + path[1])
result = operation(params) # Protect not POST methods
except AttributeError:
self.sendJsonError(404, 'Method not found')
return
except Exception as e:
logger.error('Got exception executing POST {}: {}'.format(path[1], utils.toUnicode(e.message)))
self.sendJsonError(500, str(e))
return
finally:
HTTPServerHandler.lock.release()
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
# Send the html message
self.wfile.write(json.dumps(result))
def post_logoff(self, params):
logger.debug('Sending LOGOFF to clients')
HTTPServerHandler.ipc.sendLoggofMessage()
return 'ok'
# Alias
post_logout = post_logoff
def post_message(self, params):
logger.debug('Sending MESSAGE to clients')
if 'message' not in params:
raise Exception('Invalid message parameters')
HTTPServerHandler.ipc.sendMessageMessage(params['message'])
return 'ok'
def post_script(self, params):
if 'script' not in params:
raise Exception('Invalid script parameters')
if 'user' in params:
logger.debug('Sending SCRIPT to clients')
HTTPServerHandler.ipc.sendScriptMessage(params['script'])
else:
# Execute script at server space, that is, here
# as a secondary thread
script = params['script']
def executor():
logger.debug('Executing script: {}'.format(script))
try:
six.exec_(script, None, None)
except Exception as e:
logger.error('Error executing script: {}'.format(e))
th = threading.Thread(target=executor)
th.start()
return 'ok'
def get_information(self, params):
# TODO: Return something useful? :)
return 'Information'
class HTTPServerThread(threading.Thread):
def __init__(self, address, ipc):
super(self.__class__, self).__init__()
if HTTPServerHandler.uuid is None:
HTTPServerHandler.uuid = uuid.uuid4().get_hex()
HTTPServerHandler.ipc = ipc
self.certFile = createSelfSignedCert()
self.server = socketserver.TCPServer(address, HTTPServerHandler)
self.server.socket = ssl.wrap_socket(self.server.socket, certfile=self.certFile, server_side=True)
def getServerUrl(self):
return 'https://{}:{}/{}'.format(self.server.server_address[0], self.server.server_address[1], HTTPServerHandler.uuid)
def stop(self):
logger.debug('Stopping REST Service')
self.server.shutdown()
def run(self):
self.server.serve_forever()

View File

@ -1,389 +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
'''
from __future__ import unicode_literals
import socket
import threading
import sys
import six
import traceback
import pickle
from udsactor.utils import toUnicode
from udsactor.log import logger
# The IPC Server will wait for connections from clients
# Clients will open socket, and wait for data from server
# The messages sent (from server) will be the following (subject to future changes):
# Message_id Data Action
# ------------ -------- --------------------------
# MSG_LOGOFF None Logout user from session
# MSG_MESSAGE message,level Display a message with level (INFO, WARN, ERROR, FATAL) # TODO: Include level, right now only has message
# MSG_SCRIPT python script Execute an specific python script INSIDE CLIENT environment (this messages is not sent right now)
# The messages received (sent from client) will be the following:
# Message_id Data Action
# ------------ -------- --------------------------
# REQ_LOGOUT Logout user from session
# REQ_INFORMATION None Request information from ipc server (maybe configuration parameters in a near future)
# REQ_LOGIN python script Execute an specific python script INSIDE CLIENT environment (this messages is not sent right now)
#
# All messages are in the form:
# BYTE
# 0 1-2 3 4 ...
# MSG_ID DATA_LENGTH (little endian) Data (can be 0 length)
# With a previos "MAGIC" header in fron of each message
MSG_LOGOFF = 0xA1
MSG_MESSAGE = 0xB2
MSG_SCRIPT = 0xC3
MSG_INFORMATION = 0xD4
# Request messages
REQ_INFORMATION = MSG_INFORMATION
REQ_LOGIN = 0xE5
REQ_LOGOUT = MSG_LOGOFF
VALID_MESSAGES = (MSG_LOGOFF, MSG_MESSAGE, MSG_SCRIPT, MSG_INFORMATION)
REQ_INFORMATION = 0xAA
MAGIC = b'\x55\x44\x53\x00' # UDS in hexa with a padded 0 to the right
# Allows notifying login/logout from client for linux platform
ALLOW_LOG_METHODS = sys.platform != 'win32'
# States for client processor
ST_SECOND_BYTE = 0x01
ST_RECEIVING = 0x02
ST_PROCESS_MESSAGE = 0x02
class ClientProcessor(threading.Thread):
def __init__(self, parent, clientSocket):
super(self.__class__, self).__init__()
self.parent = parent
self.clientSocket = clientSocket
self.running = False
self.messages = six.moves.queue.Queue(32) # @UndefinedVariable
def stop(self):
logger.debug('Stoping client processor')
self.running = False
def processRequest(self, msg, data):
print('Got message {}, with data {}'.format(msg, data))
def run(self):
self.running = True
self.clientSocket.setblocking(0)
state = None
recv_msg = None
recv_data = None
while self.running:
try:
counter = 1024
while counter > 0: # So we process at least the incoming queue every XX bytes readed
counter -= 1
b = self.clientSocket.recv(1)
if b == b'':
break
buf = six.byte2int(b) # Empty buffer, this is set as non-blocking
if state is None:
if buf in (REQ_INFORMATION, REQ_LOGIN, REQ_LOGOUT):
print('State set to {}'.format(buf))
state = buf
recv_msg = buf
continue # Get next byte
else:
logger.debug('Got unexpected data {}'.format(buf))
elif state in (REQ_INFORMATION, REQ_LOGIN, REQ_LOGOUT):
print('First length byte is {}'.format(buf))
msg_len = buf
state = ST_SECOND_BYTE
continue
elif state == ST_SECOND_BYTE:
msg_len += buf << 8
print('Second length byte is {}, len is {}'.format(buf, msg_len))
if msg_len == 0:
self.processRequest(recv_msg, None)
state = None
break
state = ST_RECEIVING
recv_data = b''
continue
elif state == ST_RECEIVING:
recv_data += six.int2byte(buf)
msg_len -= 1
if msg_len == 0:
self.processRequest(recv_msg, recv_data)
recv_data = None
state = None
break
else:
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
pass
try:
msg = self.messages.get(block=False)
except six.moves.queue.Empty: # No message got in time @UndefinedVariable
continue
logger.debug('Got message {}'.format(msg))
try:
m = msg[1] if msg[1] is not None else b''
l = len(m)
data = MAGIC + six.int2byte(msg[0]) + six.int2byte(l & 0xFF) + six.int2byte(l >> 8) + m
try:
self.clientSocket.sendall(data)
except socket.error as e:
# Send data error
logger.debug('Socket connection is no more available: {}'.format(e.args))
self.running = False
except Exception as e:
logger.error('Invalid message in queue: {}'.format(e))
try:
self.clientSocket.close()
except Exception:
pass # If can't close, nothing happens, just end thread
class ServerIPC(threading.Thread):
def __init__(self, listenPort, infoParams=None):
super(self.__class__, self).__init__()
self.port = listenPort
self.running = False
self.serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.threads = []
self.infoParams = infoParams
def stop(self):
logger.debug('Stopping Server IPC')
self.running = False
for t in self.threads:
t.stop()
socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect(('localhost', self.port))
self.serverSocket.close()
for t in self.threads:
t.join()
def sendMessage(self, msgId, msgData):
'''
Notify message to all listening threads
'''
logger.debug('Sending message {},{} to all clients'.format(msgId, msgData))
# Convert to bytes so length is correctly calculated
if isinstance(msgData, six.text_type):
msgData = msgData.encode('utf8')
for t in self.threads:
if t.isAlive():
logger.debug('Sending to {}'.format(t))
t.messages.put((msgId, msgData))
def sendLoggofMessage(self):
self.sendMessage(MSG_LOGOFF, '')
def sendMessageMessage(self, message):
self.sendMessage(MSG_MESSAGE, message)
def sendScriptMessage(self, script):
self.sendMessage(MSG_SCRIPT, script)
def cleanupFinishedThreads(self):
'''
Cleans up current threads list
'''
aliveThreads = []
for t in self.threads:
if t.isAlive():
logger.debug('Thread {} is alive'.format(t))
aliveThreads.append(t)
self.threads[:] = aliveThreads
def run(self):
self.running = True
self.serverSocket.bind(('localhost', self.port))
self.serverSocket.setblocking(1)
self.serverSocket.listen(4)
while True:
try:
(clientSocket, address) = self.serverSocket.accept()
# Stop processiong if thread is mean to stop
if self.running is False:
break
logger.debug('Got connection from {}'.format(address))
self.cleanupFinishedThreads() # House keeping
t = ClientProcessor(self, clientSocket)
self.threads.append(t)
t.start()
except Exception as e:
logger.error('Got an exception on Server ipc thread: {}'.format(e))
class ClientIPC(threading.Thread):
def __init__(self, listenPort):
super(ClientIPC, self).__init__()
self.port = listenPort
self.running = False
self.clientSocket = None
self.messages = six.moves.queue.Queue(32) # @UndefinedVariable
self.connect()
def stop(self):
self.running = False
def getMessage(self):
while self.running:
try:
return self.messages.get(timeout=1)
except six.moves.queue.Empty: # @UndefinedVariable
continue
return None
def sendRequestMessage(self, msg, data=None):
if data is None:
data = b''
if isinstance(data, six.text_type): # Convert to bytes if necessary
data = data.encode('utf-8')
l = len(data)
msg = six.int2byte(msg) + six.int2byte(l & 0xFF) + six.int2byte(l >> 8) + data
self.clientSocket.sendall(msg)
def requestInformation(self):
self.sendRequestMessage(REQ_INFORMATION)
def sendLogin(self, username):
self.sendRequestMessage(REQ_LOGIN, username)
def sendLogout(self, username):
self.sendRequestMessage(REQ_LOGOUT, username)
def messageReceived(self):
'''
Override this method to automatically get notified on new message
received. Message is at self.messages queue
'''
pass # Messa
def receiveBytes(self, number):
msg = b''
while self.running and len(msg) < number:
try:
buf = self.clientSocket.recv(number - len(msg))
if buf == b'':
self.running = False
break
msg += buf
except socket.timeout:
pass
if self.running is False:
return None
return msg
def connect(self):
self.clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.clientSocket.connect(('localhost', self.port))
self.clientSocket.settimeout(2) # 2 seconds timeout
def run(self):
self.running = True
while self.running:
try:
msg = b''
# We look for magic message header
while self.running: # Wait for MAGIC
try:
buf = self.clientSocket.recv(len(MAGIC) - len(msg))
if buf == b'':
self.running = False
break
msg += buf
if len(msg) != len(MAGIC):
continue # Do not have message
if msg != MAGIC: # Skip first byte an continue searchong
msg = msg[1:]
continue
break
except socket.timeout: # Timeout is here so we can get stop thread
continue
# Now we get message basic data (msg + datalen)
msg = bytearray(self.receiveBytes(3))
# We have the magic header, here comes the message itself
if msg is None:
continue
msgId = msg[0]
dataLen = msg[1] + (msg[2] << 8)
if msgId not in VALID_MESSAGES:
raise Exception('Invalid message id: {}'.format(msgId))
data = self.receiveBytes(dataLen)
if data is None:
continue
self.messages.put((msgId, data))
self.messageReceived()
except socket.error as e:
logger.error('Communication with server got an error: {}'.format(toUnicode(e.strerror)))
self.running = False
return
except Exception as e:
logger.error('Error: {}'.format(e.args))
try:
self.clientSocket.close()
except Exception:
pass # If can't close, nothing happens, just end thread

View File

@ -1,155 +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
'''
from __future__ import unicode_literals
from udsactor import operations
from udsactor.service import CommonService
from udsactor.service import initCfg
from udsactor.service import IPC_PORT
from udsactor import ipc
from udsactor.log import logger
from udsactor.linux.daemon import Daemon
from udsactor.linux import renamer
import sys
class UDSActorSvc(Daemon, CommonService):
def __init__(self, args=None):
Daemon.__init__(self, '/var/run/udsa.pid')
CommonService.__init__(self)
def rename(self, name, user=None, oldPassword=None, newPassword=None):
'''
Renames the computer, and optionally sets a password for an user
before this
'''
# Check for password change request for an user
if user is not None:
logger.info('Setting password for user {}'.format(user))
try:
operations.changeUserPassword(user, oldPassword, newPassword)
except Exception as e:
# We stop here without even renaming computer, because the
# process has failed
raise Exception(
'Could not change password for user {} (maybe invalid current password is configured at broker): {} '.format(user, unicode(e)))
renamer.rename(name)
self.setReady()
def joinDomain(self, name, domain, ou, account, password):
logger.fatal('Join domain is not supported on linux platforms right now')
def run(self):
initCfg()
logger.debug('Running Daemon')
# Linux daemon will continue running unless something is requested to
if self.interactWithBroker() is False:
logger.debug('Interact with broker returned false, stopping service after a while')
return
if self.isAlive is False:
logger.debug('The service is not alive after broker interaction, stopping it')
return
if self.rebootRequested is True:
logger.debug('Reboot has been requested, stopping service')
return
self.initIPC()
# *********************
# * Main Service loop *
# *********************
# Counter used to check ip changes only once every 10 seconds, for
# example
counter = 0
while self.isAlive:
counter += 1
if counter % 10 == 0:
self.checkIpsChanged()
# In milliseconds, will break
self.doWait(1000)
self.endIPC()
self.endAPI()
self.notifyStop()
def usage():
sys.stderr.write("usage: {} start|stop|restart|login 'username'|logout 'username'".format(sys.argv[0]))
sys.exit(2)
if __name__ == '__main__':
initCfg()
if len(sys.argv) == 3:
client = None
try:
client = ipc.ClientIPC(IPC_PORT)
client.start()
if 'login' == sys.argv[1]:
client.sendLogin(sys.argv[2])
sys.exit(0)
elif 'logout' == sys.argv[1]:
client.sendLogout(sys.argv[2])
sys.exit(0)
else:
usage()
except Exception as e:
logger.error(e)
finally:
if client is not None:
client.stop()
usage()
logger.debug('Executing actor')
daemon = UDSActorSvc()
if len(sys.argv) == 2:
if 'start' == sys.argv[1]:
daemon.start()
elif 'stop' == sys.argv[1]:
daemon.stop()
elif 'restart' == sys.argv[1]:
daemon.restart()
else:
usage()
sys.exit(0)
else:
usage()

View File

@ -1,32 +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
'''
from __future__ import unicode_literals

View File

@ -1,175 +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: : http://www.jejik.com/authors/sander_marechal/
@see: : http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/
'''
from __future__ import unicode_literals
import sys
import os
import time
import atexit
from udsactor.log import logger
from signal import SIGTERM
class Daemon:
"""
A generic daemon class.
Usage: subclass the Daemon class and override the run() method
"""
def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
self.stdin = stdin
self.stdout = stdout
self.stderr = stderr
self.pidfile = pidfile
def daemonize(self):
"""
do the UNIX double-fork magic, see Stevens' "Advanced
Programming in the UNIX Environment" for details (ISBN 0201563177)
http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
"""
try:
pid = os.fork()
if pid > 0:
# exit first parent
sys.exit(0)
except OSError as e:
logger.error("fork #1 error: {}".format(e))
sys.stderr.write("fork #1 failed: {}\n".format(e))
sys.exit(1)
# decouple from parent environment
os.chdir("/")
os.setsid()
os.umask(0)
# do second fork
try:
pid = os.fork()
if pid > 0:
# exit from second parent
sys.exit(0)
except OSError as e:
logger.error("fork #2 error: {}".format(e))
sys.stderr.write("fork #2 failed: {}\n".format(e))
sys.exit(1)
# redirect standard file descriptors
sys.stdout.flush()
sys.stderr.flush()
si = open(self.stdin, 'r')
so = open(self.stdout, 'a+')
se = open(self.stderr, 'a+', 0)
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
# write pidfile
atexit.register(self.delpid)
pid = str(os.getpid())
with open(self.pidfile, 'w+') as f:
f.write("{}\n".format(pid))
def delpid(self):
os.remove(self.pidfile)
def start(self):
"""
Start the daemon
"""
logger.debug('Starting daemon')
# Check for a pidfile to see if the daemon already runs
try:
pf = open(self.pidfile, 'r')
pid = int(pf.read().strip())
pf.close()
except IOError:
pid = None
if pid:
message = "pidfile {} already exist. Daemon already running?\n".format(pid)
logger.error(message)
sys.stderr.write(message)
sys.exit(1)
# Start the daemon
self.daemonize()
try:
self.run()
except Exception as e:
logger.error('Exception running process: {}'.format(e))
def stop(self):
"""
Stop the daemon
"""
# Get the pid from the pidfile
try:
pf = open(self.pidfile, 'r')
pid = int(pf.read().strip())
pf.close()
except IOError:
pid = None
if pid is None:
message = "pidfile {} does not exist. Daemon not running?\n".format(self.pidfile)
logger.error(message)
sys.stderr.write(message)
return # not an error in a restart
# Try killing the daemon process
try:
while True:
os.kill(pid, SIGTERM)
time.sleep(1)
except OSError as err:
if err.errno == 3: # No such process
if os.path.exists(self.pidfile):
os.remove(self.pidfile)
else:
sys.stderr.write(err)
sys.exit(1)
def restart(self):
"""
Restart the daemon
"""
self.stop()
self.start()
# Overridables
def run(self):
"""
You should override this method when you subclass Daemon. It will be called after the process has been
daemonized by start() or restart().
"""

View File

@ -1,80 +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
'''
from __future__ import unicode_literals
import logging
import os
import tempfile
import six
# Valid logging levels, from UDS Broker (uds.core.utils.log)
OTHER, DEBUG, INFO, WARN, ERROR, FATAL = (10000 * (x + 1) for x in six.moves.xrange(6)) # @UndefinedVariable
class LocalLogger(object):
def __init__(self):
# tempdir is different for "user application" and "service"
# service wil get c:\windows\temp, while user will get c:\users\XXX\temp
# Try to open logger at /var/log path
# If it fails (access denied normally), will try to open one at user's home folder, and if
# agaim it fails, open it at the tmpPath
for logDir in ('/var/log', os.path.expanduser('~'), tempfile.gettempdir()):
try:
fname = os.path.join(logDir, 'udsactor.log')
logging.basicConfig(
filename=fname,
filemode='a',
format='%(levelname)s %(asctime)s %(message)s',
level=logging.DEBUG
)
self.logger = logging.getLogger('udsactor')
os.chmod(fname, 0o0600)
return
except Exception:
pass
# Logger can't be set
self.logger = None
def log(self, level, message):
# 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)
def isWindows(self):
return False
def isLinux(self):
return True

View File

@ -1,161 +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
'''
from __future__ import unicode_literals
import socket
import platform
import fcntl
import os
import struct
import array
import six
from udsactor import utils
def _getMacAddr(ifname):
'''
Returns the mac address of an interface
Mac is returned as unicode utf-8 encoded
'''
if isinstance(ifname, list):
return dict([(name, _getMacAddr(name)) for name in ifname])
if isinstance(ifname, six.text_type):
ifname = ifname.encode('utf-8') # If unicode, convert to bytes (or str in python 2.7)
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
info = bytearray(fcntl.ioctl(s.fileno(), 0x8927, struct.pack(str('256s'), ifname[:15])))
return six.text_type(''.join(['%02x:' % char for char in info[18:24]])[:-1])
except Exception:
return None
def _getIpAddr(ifname):
'''
Returns the ip address of an interface
Ip is returned as unicode utf-8 encoded
'''
if isinstance(ifname, list):
return dict([(name, _getIpAddr(name)) for name in ifname])
if isinstance(ifname, six.text_type):
ifname = ifname.encode('utf-8') # If unicode, convert to bytes (or str in python 2.7)
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return six.text_type(socket.inet_ntoa(fcntl.ioctl(
s.fileno(),
0x8915, # SIOCGIFADDR
struct.pack(str('256s'), ifname[:15])
)[20:24]))
except Exception:
return None
def _getInterfaces():
'''
Returns a list of interfaces names coded in utf-8
'''
max_possible = 128 # arbitrary. raise if needed.
space = max_possible * 16
if platform.architecture()[0] == '32bit':
offset, length = 32, 32
elif platform.architecture()[0] == '64bit':
offset, length = 16, 40
else:
raise OSError('Unknown arquitecture {0}'.format(platform.architecture()[0]))
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
names = array.array(str('B'), b'\0' * space)
outbytes = struct.unpack(str('iL'), fcntl.ioctl(
s.fileno(),
0x8912, # SIOCGIFCONF
struct.pack(str('iL'), space, names.buffer_info()[0])
))[0]
namestr = names.tostring()
# return namestr, outbytes
return [namestr[i:i + offset].split(b'\0', 1)[0].decode('utf-8') for i in range(0, outbytes, length)]
def _getIpAndMac(ifname):
ip, mac = _getIpAddr(ifname), _getMacAddr(ifname)
return (ip, mac)
def getComputerName():
'''
Returns computer name, with no domain
'''
return socket.gethostname().split('.')[0]
def getNetworkInfo():
for ifname in _getInterfaces():
ip, mac = _getIpAndMac(ifname)
if mac != '00:00:00:00:00:00': # Skips local interfaces
yield utils.Bunch(name=ifname, mac=mac, ip=ip)
def getDomainName():
return ''
def getLinuxVersion():
lv = platform.linux_distribution()
return lv[0] + ', ' + lv[1]
def reboot(flags=0):
'''
Simple reboot using os command
'''
os.system('/sbin/shutdown now -r')
def loggoff():
pass
def renameComputer(newName):
pass
def joinDomain(domain, ou, account, password, executeInOneStep=False):
pass
def changeUserPassword(user, oldPassword, newPassword):
'''
Simple password change for user using command line
'''
os.system('echo "{1}\n{1}" | /usr/bin/passwd {0} 2> /dev/null'.format(user, newPassword))
def getIdleDuration():
return 0

View File

@ -1,59 +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
'''
from __future__ import unicode_literals
import platform
import os
import sys
import pkgutil
from udsactor.log import logger
renamers = {}
def rename(newName):
distribution = platform.linux_distribution()[0].lower()
if distribution in renamers:
return renamers[distribution](newName)
logger.error('Renamer for platform "{0}" not found'.format(distribution))
return False
# Do load of packages
def _init():
pkgpath = os.path.dirname(sys.modules[__name__].__file__)
for _, name, _ in pkgutil.iter_modules([pkgpath]):
__import__(__name__ + '.' + name, globals(), locals())
_init()

View File

@ -1,68 +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
'''
from __future__ import unicode_literals
from udsactor.linux.renamer import renamers
from udsactor.log import logger
import os
def rename(newName):
'''
Debian renamer
Expects new host name on newName
Host does not needs to be rebooted after renaming
'''
logger.debug('using Debian renamer')
with open('/etc/hostname', 'w') as hostname:
hostname.write(newName)
# Force system new name
os.system('/bin/hostname %s' % newName)
# add name to "hosts"
with open('/etc/hosts', 'r') as hosts:
lines = hosts.readlines()
with open('/etc/hosts', 'w') as hosts:
hosts.write("127.0.1.1\t%s\n" % newName)
for l in lines:
if l[:9] == '127.0.1.1': # Skips existing 127.0.1.1. if it already exists
continue
hosts.write(l)
return True
# All names in lower case
renamers['debian'] = rename
renamers['ubuntu'] = rename

View File

@ -1,80 +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
'''
import six
import os
DEBUG = False
CONFIGFILE = '/etc/udsactor/udsactor.cfg' if DEBUG is False else '/tmp/udsactor.cfg'
def checkPermissions():
return True if DEBUG else os.getuid() == 0
def readConfig():
res = {}
try:
cfg = six.moves.configparser.SafeConfigParser() # @UndefinedVariable
cfg.optionxform = six.text_type
cfg.read(CONFIGFILE)
# Just reads 'uds' section
for key in cfg.options('uds'):
res[key] = cfg.get('uds', key)
if res[key].lower() in ('true', 'yes', 'si'):
res[key] = True
elif res[key].lower() in ('false', 'no'):
res[key] = False
except Exception:
pass
return res
def writeConfig(data):
cfg = six.moves.configparser.SafeConfigParser() # @UndefinedVariable
cfg.optionxform = six.text_type
cfg.add_section('uds')
for key, val in data.items():
cfg.set('uds', key, str(val))
# Ensures exists destination folder
dirname = os.path.dirname(CONFIGFILE)
if not os.path.exists(dirname):
os.mkdir(dirname, mode=0o700) # Will create only if route to path already exists, for example, /etc (that must... :-))
with open(CONFIGFILE, 'w') as f:
cfg.write(f)
os.chmod(CONFIGFILE, 0o0600)

View File

@ -1,91 +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
'''
from __future__ import unicode_literals
import sys
import six
if sys.platform == 'win32':
from udsactor.windows.log import LocalLogger # @UnusedImport
else:
from udsactor.linux.log import LocalLogger # @Reimport
# Valid logging levels, from UDS Broker (uds.core.utils.log)
OTHER, DEBUG, INFO, WARN, ERROR, FATAL = (10000 * (x + 1) for x in six.moves.xrange(6)) # @UndefinedVariable
class Logger(object):
def __init__(self):
self.logLevel = OTHER
self.logger = LocalLogger()
self.remoteLogger = None
def setLevel(self, level):
self.logLevel = int(level) # Ensures level is an integer or fails
self.logger.log(INFO, 'Setting LogLevel to {}'.format(level))
def setRemoteLogger(self, remoteLogger):
self.remoteLogger = remoteLogger
def log(self, level, message):
if level < self.logLevel: # Skip not wanted messages
return
# If remote logger is available, notify message to it
try:
if self.remoteLogger is not None and self.remoteLogger.isConnected:
self.remoteLogger.log(level, message)
except Exception as e:
self.logger.log(FATAL, 'Error notifying log to broker: {}'.format(e.message))
self.logger.log(level, message)
def debug(self, message):
self.log(DEBUG, message)
def warn(self, message):
self.log(WARN, message)
def info(self, message):
self.log(WARN, message)
def error(self, message):
self.log(ERROR, message)
def fatal(self, message):
self.log(FATAL, message)
def flush(self):
pass
logger = Logger()

View File

@ -1,40 +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
'''
# pylint: disable=unused-wildcard-import,wildcard-import
from __future__ import unicode_literals
import sys
if sys.platform == 'win32':
from .windows.operations import * # @UnusedWildImport
else:
from .linux.operations import * # @UnusedWildImport

View File

@ -1,272 +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
'''
from __future__ import unicode_literals
from udsactor.log import logger
from . import operations
from . import store
from . import REST
from . import ipc
from . import httpserver
from .utils import exceptionToMessage
import socket
import time
import random
IPC_PORT = 39188
cfg = None
def initCfg():
global cfg # pylint: disable=global-statement
cfg = store.readConfig()
if logger.logger.isWindows():
# Logs will also go to windows event log for services
logger.logger.serviceLogger = True
if cfg is not None:
logger.setLevel(cfg.get('logLevel', 20000))
else:
logger.setLevel(20000)
class CommonService(object):
def __init__(self):
self.isAlive = True
self.api = None
self.ipc = None
self.httpServer = None
self.rebootRequested = False
self.knownIps = []
socket.setdefaulttimeout(20)
def reboot(self):
self.rebootRequested = True
def setReady(self):
self.api.setReady([(v.mac, v.ip) for v in operations.getNetworkInfo()])
def interactWithBroker(self, scrambledResponses=False):
'''
Returns True to continue to main loop, false to stop & exit service
'''
# If no configuration is found, stop service
if cfg is None:
logger.fatal('No configuration found, stopping service')
return False
self.api = REST.Api(cfg['host'], cfg['masterKey'], cfg['ssl'], scrambledResponses=scrambledResponses)
# Wait for Broker to be ready
counter = 0
while self.isAlive:
try:
# getNetworkInfo is a generator function
netInfo = tuple(operations.getNetworkInfo())
self.knownIps = dict(((i.mac, i.ip) for i in netInfo))
ids = ','.join([i.mac for i in netInfo])
if ids == '':
# Wait for any network interface to be ready
logger.debug('No network interfaces found, retrying in a while...')
raise Exception()
logger.debug('Ids: {}'.format(ids))
self.api.init(ids)
# Set remote logger to notify log info to broker
logger.setRemoteLogger(self.api)
break
except REST.InvalidKeyError:
logger.fatal('Can\'t sync with broker: Invalid broker Master Key')
return False
except REST.UnmanagedHostError:
# Maybe interface that is registered with broker is not enabled already?
# Right now, we thing that the interface connected to broker is
# the interface that broker will know, let's see how this works
logger.fatal('This host is not managed by UDS Broker (ids: {})'.format(ids))
return False # On unmanaged hosts, there is no reason right now to continue running
except Exception as e:
logger.debug('Exception caugh: {}, retrying'.format(exceptionToMessage(e)))
# Any other error is expectable and recoverable, so let's wait a bit and retry again
# but, if too many errors, will log it (one every minute, for
# example)
counter += 1
if counter % 60 == 0: # Every 5 minutes, raise a log
logger.info('Trying to inititialize connection with broker (last error: {})'.format(exceptionToMessage(e)))
# Wait a bit before next check
self.doWait(5000)
# Broker connection is initialized, now get information about what to
# do
counter = 0
while self.isAlive:
try:
logger.debug('Requesting information of what to do now')
info = self.api.information()
data = info.split('\r')
if len(data) != 2:
logger.error('The format of the information message is not correct (got {})'.format(info))
raise Exception
params = data[1].split('\t')
if data[0] == 'rename':
try:
if len(params) == 1: # Simple rename
self.rename(params[0])
# Rename with change password for an user
elif len(params) == 4:
self.rename(params[0], params[1], params[2], params[3])
else:
logger.error('Got invalid parameter for rename operation: {}'.format(params))
return False
break
except Exception as e:
logger.error('Error at computer renaming stage: {}'.format(e.message))
return False
elif data[0] == 'domain':
if len(params) != 5:
logger.error('Got invalid parameters for domain message: {}'.format(params))
return False
self.joinDomain(params[0], params[1], params[2], params[3], params[4])
break
else:
logger.error('Unrecognized action sent from broker: {}'.format(data[0]))
return False # Stop running service
except REST.UserServiceNotFoundError:
logger.error('The host has lost the sync state with broker! (host uuid changed?)')
return False
except Exception:
counter += 1
if counter % 60 == 0:
logger.warn('Too many retries in progress, though still trying (last error: {})'.format(e.message.decode('windows-1250', 'ignore')))
# Any other error is expectable and recoverable, so let's wait
# a bit and retry again
# Wait a bit before next check
self.doWait(5000)
if self.rebootRequested:
try:
operations.reboot()
except Exception as e:
logger.error('Exception on reboot: {}'.format(e.message))
return False # Stops service
return True
def checkIpsChanged(self):
if self.api.uuid is None:
return # Not connected
netInfo = tuple(operations.getNetworkInfo())
for i in netInfo:
# If at least one ip has changed
if i.mac in self.knownIps and self.knownIps[i.mac] != i.ip:
logger.info('Notifying ip change to broker (mac {}, from {} to {})'.format(i.mac, self.knownIps[i.mac], i.ip))
try:
# Notifies all interfaces IPs
self.api.notifyIpChanges(((v.mac, v.ip) for v in netInfo))
# Regenerates Known ips
self.knownIps = dict(((i.mac, i.ip) for i in netInfo))
except Exception as e:
logger.warn('Got an error notifiying IPs to broker: {} (will retry in a bit)'.format(e.message.decode('windows-1250', 'ignore')))
def initIPC(self):
# ******************************************
# * Initialize listener IPC & REST threads *
# ******************************************
logger.debug('Starting IPC listener at {}'.format(IPC_PORT))
self.ipc = ipc.ServerIPC(IPC_PORT)
self.ipc.start()
if self.api.mac in self.knownIps:
address = (self.knownIps[self.api.mac], random.randrange(32000, 64000))
logger.debug('Starting REST listener at {}'.format(address))
self.httpServer = httpserver.HTTPServerThread(address, self.ipc)
self.httpServer.start()
# And notify it to broker
self.api.notifyComm(self.httpServer.getServerUrl())
def endIPC(self):
# Remove IPC threads
if self.ipc is not None:
try:
self.ipc.stop()
except:
logger.error('Couln\'t stop ipc server')
if self.httpServer is not None:
try:
self.httpServer.stop()
except:
logger.error('Couln\'t stop REST server')
def endAPI(self):
if self.api is not None:
try:
self.api.notifyComm(None)
except:
logger.error('Couln\'t remove comms url from broker')
self.notifyStop()
# ***************************************************
# Methods that ARE overriden by linux & windows Actor
# ***************************************************
def rename(self, name, user=None, oldPassword=None, newPassword=None):
'''
Invoked when broker requests a rename action
MUST BE OVERRIDEN
'''
raise NotImplementedError('Method renamed has not been implemented!')
def joinDomain(self, name, domain, ou, account, password):
'''
Invoked when broker requests a "domain" action
MUST BE OVERRIDEN
'''
raise NotImplementedError('Method renamed has not been implemented!')
# ****************************************
# Methods that CAN BE overriden by actors
# ****************************************
def doWait(self, miliseconds):
'''
Invoked to wait a bit
CAN be OVERRIDEN
'''
time.sleep(float(miliseconds) / 1000)
def notifyStop(self):
'''
Overriden to log stop
'''
logger.info('Service is being stopped')

View File

@ -1,39 +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
'''
# pylint: disable=unused-wildcard-import, wildcard-import
from __future__ import unicode_literals
import sys
if sys.platform == 'win32':
from udsactor.windows.store import * # @UnusedWildImport
else:
from udsactor.linux.store import * # @UnusedWildImport

View File

@ -1,72 +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
'''
from __future__ import unicode_literals
import sys
import six
if sys.platform == 'win32':
_fromEncoding = 'windows-1250'
else:
_fromEncoding = 'utf-8'
def toUnicode(msg):
try:
if not isinstance(msg, six.text_type):
if isinstance(msg, six.binary_type):
return msg.decode(_fromEncoding, 'ignore')
return six.text_type(msg)
else:
return msg
except Exception:
try:
return six.text_type(msg)
except Exception:
return ''
def exceptionToMessage(e):
msg = ''
for arg in e.args:
if isinstance(arg, Exception):
msg = msg + exceptionToMessage(arg)
else:
msg = msg + toUnicode(arg) + '. '
return msg
class Bunch(dict):
def __init__(self, **kw):
dict.__init__(self, kw)
self.__dict__ = self

View File

@ -1,139 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2014 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
# _*_ coding: iso-8859-1 _*_
from __future__ import unicode_literals
import win32com.client
import win32com.server.policy
import os
from udsactor.log import logger
# based on python SENS example from
# http://timgolden.me.uk/python/win32_how_do_i/track-session-events.html
# from Sens.h
SENSGUID_PUBLISHER = "{5fee1bd6-5b9b-11d1-8dd2-00aa004abd5e}"
SENSGUID_EVENTCLASS_LOGON = "{d5978630-5b9f-11d1-8dd2-00aa004abd5e}"
# from EventSys.h
PROGID_EventSystem = "EventSystem.EventSystem"
PROGID_EventSubscription = "EventSystem.EventSubscription"
IID_ISensLogon = "{d597bab3-5b9f-11d1-8dd2-00aa004abd5e}"
class SensLogon(win32com.server.policy.DesignatedWrapPolicy):
_com_interfaces_ = [IID_ISensLogon]
_public_methods_ = [
'Logon',
'Logoff',
'StartShell',
'DisplayLock',
'DisplayUnlock',
'StartScreenSaver',
'StopScreenSaver'
]
def __init__(self, api):
self._wrap_(self)
self.api = api
def Logon(self, *args):
logger.debug('Logon event: {}'.format(args))
if self.api is not None and self.api.isConnected:
try:
data = self.api.login(args[0])
logger.debug('Data received for login: {}'.format(data))
data = data.split('\t')
if len(data) == 2:
logger.debug('Data is valid: {}'.format(data))
windir = os.environ['windir']
with open(os.path.join(windir, 'remoteip.txt'), 'w') as f:
f.write(data[0])
with open(os.path.join(windir, 'remoteh.txt'), 'w') as f:
f.write(data[1])
except Exception as e:
logger.fatal('Error notifying logon to server: {}'.format(e))
def Logoff(self, *args):
logger.debug('Logoff event: {}'.format(args))
if self.api is not None and self.api.isConnected:
try:
self.api.logout(args[0])
except Exception as e:
logger.fatal('Error notifying logon to server: {}'.format(e))
def StartShell(self, *args):
logevent('StartShell : %s' % [args])
def DisplayLock(self, *args):
logevent('DisplayLock : %s' % [args])
def DisplayUnlock(self, *args):
logevent('DisplayUnlock : %s' % [args])
def StartScreenSaver(self, *args):
# When finished basic actor, we will use this to provide a new parameter: logout on screensaver
# This will allow to easily close sessions of idle users
logevent('StartScreenSaver : %s' % [args])
def StopScreenSaver(self, *args):
logevent('StopScreenSaver : %s' % [args])
def logevent(msg):
logger.info(msg)
# def register():
# call the CoInitialize to allow the registration to run in an other
# thread
# pythoncom.CoInitialize()
#logevent('Registring ISensLogon')
# sl=SensLogon()
# subscription_interface=pythoncom.WrapObject(sl)
# event_system=win32com.client.Dispatch(PROGID_EventSystem)
# event_subscription=win32com.client.Dispatch(PROGID_EventSubscription)
# event_subscription.EventClassID=SENSGUID_EVENTCLASS_LOGON
# event_subscription.PublisherID=SENSGUID_PUBLISHER
#event_subscription.SubscriptionName='Python subscription'
# event_subscription.SubscriberInterface=subscription_interface
#event_system.Store(PROGID_EventSubscription, event_subscription)
# pythoncom.PumpMessages()
##logevent('ISensLogon stopped')

View File

@ -1,254 +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
'''
from __future__ import unicode_literals
# pylint: disable=unused-wildcard-import, wildcard-import
import win32serviceutil # @UnresolvedImport, pylint: disable=import-error
import win32service # @UnresolvedImport, pylint: disable=import-error
import win32event # @UnresolvedImport, pylint: disable=import-error
import win32com.client # @UnresolvedImport, @UnusedImport, pylint: disable=import-error
import pythoncom # @UnresolvedImport, pylint: disable=import-error
import servicemanager # @UnresolvedImport, pylint: disable=import-error
from udsactor import operations
from udsactor.service import CommonService
from udsactor.service import initCfg
from udsactor.log import logger
from .SENS import SensLogon
from .SENS import logevent
from .SENS import SENSGUID_EVENTCLASS_LOGON
from .SENS import SENSGUID_PUBLISHER
from .SENS import PROGID_EventSubscription
from .SENS import PROGID_EventSystem
class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService):
'''
This class represents a Windows Service for managing actor interactions
with UDS Broker and Machine
'''
_svc_name_ = "UDSActor"
_svc_display_name_ = "UDS Actor Service"
_svc_description_ = "UDS Actor for machines managed by UDS Broker"
# 'System Event Notification' is the SENS service
_svc_deps_ = ['EventLog', 'SENS']
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
CommonService.__init__(self)
self.hWaitStop = win32event.CreateEvent(None, 1, 0, None)
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
self.isAlive = False
win32event.SetEvent(self.hWaitStop)
SvcShutdown = SvcStop
def notifyStop(self):
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STOPPED,
(self._svc_name_, ''))
def doWait(self, miliseconds):
win32event.WaitForSingleObject(self.hWaitStop, miliseconds)
def rename(self, name, user=None, oldPassword=None, newPassword=None):
'''
Renames the computer, and optionally sets a password for an user
before this
'''
hostName = operations.getComputerName()
if hostName.lower() == name.lower():
logger.info('Computer name is now {}'.format(hostName))
self.setReady()
return
# Check for password change request for an user
if user is not None:
logger.info('Setting password for user {}'.format(user))
try:
operations.changeUserPassword(user, oldPassword, newPassword)
except Exception as e:
# We stop here without even renaming computer, because the
# process has failed
raise Exception(
'Could not change password for user {} (maybe invalid current password is configured at broker): {} '.format(user, unicode(e)))
operations.renameComputer(name)
# Reboot just after renaming
logger.info('Rebooting computer got activate new name {}'.format(name))
self.reboot()
def oneStepJoin(self, name, domain, ou, account, password):
'''
Ejecutes the join domain in exactly one step
'''
currName = operations.getComputerName()
# If name is desired, simply execute multiStepJoin, because computer
# name will not change
if currName.lower() == name.lower():
self.multiStepJoin(name, domain, ou, account, password)
else:
operations.renameComputer(name)
logger.debug('Computer renamed to {} without reboot'.format(name))
operations.joinDomain(
domain, ou, account, password, executeInOneStep=True)
logger.debug(
'Requested join domain {} without errors'.format(domain))
self.reboot()
def multiStepJoin(self, name, domain, ou, account, password):
currName = operations.getComputerName()
if currName.lower() == name.lower():
currDomain = operations.getDomainName()
if currDomain is not None and currDomain.lower() == domain.lower():
logger.info(
'Machine {} is part of domain {}'.format(name, domain))
self.setReady()
else:
operations.joinDomain(
domain, ou, account, password, executeInOneStep=False)
else:
operations.renameComputer(name)
logger.info(
'Rebooting computer got activate new name {}'.format(name))
self.reboot()
def joinDomain(self, name, domain, ou, account, password):
ver = operations.getWindowsVersion()
ver = ver[0] * 10 + ver[1]
logger.info('Starting joining domain {} with name {} (detected operating version: {})'.format(
domain, name, ver))
# Accepts one step joinDomain, also remember XP is no more supported by
# microsoft, but this also must works with it because will do a "multi
# step" join
if ver >= 60:
self.oneStepJoin(name, domain, ou, account, password)
else:
self.multiStepJoin(name, domain, ou, account, password)
def SvcDoRun(self):
'''
Main service loop
'''
initCfg()
logger.debug('running SvcDoRun')
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STARTED,
(self._svc_name_, ''))
# call the CoInitialize to allow the registration to run in an other
# thread
logger.debug('Initializing com...')
pythoncom.CoInitialize()
# ********************************************************
# * Ask brokers what to do before proceding to main loop *
# ********************************************************
if self.interactWithBroker(scrambledResponses=True) is False:
logger.debug('Interact with broker returned false, stopping service after a while')
self.notifyStop()
win32event.WaitForSingleObject(self.hWaitStop, 5000)
return
if self.isAlive is False:
logger.debug('The service is not alive after broker interaction, stopping it')
self.notifyStop()
return
if self.rebootRequested is True:
logger.debug('Reboot has been requested, stopping service')
self.notifyStop()
return
self.initIPC()
# ********************************
# * Registers SENS subscriptions *
# ********************************
logevent('Registering ISensLogon')
subscription_guid = '{41099152-498E-11E4-8FD3-10FEED05884B}'
sl = SensLogon(self.api)
subscription_interface = pythoncom.WrapObject(sl)
event_system = win32com.client.Dispatch(PROGID_EventSystem)
event_subscription = win32com.client.Dispatch(PROGID_EventSubscription)
event_subscription.EventClassID = SENSGUID_EVENTCLASS_LOGON
event_subscription.PublisherID = SENSGUID_PUBLISHER
event_subscription.SubscriptionName = 'UDS Actor subscription'
event_subscription.SubscriptionID = subscription_guid
event_subscription.SubscriberInterface = subscription_interface
event_system.Store(PROGID_EventSubscription, event_subscription)
logger.debug('Registered SENS, running main loop')
# *********************
# * Main Service loop *
# *********************
# Counter used to check ip changes only once every 10 seconds, for
# example
counter = 0
while self.isAlive:
counter += 1
# Process SENS messages, This will be a bit asyncronous (1 second
# delay)
pythoncom.PumpWaitingMessages()
if counter % 10 == 0:
self.checkIpsChanged()
# In milliseconds, will break
win32event.WaitForSingleObject(self.hWaitStop, 1000)
logger.debug('Exited main loop, deregistering SENS')
# *******************************************
# * Remove SENS subscription before exiting *
# *******************************************
event_system.Remove(
PROGID_EventSubscription, "SubscriptionID == " + subscription_guid)
self.endIPC() # Ends IPC servers
self.endAPI() # And deinitializes REST api if needed
self.notifyStop()
if __name__ == '__main__':
initCfg()
win32serviceutil.HandleCommandLine(UDSActorSvc)

View File

@ -1,32 +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
'''
from __future__ import unicode_literals

View File

@ -1,77 +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
'''
from __future__ import unicode_literals
import servicemanager # @UnresolvedImport, pylint: disable=import-error
import logging
import os
import tempfile
# Valid logging levels, from UDS Broker (uds.core.utils.log)
OTHER, DEBUG, INFO, WARN, ERROR, FATAL = (10000 * (x + 1) for x in xrange(6))
class LocalLogger(object):
def __init__(self):
# tempdir is different for "user application" and "service"
# service wil get c:\windows\temp, while user will get c:\users\XXX\temp
logging.basicConfig(
filename=os.path.join(tempfile.gettempdir(), 'udsactor.log'),
filemode='a',
format='%(levelname)s %(asctime)s %(message)s',
level=logging.DEBUG
)
self.logger = logging.getLogger('udsactor')
self.serviceLogger = False
def log(self, level, message):
# 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(level / 1000 - 10, message)
if level < INFO or self.serviceLogger is False: # Only information and above will be on event log
return
if level < WARN: # Info
servicemanager.LogInfoMsg(message)
elif level < ERROR: # WARN
servicemanager.LogWarningMsg(message)
else: # Error & Fatal
servicemanager.LogErrorMsg(message)
def isWindows(self):
return True
def isLinux(self):
return False

View File

@ -1,194 +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
'''
from __future__ import unicode_literals
import win32com.client # @UnresolvedImport, pylint: disable=import-error
import win32net # @UnresolvedImport, pylint: disable=import-error
import win32security # @UnresolvedImport, pylint: disable=import-error
import win32api # @UnresolvedImport, pylint: disable=import-error
import win32con # @UnresolvedImport, pylint: disable=import-error
import ctypes
from ctypes.wintypes import DWORD, LPCWSTR
from udsactor import utils
from udsactor.log import logger
def getErrorMessage(res=0):
msg = win32api.FormatMessage(res)
return msg.decode('windows-1250', 'ignore')
def getComputerName():
return win32api.GetComputerNameEx(win32con.ComputerNamePhysicalDnsHostname)
def getNetworkInfo():
obj = win32com.client.Dispatch("WbemScripting.SWbemLocator")
wmobj = obj.ConnectServer("localhost", "root\cimv2")
adapters = wmobj.ExecQuery("Select * from Win32_NetworkAdapterConfiguration where IpEnabled=True")
try:
for obj in adapters:
for ip in obj.IPAddress:
if ':' in ip: # Is IPV6, skip this
continue
if ip == '' or ip is None:
continue
yield utils.Bunch(name=obj.Caption, mac=obj.MACAddress, ip=ip)
except Exception:
return
def getDomainName():
'''
Will return the domain name if we belong a domain, else None
(if part of a network group, will also return None)
'''
# Status:
# 0 = Unknown
# 1 = Unjoined
# 2 = Workgroup
# 3 = Domain
domain, status = win32net.NetGetJoinInformation()
if status != 3:
domain = None
return domain
def getWindowsVersion():
return win32api.GetVersionEx()
EWX_LOGOFF = 0x00000000
EWX_SHUTDOWN = 0x00000001
EWX_REBOOT = 0x00000002
EWX_FORCE = 0x00000004
EWX_POWEROFF = 0x00000008
EWX_FORCEIFHUNG = 0x00000010
def reboot(flags=EWX_FORCEIFHUNG | EWX_REBOOT):
hproc = win32api.GetCurrentProcess()
htok = win32security.OpenProcessToken(hproc, win32security.TOKEN_ADJUST_PRIVILEGES | win32security.TOKEN_QUERY)
privs = ((win32security.LookupPrivilegeValue(None, win32security.SE_SHUTDOWN_NAME), win32security.SE_PRIVILEGE_ENABLED),)
win32security.AdjustTokenPrivileges(htok, 0, privs)
win32api.ExitWindowsEx(flags, 0)
def loggoff():
win32api.ExitWindowsEx(EWX_LOGOFF)
def renameComputer(newName):
# Needs admin privileges to work
if ctypes.windll.kernel32.SetComputerNameExW(DWORD(win32con.ComputerNamePhysicalDnsHostname), LPCWSTR(newName)) == 0:
# win32api.FormatMessage -> returns error string
# win32api.GetLastError -> returns error code
# (just put this comment here to remember to log this when logger is available)
error = getErrorMessage()
computerName = win32api.GetComputerNameEx(win32con.ComputerNamePhysicalDnsHostname)
raise Exception('Error renaming computer from {} to {}: {}'.format(computerName, newName, error))
NETSETUP_JOIN_DOMAIN = 0x00000001
NETSETUP_ACCT_CREATE = 0x00000002
NETSETUP_ACCT_DELETE = 0x00000004
NETSETUP_WIN9X_UPGRADE = 0x00000010
NETSETUP_DOMAIN_JOIN_IF_JOINED = 0x00000020
NETSETUP_JOIN_UNSECURE = 0x00000040
NETSETUP_MACHINE_PWD_PASSED = 0x00000080
NETSETUP_JOIN_WITH_NEW_NAME = 0x00000400
NETSETUP_DEFER_SPN_SET = 0x1000000
def joinDomain(domain, ou, account, password, executeInOneStep=False):
# If account do not have domain, include it
if '@' not in account and '\\' not in account:
if '.' in domain:
account = account + '@' + domain
else:
account = domain + '\\' + account
# Do log
flags = NETSETUP_ACCT_CREATE | NETSETUP_DOMAIN_JOIN_IF_JOINED | NETSETUP_JOIN_DOMAIN
if executeInOneStep:
flags |= NETSETUP_JOIN_WITH_NEW_NAME
flags = DWORD(flags)
domain = LPCWSTR(domain)
# Must be in format "ou=.., ..., dc=...,"
ou = LPCWSTR(ou) if ou is not None and ou != '' else None
account = LPCWSTR(account)
password = LPCWSTR(password)
res = ctypes.windll.netapi32.NetJoinDomain(None, domain, ou, account, password, flags)
# Machine found in another ou, use it and warn this on log
if res == 2224:
flags = DWORD(NETSETUP_DOMAIN_JOIN_IF_JOINED | NETSETUP_JOIN_DOMAIN)
res = ctypes.windll.netapi32.NetJoinDomain(None, domain, None, account, password, flags)
if res != 0:
# Log the error
error = getErrorMessage(res)
print res, error
raise Exception('Error joining domain {}, with credentials {}/*****{}: {}, {}'.format(domain.value, account.value, ', under OU {}'.format(ou.value) if ou.value != None else '', res, error))
def changeUserPassword(user, oldPassword, newPassword):
computerName = LPCWSTR(getComputerName())
user = LPCWSTR(user)
oldPassword = LPCWSTR(oldPassword)
newPassword = LPCWSTR(newPassword)
res = ctypes.windll.netapi32.NetUserChangePassword(computerName, user, oldPassword, newPassword)
if res != 0:
# Log the error, and raise exception to parent
error = getErrorMessage()
raise Exception('Error changing password for user {}: {}'.format(user.value, error))
class LASTINPUTINFO(ctypes.Structure):
_fields_ = [
('cbSize', ctypes.c_uint),
('dwTime', ctypes.c_uint),
]
def getIdleDuration():
lastInputInfo = LASTINPUTINFO()
lastInputInfo.cbSize = ctypes.sizeof(lastInputInfo)
ctypes.windll.user32.GetLastInputInfo(ctypes.byref(lastInputInfo))
millis = ctypes.windll.kernel32.GetTickCount() - lastInputInfo.dwTime
return millis / 1000.0

View File

@ -1,94 +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
'''
from __future__ import unicode_literals
from win32com.shell import shell # @UnresolvedImport, pylint: disable=import-error
import _winreg as wreg # @UnresolvedImport, pylint: disable=import-error
import win32security # @UnresolvedImport, pylint: disable=import-error
import cPickle
# Can be changed to whatever we want, but registry key is protected by permissions
def encoder(data):
return data.encode('bz2')
def decoder(data):
return data.decode('bz2')
DEBUG = True
path = 'Software\\UDSActor'
baseKey = wreg.HKEY_CURRENT_USER if DEBUG is True else wreg.HKEY_LOCAL_MACHINE # @UndefinedVariable
def checkPermissions():
return True if DEBUG else shell.IsUserAnAdmin()
def fixRegistryPermissions(handle):
if DEBUG:
return
# Fix permissions so users can't read this key
v = win32security.GetSecurityInfo(handle, win32security.SE_REGISTRY_KEY, win32security.DACL_SECURITY_INFORMATION)
dacl = v.GetSecurityDescriptorDacl()
n = 0
# Remove all normal users access permissions to the registry key
while n < dacl.GetAceCount():
if unicode(dacl.GetAce(n)[2]) == u'PySID:S-1-5-32-545': # Whell known Users SID
dacl.DeleteAce(n)
else:
n += 1
win32security.SetSecurityInfo(handle, win32security.SE_REGISTRY_KEY,
win32security.DACL_SECURITY_INFORMATION | win32security.PROTECTED_DACL_SECURITY_INFORMATION,
None, None, dacl, None)
def readConfig():
try:
key = wreg.OpenKey(baseKey, path, 0, wreg.KEY_QUERY_VALUE) # @UndefinedVariable
data, _ = wreg.QueryValueEx(key, '') # @UndefinedVariable
wreg.CloseKey(key) # @UndefinedVariable
return cPickle.loads(decoder(data))
except Exception:
return None
def writeConfig(data):
try:
key = wreg.OpenKey(baseKey, path, 0, wreg.KEY_ALL_ACCESS) # @UndefinedVariable
except Exception:
key = wreg.CreateKeyEx(baseKey, path, 0, wreg.KEY_ALL_ACCESS) # @UndefinedVariable
fixRegistryPermissions(key.handle)
wreg.SetValueEx(key, "", 0, wreg.REG_BINARY, encoder(cPickle.dumps(data))) # @UndefinedVariable
wreg.CloseKey(key) # @UndefinedVariable

6
actors/linux/udsactor-daemon Executable file
View File

@ -0,0 +1,6 @@
#!/bin/sh
FOLDER=/usr/share/pyshared/UDSActor
cd $FOLDER
python -m udsactor.linux.UDSActorService $@

View File

@ -44,6 +44,7 @@ from udsactor.linux.daemon import Daemon
from udsactor.linux import renamer
import sys
import prctl
class UDSActorSvc(Daemon, CommonService):
@ -78,6 +79,7 @@ class UDSActorSvc(Daemon, CommonService):
initCfg()
logger.debug('Running Daemon')
prctl.set_proctitle('UDSActorDaemon')
# Linux daemon will continue running unless something is requested to
if self.interactWithBroker() is False:

View File

@ -129,6 +129,9 @@ class Daemon:
except Exception as e:
logger.error('Exception running process: {}'.format(e))
if os.path.exists(self.pidfile):
os.remove(self.pidfile)
def stop(self):
"""
Stop the daemon

View File

@ -51,7 +51,6 @@ class Logger(object):
def setLevel(self, level):
self.logLevel = int(level) # Ensures level is an integer or fails
self.logger.log(INFO, 'Setting LogLevel to {}'.format(level))
def setRemoteLogger(self, remoteLogger):
self.remoteLogger = remoteLogger