From 676447c65a243c49a547f5dfaa08601a08fdd321 Mon Sep 17 00:00:00 2001 From: ramon Date: Fri, 17 Feb 2017 13:40:03 +0000 Subject: [PATCH] =?UTF-8?q?#718:=20Preparar=20OGAgent=20para=20macOS=20a?= =?UTF-8?q?=20partir=20del=20c=C3=B3digo=20de=20Linux.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: https://opengnsys.es/svn/branches/version1.1@5205 a21b9725-9963-47de-94b9-378ad31fedc9 --- macos/Makefile | 72 +++++++++ macos/scripts/OGAgentTool | 6 + macos/scripts/OGAgentTool-startup | 10 ++ macos/scripts/ogagent | 6 + src/opengnsys/macos/__init__.py | 32 ++++ src/opengnsys/macos/operations.py | 254 ++++++++++++++++++++++++++++++ src/opengnsys/operations.py | 11 +- 7 files changed, 388 insertions(+), 3 deletions(-) create mode 100644 macos/Makefile create mode 100644 macos/scripts/OGAgentTool create mode 100644 macos/scripts/OGAgentTool-startup create mode 100644 macos/scripts/ogagent create mode 100644 src/opengnsys/macos/__init__.py create mode 100644 src/opengnsys/macos/operations.py diff --git a/macos/Makefile b/macos/Makefile new file mode 100644 index 0000000..526d710 --- /dev/null +++ b/macos/Makefile @@ -0,0 +1,72 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +# Directories +SOURCEDIR := ../src +DESTDIR := /usr/local +LIBDIR := $(DESTDIR)/share/OGAgent +BINDIR := $(DESTDIR)/bin +SBINDIR = $(DESTDIR)/sbin +#APPSDIR := $(DESTDIR)/usr/share/applications +CFGDIR := $(DESTDIR)/etc/ogagent +#INITDIR := $(DESTDIR)/etc/init.d +#XDGAUTOSTARTDIR := $(DESTDIR)/etc/xdg/autostart +#KDEAUTOSTARTDIR := $(DESTDIR)/usr/share/autostart + +PYC := $(shell find $(SOURCEDIR) -name '*.py[co]') +CACHES := $(shell find $(SOURCEDIR) -name '__pycache__') + +clean: + rm -rf $(PYC) $(CACHES) + +install: + dependencies + install-ogagent + +dependencies: + easy_install pip # Si no existe pip. + # comprobar versión de six, descargar e instalar con easy_install. + easy_install netifaces + +install-ogagent: + mkdir -p $(LIBDIR) + mkdir -p $(BINDIR) + mkdir -p $(SBINDIR) + mkdir -p $(CFGDIR) + + mkdir -p $(LIBDIR)/img + + # Cleans up .pyc and cache folders + rm -f $(PYC) $(CACHES) + + cp -r $(SOURCEDIR)/opengnsys $(LIBDIR)/opengnsys + cp -r $(SOURCEDIR)/cfg $(LIBDIR)/cfg + cp $(SOURCEDIR)/img/oga.png $(LIBDIR)/img + + cp $(SOURCEDIR)/OGAgentUser.py $(LIBDIR) + # QT Dialogs & resources + #cp $(SOURCEDIR)/*_ui.py $(LIBDIR) + #cp $(SOURCEDIR)/OGAgent_rc.py $(LIBDIR) + + # Autostart elements for gnome/kde + #cp desktop/OGAgentTool.desktop $(XDGAUTOSTARTDIR) + #cp desktop/OGAgentTool.desktop $(KDEAUTOSTARTDIR) + + # scripts + cp scripts/ogagent $(BINDIR) + cp scripts/OGAgentTool-startup $(BINDIR) + cp scripts/OGAgentTool $(BINDIR) + + # Fix permissions + chmod 755 $(BINDIR)/ogagent + chmod 755 $(BINDIR)/OGAgentTool-startup + chmod 755 $(LIBDIR)/OGAgentUser.py + chmod 600 $(LIBDIR)/cfg/ogagent.cfg + + ln -fs $(LIBDIR)/cfg/ogagent.cfg $(CFGDIR) + ln -fs $(LIBDIR)/cfg/ogclient.cfg $(CFGDIR) + +uninstall: + rm -rf $(LIBDIR) + rm -f $(BINDIR)/ogagent $(BINDIR)/OGAgent-Tool* + rm -rf $(CFGDIR) diff --git a/macos/scripts/OGAgentTool b/macos/scripts/OGAgentTool new file mode 100644 index 0000000..a285f33 --- /dev/null +++ b/macos/scripts/OGAgentTool @@ -0,0 +1,6 @@ +#!/bin/sh + +FOLDER=/usr/local/share/OGAgent + +cd $FOLDER +python OGAgentUser.py $@ diff --git a/macos/scripts/OGAgentTool-startup b/macos/scripts/OGAgentTool-startup new file mode 100644 index 0000000..db00092 --- /dev/null +++ b/macos/scripts/OGAgentTool-startup @@ -0,0 +1,10 @@ +#!/bin/sh + +# Simple hack to wait for systray to be present +# Exec tool if not already runned by session manager +ps -ef | grep "$USER" | grep -v grep | grep -v OGAgentTool-startup | grep 'OGAgentTool' -q +# If not already running +if [ $? -eq 1 ]; then + sleep 5 + exec /usr/local/bin/OGAgentTool +fi diff --git a/macos/scripts/ogagent b/macos/scripts/ogagent new file mode 100644 index 0000000..5804a4b --- /dev/null +++ b/macos/scripts/ogagent @@ -0,0 +1,6 @@ +#!/bin/sh + +FOLDER=/usr/local/share/OGAgent + +cd $FOLDER +python -m opengnsys.linux.OGAgentService $@ diff --git a/src/opengnsys/macos/__init__.py b/src/opengnsys/macos/__init__.py new file mode 100644 index 0000000..ee5ba4c --- /dev/null +++ b/src/opengnsys/macos/__init__.py @@ -0,0 +1,32 @@ +# -*- 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: Ramón M. Gómez, ramongomez at us dot es +''' +from __future__ import unicode_literals diff --git a/src/opengnsys/macos/operations.py b/src/opengnsys/macos/operations.py new file mode 100644 index 0000000..12e2231 --- /dev/null +++ b/src/opengnsys/macos/operations.py @@ -0,0 +1,254 @@ +# -*- 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 ctypes # @UnusedImport +import ctypes.util +import subprocess +import struct +import array +import six +from opengnsys import utils +import netifaces + + +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: + return netifaces.ifaddress(ifname)[2][0]['addr'] + except Exception: + return None + + +def _getInterfaces(): + ''' + Returns a list of interfaces names + ''' + return netifaces.interfaces() + + +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(): + ''' + Obtains a list of network interfaces + @return: A "generator" of elements, that are dict-as-object, with this elements: + name: Name of the interface + mac: mac of the interface + ip: ip of the interface + ''' + 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 getMacosVersion(): + return 'macOS {}'.format(platform.mac_ver()[0]) + + +def reboot(flags=0): + ''' + Simple reboot using os command + ''' + # Workaround for dummy thread + if six.PY3 is False: + import threading + threading._DummyThread._Thread__stop = lambda x: 42 + + # Check for OpenGnsys Client or GNU/Linux distribution. + if os.path.exists('/scripts/oginit'): + subprocess.call('source /opt/opengnsys/etc/preinit/loadenviron.sh; /opt/opengnsys/scripts/reboot', shell=True) + else: + subprocess.call(['/sbin/reboot']) + +def poweroff(flags=0): + ''' + Simple poweroff using os command + ''' + # Workaround for dummy thread + if six.PY3 is False: + import threading + threading._DummyThread._Thread__stop = lambda x: 42 + + # Check for OpenGnsys Client or GNU/Linux distribution. + if os.path.exists('/scripts/oginit'): + subprocess.call('source /opt/opengnsys/etc/preinit/loadenviron.sh; /opt/opengnsys/scripts/poweroff', shell=True) + else: + subprocess.call(['/sbin/poweroff']) + + + +def logoff(): + ''' + Kills all curent user processes, which must send a logogof + caveat: If the user has other sessions, will also disconnect from them + ''' + # Workaround for dummy thread + if six.PY3 is False: + import threading + threading._DummyThread._Thread__stop = lambda x: 42 + + subprocess.call(['/usr/bin/pkill', '-u', os.environ['USER']]) + + +def renameComputer(newName): + rename(newName) + + +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)) + + +class XScreenSaverInfo(ctypes.Structure): + _fields_ = [('window', ctypes.c_long), + ('state', ctypes.c_int), + ('kind', ctypes.c_int), + ('til_or_since', ctypes.c_ulong), + ('idle', ctypes.c_ulong), + ('eventMask', ctypes.c_ulong)] + +# Initialize xlib & xss +try: + xlibPath = ctypes.util.find_library('X11') + xssPath = ctypes.util.find_library('Xss') + xlib = ctypes.cdll.LoadLibrary(xlibPath) + xss = ctypes.cdll.LoadLibrary(xssPath) + + # Fix result type to XScreenSaverInfo Structure + xss.XScreenSaverQueryExtension.restype = ctypes.c_int + xss.XScreenSaverAllocInfo.restype = ctypes.POINTER(XScreenSaverInfo) # Result in a XScreenSaverInfo structure +except Exception: # Libraries not accesible, not found or whatever.. + xlib = xss = None + + +def initIdleDuration(atLeastSeconds): + ''' + On linux we set the screensaver to at least required seconds, or we never will get "idle" + ''' + # Workaround for dummy thread + if six.PY3 is False: + import threading + threading._DummyThread._Thread__stop = lambda x: 42 + + subprocess.call(['/usr/bin/xset', 's', '{}'.format(atLeastSeconds + 30)]) + # And now reset it + subprocess.call(['/usr/bin/xset', 's', 'reset']) + + +def getIdleDuration(): + ''' + Returns idle duration, in seconds + ''' + if xlib is None or xss is None: + return 0 # Libraries not available + + # production code might want to not hardcode the offset 16... + display = xlib.XOpenDisplay(None) + + event_base = ctypes.c_int() + error_base = ctypes.c_int() + + available = xss.XScreenSaverQueryExtension(display, ctypes.byref(event_base), ctypes.byref(error_base)) + if available != 1: + return 0 # No screen saver is available, no way of getting idle + + info = xss.XScreenSaverAllocInfo() + xss.XScreenSaverQueryInfo(display, xlib.XDefaultRootWindow(display), info) + + if info.contents.state != 0: + return 3600 * 100 * 1000 # If screen saver is active, return a high enough value + + return info.contents.idle / 1000.0 + + +def getCurrentUser(): + ''' + Returns current logged in user + ''' + return os.environ['USER'] + +def showPopup(title, message): + ''' + Displays a message box on user's session (during 1 min). + ''' + return subprocess.call('zenity --info --timeout 60 --title "{}" --text "{}"'.format(title, message), shell=True) + diff --git a/src/opengnsys/operations.py b/src/opengnsys/operations.py index ae90cbe..1b7c050 100644 --- a/src/opengnsys/operations.py +++ b/src/opengnsys/operations.py @@ -40,6 +40,11 @@ if sys.platform == 'win32': osType = 'Windows' osVersion = '.'.join(map(str,getWindowsVersion()[:3]))+' '+getWindowsVersion()[4] else: - from .linux.operations import * # @UnusedWildImport - osType = 'Linux' - osVersion = getLinuxVersion().replace(',','') + if sys.platform == 'darwin': + from .macos.operations import * # @UnusedWildImport + osType = 'MacOS' + osVersion = getMacosVersion().replace(',','') + else: + from .linux.operations import * # @UnusedWildImport + osType = 'Linux' + osVersion = getLinuxVersion().replace(',','')