refs #198 adds ogAdmServer and ogAdmAgent code
parent
ddc8a5faa3
commit
628feff05c
|
@ -0,0 +1,9 @@
|
|||
OpenGnsys Services for Clients README
|
||||
=======================================
|
||||
|
||||
|
||||
Este directorio contiene el código fuente de los servicios OpenGnsys específicos para clientes.
|
||||
|
||||
- ogAdmClient servicio para cliente ogLive que atiende peticiones de OpenGnsys Server
|
||||
- ogagent OGAgent: agente modular para sistemas operativos con API REST
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
# makefile
|
||||
|
||||
# Nombre del proyecto
|
||||
PROYECTO := ogAdmClient
|
||||
|
||||
# Directorios y librerias
|
||||
DIRS :=
|
||||
LIBS := -static
|
||||
|
||||
# Opciones de compilacion
|
||||
OPCS := -m32 -O0 -g -Wall # Depuracion
|
||||
#OPCS := -m32 -O3 -Wall # Optimizacion
|
||||
|
||||
# Ficheros objetos
|
||||
OBJS := sources/ogAdmClient.o
|
||||
|
||||
all: $(PROYECTO)
|
||||
|
||||
$(PROYECTO): $(OBJS)
|
||||
gcc $(OPCS) $(DIRS) $(LIBS) $(OBJS) -o $(PROYECTO)
|
||||
# strip $(PROYECTO) # Optimizacion
|
||||
|
||||
clean:
|
||||
rm -f $(PROYECTO) $(OBJS)
|
||||
|
||||
sources/%.o: sources/%.c
|
||||
gcc $(OPCS) -I ../../Includes -c -o"$@" "$<"
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
ServidorAdm=SERVERIP
|
||||
PUERTO=2008
|
||||
PATHINTERFACE=/opt/opengnsys/interfaceAdm
|
||||
UrlMenu=OPENGNSYSURL/varios/menubrowser.php
|
||||
UrlMsg=http://localhost/cgi-bin/httpd-log.sh
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,183 @@
|
|||
// ********************************************************************************************************
|
||||
// Cliernte: ogAdmClient
|
||||
// Autor: José Manuel Alonso (E.T.S.I.I.) Universidad de Sevilla
|
||||
// Fecha Creación: Marzo-2010
|
||||
// Fecha Última modificación: Marzo-2010
|
||||
// Nombre del fichero: ogAdmClient.h
|
||||
// Descripción :Este fichero implementa el cliente general del sistema
|
||||
// ********************************************************************************************************
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <signal.h>
|
||||
#include "ogAdmLib.h"
|
||||
// ________________________________________________________________________________________________________
|
||||
// Variables globales
|
||||
// ________________________________________________________________________________________________________
|
||||
char *idordenador; // Identificador del ordenador
|
||||
char *nombreordenador; // Nombre del ordenador
|
||||
char *cache; // Tamaño de la caché
|
||||
char *idproautoexec; // Identificador del procedimiento de autoexec
|
||||
char *idcentro; // Identificador de la Unidad Organizativa
|
||||
char *idaula; // Identificador del aula
|
||||
char IPlocal[LONIP]; // Ip local
|
||||
|
||||
char servidoradm[LONPRM]; // Dirección IP del servidor de administración
|
||||
char puerto[LONPRM]; // Puerto de comunicación
|
||||
char pathinterface[LONPRM]; // Path donde está la interface entre la administración y el módulo de clonación
|
||||
|
||||
char interface[LONFUN]; // Nombre del módulo,función o script de la interface con el módulo de administración
|
||||
char parametros[LONSTD]; // Parámetros para la llamada
|
||||
int herror;
|
||||
|
||||
BOOLEAN CMDPTES; // Para bucle de comandos pendientes
|
||||
|
||||
|
||||
char urlmenu[MAXLONURL]; // Url de la pagina de menu para el browser
|
||||
char urlmsg[MAXLONURL]; // Url de la página de mensajed para el browser
|
||||
|
||||
|
||||
typedef struct{ // Estructura usada para referenciar las funciones que procesan las tramas
|
||||
char nf[LONFUN]; // Nombre de la función
|
||||
BOOLEAN (*fptr)(TRAMA*); // Puntero a la función que procesa la trama
|
||||
}MSGFUN;
|
||||
MSGFUN tbfuncionesClient[MAXIMAS_FUNCIONES];
|
||||
// ________________________________________________________________________________________________________
|
||||
// Tabla de errores de la ejecución de los scripts
|
||||
// ________________________________________________________________________________________________________
|
||||
char* tbErroresScripts[]={"Se han generado errores desconocidos. No se puede continuar la ejecución de este módulo",\
|
||||
"001-Formato de ejecución incorrecto.",\
|
||||
"002-Fichero o dispositivo no encontrado",\
|
||||
"003-Error en partición de disco",\
|
||||
"004-Partición o fichero bloqueado",\
|
||||
"005-Error al crear o restaurar una imagen",\
|
||||
"006-Sin sistema operativo",\
|
||||
"007-Programa o función BOOLEAN no ejecutable",\
|
||||
"008-Error en la creación del archivo de eco para consola remota",\
|
||||
"009-Error en la lectura del archivo temporal de intercambio",\
|
||||
"010-Error al ejecutar la llamada a la interface de administración",\
|
||||
"011-La información retornada por la interface de administración excede de la longitud permitida",\
|
||||
"012-Error en el envío de fichero por la red",\
|
||||
"013-Error en la creación del proceso hijo",\
|
||||
"014-Error de escritura en destino",\
|
||||
"015-Sin Cache en el Cliente",\
|
||||
"016-No hay espacio en la cache para almacenar fichero-imagen",\
|
||||
"017-Error al Reducir el Sistema Archivos",\
|
||||
"018-Error al Expandir el Sistema Archivos",\
|
||||
"019-Valor fuera de rango o no válido.",\
|
||||
"020-Sistema de archivos desconocido o no se puede montar",\
|
||||
"021-Error en partición de caché local",\
|
||||
"022-El disco indicado no contiene una particion GPT",\
|
||||
"023-Error no definido",\
|
||||
"024-Error no definido",\
|
||||
"025-Error no definido",\
|
||||
"026-Error no definido",\
|
||||
"027-Error no definido",\
|
||||
"028-Error no definido",\
|
||||
"029-Error no definido",\
|
||||
"030-Error al restaurar imagen - Imagen mas grande que particion",\
|
||||
"031-Error al realizar el comando updateCache",\
|
||||
"032-Error al formatear",\
|
||||
"033-Archivo de imagen corrupto o de otra versión de partclone",\
|
||||
"034-Error no definido",\
|
||||
"035-Error no definido",\
|
||||
"036-Error no definido",\
|
||||
"037-Error no definido",\
|
||||
"038-Error no definido",\
|
||||
"039-Error no definido",\
|
||||
"040-Error imprevisto no definido",\
|
||||
"041-Error no definido",\
|
||||
"042-Error no definido",\
|
||||
"043-Error no definido",\
|
||||
"044-Error no definido",\
|
||||
"045-Error no definido",\
|
||||
"046-Error no definido",\
|
||||
"047-Error no definido",\
|
||||
"048-Error no definido",\
|
||||
"049-Error no definido",\
|
||||
"050-Error en la generación de sintaxis de transferenica unicast",\
|
||||
"051-Error en envio UNICAST de una particion",\
|
||||
"052-Error en envio UNICAST de un fichero",\
|
||||
"053-Error en la recepcion UNICAST de una particion",\
|
||||
"054-Error en la recepcion UNICAST de un fichero",\
|
||||
"055-Error en la generacion de sintaxis de transferenica Multicast",\
|
||||
"056-Error en envio MULTICAST de un fichero",\
|
||||
"057-Error en la recepcion MULTICAST de un fichero",\
|
||||
"058-Error en envio MULTICAST de una particion",\
|
||||
"059-Error en la recepcion MULTICAST de una particion",\
|
||||
"060-Error en la conexion de una sesion UNICAST|MULTICAST con el MASTER",\
|
||||
"061-Error no definido",\
|
||||
"062-Error no definido",\
|
||||
"063-Error no definido",\
|
||||
"064-Error no definido",\
|
||||
"065-Error no definido",\
|
||||
"066-Error no definido",\
|
||||
"067-Error no definido",\
|
||||
"068-Error no definido",\
|
||||
"069-Error no definido",\
|
||||
"070-Error al montar una imagen sincronizada.",\
|
||||
"071-Imagen no sincronizable (es monolitica).",\
|
||||
"072-Error al desmontar la imagen.",\
|
||||
"073-No se detectan diferencias entre la imagen basica y la particion.",\
|
||||
"074-Error al sincronizar, puede afectar la creacion/restauracion de la imagen.",\
|
||||
"Error desconocido "
|
||||
};
|
||||
#define MAXERRORSCRIPT 74 // Error máximo cometido
|
||||
// ________________________________________________________________________________________________________
|
||||
// Prototipo de funciones
|
||||
// ________________________________________________________________________________________________________
|
||||
BOOLEAN autoexecCliente(TRAMA*);
|
||||
BOOLEAN RESPUESTA_AutoexecCliente(TRAMA*);
|
||||
void procesaComandos(TRAMA*);
|
||||
|
||||
BOOLEAN tomaConfiguracion(char*);
|
||||
BOOLEAN tomaIPlocal(void);
|
||||
void scriptLog(const char *,int );
|
||||
|
||||
BOOLEAN gestionaTrama(TRAMA *);
|
||||
BOOLEAN inclusionCliente();
|
||||
char* LeeConfiguracion();
|
||||
BOOLEAN RESPUESTA_InclusionCliente(TRAMA *);
|
||||
|
||||
BOOLEAN comandosPendientes(TRAMA*);
|
||||
BOOLEAN NoComandosPtes(TRAMA *);
|
||||
|
||||
BOOLEAN respuestaEjecucionComando(TRAMA *,int,char*);
|
||||
BOOLEAN Sondeo(TRAMA *);
|
||||
BOOLEAN Actualizar(TRAMA *);
|
||||
int Purgar(TRAMA* );
|
||||
|
||||
BOOLEAN ConsolaRemota(TRAMA*);
|
||||
|
||||
BOOLEAN Arrancar(TRAMA *);
|
||||
BOOLEAN Apagar(TRAMA *);
|
||||
BOOLEAN Reiniciar(TRAMA *);
|
||||
BOOLEAN IniciarSesion(TRAMA *);
|
||||
BOOLEAN CrearImagen(TRAMA *);
|
||||
BOOLEAN CrearImagenBasica(TRAMA *);
|
||||
BOOLEAN CrearSoftIncremental(TRAMA*);
|
||||
|
||||
BOOLEAN InventarioHardware(TRAMA *);
|
||||
BOOLEAN InventariandoSoftware(TRAMA *,BOOLEAN,char*);
|
||||
BOOLEAN EjecutarScript(TRAMA *);
|
||||
BOOLEAN ejecutaArchivo(char*,TRAMA*);
|
||||
|
||||
BOOLEAN cuestionCache(char*);
|
||||
int cargaPaginaWeb(char *);
|
||||
void muestraMenu(void);
|
||||
void muestraMensaje(int idx,char*);
|
||||
|
||||
BOOLEAN enviaMensajeServidor(SOCKET *,TRAMA *,char);
|
||||
|
||||
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,182 @@
|
|||
Format: 1.0
|
||||
Source: ogagent-oglive
|
||||
Binary: ogagent-oglive
|
||||
Architecture: all
|
||||
Version: 3.0.0-20190520
|
||||
Checksums-Md5:
|
||||
7252ce182f6ea1d1c6d569046770cbad 33624 ogagent-oglive_3.0.0-20190520_all.deb
|
||||
Checksums-Sha1:
|
||||
5c1a1984bc7598e33a4271d11954352a77adda50 33624 ogagent-oglive_3.0.0-20190520_all.deb
|
||||
Checksums-Sha256:
|
||||
94624c2751b6178a9476ee57a5f59c98f516cf5795f849383418a4182644dd0d 33624 ogagent-oglive_3.0.0-20190520_all.deb
|
||||
Build-Origin: Ubuntu
|
||||
Build-Architecture: amd64
|
||||
Build-Date: Thu, 14 Dec 2023 08:28:43 +0100
|
||||
Build-Tainted-By:
|
||||
merged-usr-via-aliased-dirs
|
||||
usr-local-has-configs
|
||||
usr-local-has-libraries
|
||||
usr-local-has-programs
|
||||
Installed-Build-Depends:
|
||||
autoconf (= 2.71-2),
|
||||
automake (= 1:1.16.5-1.3),
|
||||
autopoint (= 0.21-4ubuntu4),
|
||||
autotools-dev (= 20220109.1),
|
||||
base-files (= 12ubuntu4.4),
|
||||
base-passwd (= 3.5.52build1),
|
||||
bash (= 5.1-6ubuntu1),
|
||||
binutils (= 2.38-4ubuntu2.4),
|
||||
binutils-common (= 2.38-4ubuntu2.4),
|
||||
binutils-x86-64-linux-gnu (= 2.38-4ubuntu2.4),
|
||||
bsdextrautils (= 2.37.2-4ubuntu3),
|
||||
bsdutils (= 1:2.37.2-4ubuntu3),
|
||||
build-essential (= 12.9ubuntu3),
|
||||
bzip2 (= 1.0.8-5build1),
|
||||
coreutils (= 8.32-4.1ubuntu1),
|
||||
cpp (= 4:11.2.0-1ubuntu1),
|
||||
cpp-11 (= 11.4.0-1ubuntu1~22.04),
|
||||
dash (= 0.5.11+git20210903+057cd650a4ed-3build1),
|
||||
debconf (= 1.5.79ubuntu1),
|
||||
debhelper (= 13.6ubuntu1),
|
||||
debianutils (= 5.5-1ubuntu2),
|
||||
debugedit (= 1:5.0-4build1),
|
||||
dh-autoreconf (= 20),
|
||||
dh-strip-nondeterminism (= 1.13.0-1),
|
||||
diffutils (= 1:3.8-0ubuntu2),
|
||||
dpkg (= 1.21.1ubuntu2.2),
|
||||
dpkg-dev (= 1.21.1ubuntu2.2),
|
||||
dwz (= 0.14-1build2),
|
||||
file (= 1:5.41-3ubuntu0.1),
|
||||
findutils (= 4.8.0-1ubuntu3),
|
||||
g++ (= 4:11.2.0-1ubuntu1),
|
||||
g++-11 (= 11.4.0-1ubuntu1~22.04),
|
||||
gawk (= 1:5.1.0-1ubuntu0.1),
|
||||
gcc (= 4:11.2.0-1ubuntu1),
|
||||
gcc-11 (= 11.4.0-1ubuntu1~22.04),
|
||||
gcc-11-base (= 11.4.0-1ubuntu1~22.04),
|
||||
gcc-12-base (= 12.3.0-1ubuntu1~22.04),
|
||||
gettext (= 0.21-4ubuntu4),
|
||||
gettext-base (= 0.21-4ubuntu4),
|
||||
grep (= 3.7-1build1),
|
||||
groff-base (= 1.22.4-8build1),
|
||||
gzip (= 1.10-4ubuntu4.1),
|
||||
hostname (= 3.23ubuntu2),
|
||||
init-system-helpers (= 1.62),
|
||||
install-info (= 6.8-4build1),
|
||||
intltool-debian (= 0.35.0+20060710.5),
|
||||
libacl1 (= 2.3.1-1),
|
||||
libarchive-zip-perl (= 1.68-1),
|
||||
libasan6 (= 11.4.0-1ubuntu1~22.04),
|
||||
libatomic1 (= 12.3.0-1ubuntu1~22.04),
|
||||
libattr1 (= 1:2.5.1-1build1),
|
||||
libaudit-common (= 1:3.0.7-1build1),
|
||||
libaudit1 (= 1:3.0.7-1build1),
|
||||
libbinutils (= 2.38-4ubuntu2.4),
|
||||
libblkid1 (= 2.37.2-4ubuntu3),
|
||||
libbz2-1.0 (= 1.0.8-5build1),
|
||||
libc-bin (= 2.35-0ubuntu3.5),
|
||||
libc-dev-bin (= 2.35-0ubuntu3.5),
|
||||
libc6 (= 2.35-0ubuntu3.5),
|
||||
libc6-dev (= 2.35-0ubuntu3.5),
|
||||
libcap-ng0 (= 0.7.9-2.2build3),
|
||||
libcap2 (= 1:2.44-1ubuntu0.22.04.1),
|
||||
libcc1-0 (= 12.3.0-1ubuntu1~22.04),
|
||||
libcom-err2 (= 1.46.5-2ubuntu1.1),
|
||||
libcrypt-dev (= 1:4.4.27-1),
|
||||
libcrypt1 (= 1:4.4.27-1),
|
||||
libctf-nobfd0 (= 2.38-4ubuntu2.4),
|
||||
libctf0 (= 2.38-4ubuntu2.4),
|
||||
libdb5.3 (= 5.3.28+dfsg1-0.8ubuntu3),
|
||||
libdebconfclient0 (= 0.261ubuntu1),
|
||||
libdebhelper-perl (= 13.6ubuntu1),
|
||||
libdpkg-perl (= 1.21.1ubuntu2.2),
|
||||
libdw1 (= 0.186-1build1),
|
||||
libelf1 (= 0.186-1build1),
|
||||
libfile-stripnondeterminism-perl (= 1.13.0-1),
|
||||
libgcc-11-dev (= 11.4.0-1ubuntu1~22.04),
|
||||
libgcc-s1 (= 12.3.0-1ubuntu1~22.04),
|
||||
libgcrypt20 (= 1.9.4-3ubuntu3),
|
||||
libgdbm-compat4 (= 1.23-1),
|
||||
libgdbm6 (= 1.23-1),
|
||||
libgmp10 (= 2:6.2.1+dfsg-3ubuntu1),
|
||||
libgomp1 (= 12.3.0-1ubuntu1~22.04),
|
||||
libgpg-error0 (= 1.43-3),
|
||||
libgssapi-krb5-2 (= 1.19.2-2ubuntu0.3),
|
||||
libicu70 (= 70.1-2),
|
||||
libisl23 (= 0.24-2build1),
|
||||
libitm1 (= 12.3.0-1ubuntu1~22.04),
|
||||
libk5crypto3 (= 1.19.2-2ubuntu0.3),
|
||||
libkeyutils1 (= 1.6.1-2ubuntu3),
|
||||
libkrb5-3 (= 1.19.2-2ubuntu0.3),
|
||||
libkrb5support0 (= 1.19.2-2ubuntu0.3),
|
||||
liblsan0 (= 12.3.0-1ubuntu1~22.04),
|
||||
liblz4-1 (= 1.9.3-2build2),
|
||||
liblzma5 (= 5.2.5-2ubuntu1),
|
||||
libmagic-mgc (= 1:5.41-3ubuntu0.1),
|
||||
libmagic1 (= 1:5.41-3ubuntu0.1),
|
||||
libmount1 (= 2.37.2-4ubuntu3),
|
||||
libmpc3 (= 1.2.1-2build1),
|
||||
libmpfr6 (= 4.1.0-3build3),
|
||||
libnsl-dev (= 1.3.0-2build2),
|
||||
libnsl2 (= 1.3.0-2build2),
|
||||
libpam-modules (= 1.4.0-11ubuntu2.3),
|
||||
libpam-modules-bin (= 1.4.0-11ubuntu2.3),
|
||||
libpam-runtime (= 1.4.0-11ubuntu2.3),
|
||||
libpam0g (= 1.4.0-11ubuntu2.3),
|
||||
libpcre2-8-0 (= 10.40-1+ubuntu22.04.1+deb.sury.org+1),
|
||||
libpcre3 (= 2:8.45-1+ubuntu22.04.1+deb.sury.org+1),
|
||||
libperl5.34 (= 5.34.0-3ubuntu1.3),
|
||||
libpipeline1 (= 1.5.5-1),
|
||||
libquadmath0 (= 12.3.0-1ubuntu1~22.04),
|
||||
libreadline8 (= 8.1.2-1),
|
||||
libseccomp2 (= 2.5.3-2ubuntu2),
|
||||
libselinux1 (= 3.3-1build2),
|
||||
libsigsegv2 (= 2.13-1ubuntu3),
|
||||
libsmartcols1 (= 2.37.2-4ubuntu3),
|
||||
libssl3 (= 3.0.2-0ubuntu1.12),
|
||||
libstdc++-11-dev (= 11.4.0-1ubuntu1~22.04),
|
||||
libstdc++6 (= 12.3.0-1ubuntu1~22.04),
|
||||
libsub-override-perl (= 0.09-2),
|
||||
libsystemd0 (= 249.11-0ubuntu3.11),
|
||||
libtinfo6 (= 6.3-2ubuntu0.1),
|
||||
libtirpc-common (= 1.3.2-2ubuntu0.1),
|
||||
libtirpc-dev (= 1.3.2-2ubuntu0.1),
|
||||
libtirpc3 (= 1.3.2-2ubuntu0.1),
|
||||
libtool (= 2.4.6-15build2),
|
||||
libtsan0 (= 11.4.0-1ubuntu1~22.04),
|
||||
libubsan1 (= 12.3.0-1ubuntu1~22.04),
|
||||
libuchardet0 (= 0.0.7-1build2),
|
||||
libudev1 (= 249.11-0ubuntu3.11),
|
||||
libunistring2 (= 1.0-1),
|
||||
libuuid1 (= 2.37.2-4ubuntu3),
|
||||
libxml2 (= 2.9.14+dfsg-0.1+ubuntu22.04.1+deb.sury.org+1),
|
||||
libzstd1 (= 1.4.8+dfsg-3build1),
|
||||
linux-libc-dev (= 5.15.0-91.101),
|
||||
login (= 1:4.8.1-2ubuntu2.1),
|
||||
lsb-base (= 11.1.0ubuntu4),
|
||||
lto-disabled-list (= 24),
|
||||
m4 (= 1.4.18-5ubuntu2),
|
||||
make (= 4.3-4.1build1),
|
||||
man-db (= 2.10.2-1),
|
||||
mawk (= 1.3.4.20200120-3),
|
||||
ncurses-base (= 6.3-2ubuntu0.1),
|
||||
ncurses-bin (= 6.3-2ubuntu0.1),
|
||||
patch (= 2.7.6-7build2),
|
||||
perl (= 5.34.0-3ubuntu1.3),
|
||||
perl-base (= 5.34.0-3ubuntu1.3),
|
||||
perl-modules-5.34 (= 5.34.0-3ubuntu1.3),
|
||||
po-debconf (= 1.0.21+nmu1),
|
||||
readline-common (= 8.1.2-1),
|
||||
rpcsvc-proto (= 1.4.2-0ubuntu6),
|
||||
sed (= 4.8-1ubuntu2),
|
||||
sensible-utils (= 0.0.17),
|
||||
sysvinit-utils (= 3.01-1ubuntu1),
|
||||
tar (= 1.34+dfsg-1ubuntu0.1.22.04.2),
|
||||
util-linux (= 2.37.2-4ubuntu3),
|
||||
xz-utils (= 5.2.5-2ubuntu1),
|
||||
zlib1g (= 1:1.2.11.dfsg-2ubuntu9.2)
|
||||
Environment:
|
||||
DEB_BUILD_OPTIONS="parallel=8"
|
||||
DEB_BUILD_PROFILES="noudeb"
|
||||
LANG="C"
|
||||
SOURCE_DATE_EPOCH="1529319600"
|
|
@ -0,0 +1,26 @@
|
|||
Format: 1.8
|
||||
Date: Mon, 18 Jun 2018 13:00:00 +0200
|
||||
Source: ogagent-oglive
|
||||
Binary: ogagent-oglive
|
||||
Built-For-Profiles: noudeb
|
||||
Architecture: all
|
||||
Version: 3.0.0-20190520
|
||||
Distribution: unstable
|
||||
Urgency: medium
|
||||
Maintainer: Ramón M. Gómez <ramongomez@us.es>
|
||||
Changed-By: Ramón M. Gómez <ramongomez@us.es>
|
||||
Description:
|
||||
ogagent-oglive - OpenGnsys Agent for ogLive client
|
||||
Changes:
|
||||
ogagent-oglive (3.0.0-20190520) unstable; urgency=medium
|
||||
.
|
||||
* OGAgent for ogLive compatible with OpenGnsys 3 web API
|
||||
Checksums-Sha1:
|
||||
5c1a1984bc7598e33a4271d11954352a77adda50 33624 ogagent-oglive_3.0.0-20190520_all.deb
|
||||
ec6e0da3760059612e014f25297cb73e887697cd 5954 ogagent-oglive_3.0.0-20190520_amd64.buildinfo
|
||||
Checksums-Sha256:
|
||||
94624c2751b6178a9476ee57a5f59c98f516cf5795f849383418a4182644dd0d 33624 ogagent-oglive_3.0.0-20190520_all.deb
|
||||
7f2a8b5e25860c53c72b1a438010be022b3bf1c0a05f3562b570f9e940bfd450 5954 ogagent-oglive_3.0.0-20190520_amd64.buildinfo
|
||||
Files:
|
||||
7252ce182f6ea1d1c6d569046770cbad 33624 admin optional ogagent-oglive_3.0.0-20190520_all.deb
|
||||
9f2806400070d5df4bb4c54a010dde70 5954 admin optional ogagent-oglive_3.0.0-20190520_amd64.buildinfo
|
|
@ -0,0 +1 @@
|
|||
./readme.txt
|
|
@ -0,0 +1,2 @@
|
|||
ogagent-oglive_3.0.0-20190520_all.deb admin optional
|
||||
ogagent-oglive_3.0.0-20190520_amd64.buildinfo admin optional
|
|
@ -0,0 +1,14 @@
|
|||
dh_prep
|
||||
dh_installdirs
|
||||
dh_installchangelogs
|
||||
dh_installdocs
|
||||
dh_installdebconf
|
||||
dh_installinit
|
||||
dh_compress
|
||||
dh_link
|
||||
dh_fixperms
|
||||
dh_installdeb
|
||||
dh_shlibdeps
|
||||
dh_gencontrol
|
||||
dh_md5sums
|
||||
dh_builddeb
|
|
@ -0,0 +1,11 @@
|
|||
Package: ogagent-oglive
|
||||
Version: 3.0.0-20190520
|
||||
Architecture: all
|
||||
Maintainer: Ramón M. Gómez <ramongomez@us.es>
|
||||
Installed-Size: 235
|
||||
Depends: python-requests (>= 0.8.2), python-six (>= 1.1), python-prctl (>= 1.1.1), python (>= 2.7), libxss1
|
||||
Section: admin
|
||||
Priority: optional
|
||||
Homepage: https://opengnsys.es
|
||||
Description: OpenGnsys Agent for ogLive client
|
||||
This package provides the required components to allow this machine to work on an environment managed by OpenGnsys.
|
|
@ -0,0 +1,44 @@
|
|||
a645b097e936b35a0f9d667320b97c7a usr/bin/ogagent
|
||||
f1af20ebcb0f9ca24050f5440b280971 usr/share/OGAgent/cfg/ogagent.cfg
|
||||
1a151168042f9f91b0ac9de8d81b1cc9 usr/share/OGAgent/cfg/ogclient.cfg
|
||||
e9b55cf5a2893cb205ece2914ad2cec6 usr/share/OGAgent/opengnsys/RESTApi.py
|
||||
b2145636e0e9f3733a07dc3c864ffba6 usr/share/OGAgent/opengnsys/__init__.py
|
||||
a53a553fabe11b358872eaa6f15e51c7 usr/share/OGAgent/opengnsys/certs.py
|
||||
b6b6d13dffe43f62019489701bd3dceb usr/share/OGAgent/opengnsys/config.py
|
||||
5ddd04ea082b0030b56dbac442817ba2 usr/share/OGAgent/opengnsys/httpserver.py
|
||||
a7ceb4791ea9a1e5074f37c27aaef65b usr/share/OGAgent/opengnsys/ipc.py
|
||||
08005cd7b24449275157d9c560f729c0 usr/share/OGAgent/opengnsys/linux/OGAgentService.py
|
||||
3ef55d64ebda86651c3d882c5c649389 usr/share/OGAgent/opengnsys/linux/__init__.py
|
||||
e5c7da55543002f86d591d243383563a usr/share/OGAgent/opengnsys/linux/daemon.py
|
||||
86e7674bddc118fc1ceb9cd1de7d5912 usr/share/OGAgent/opengnsys/linux/log.py
|
||||
18c89c3f7a9d457f99c1e1bb9e3297dd usr/share/OGAgent/opengnsys/linux/operations.py
|
||||
f287da160c9d182e8a11d86bce27fd69 usr/share/OGAgent/opengnsys/linux/renamer/__init__.py
|
||||
dee0a162a35e6bf8b18e39c4b8c4922c usr/share/OGAgent/opengnsys/linux/renamer/debian.py
|
||||
39cc52a365eac6caec10dbad16d3dceb usr/share/OGAgent/opengnsys/linux/renamer/opensuse.py
|
||||
b97470f7d68208d837659fcd6b910c9f usr/share/OGAgent/opengnsys/linux/renamer/redhat.py
|
||||
f2255a73b7801f9596a9929358d75629 usr/share/OGAgent/opengnsys/loader.py
|
||||
8d648c5a0cbb79e69c3d0eeee8247226 usr/share/OGAgent/opengnsys/log.py
|
||||
cc21354530aecfd1376d0a9efcedc0f5 usr/share/OGAgent/opengnsys/macos/__init__.py
|
||||
5ed4af5272fe5f5e4bd1f5ad591edd3a usr/share/OGAgent/opengnsys/macos/operations.py
|
||||
d41d8cd98f00b204e9800998ecf8427e usr/share/OGAgent/opengnsys/modules/__init__.py
|
||||
5be7cd44bb941107cbe67345982cce22 usr/share/OGAgent/opengnsys/modules/client/OpenGnSys/__init__.py
|
||||
d41d8cd98f00b204e9800998ecf8427e usr/share/OGAgent/opengnsys/modules/client/__init__.py
|
||||
a7903db8bd76249a4e0ee72147542ecd usr/share/OGAgent/opengnsys/modules/server/OpenGnSys/__init__.py
|
||||
d41d8cd98f00b204e9800998ecf8427e usr/share/OGAgent/opengnsys/modules/server/__init__.py
|
||||
3ef55d64ebda86651c3d882c5c649389 usr/share/OGAgent/opengnsys/oglive/__init__.py
|
||||
e5c7da55543002f86d591d243383563a usr/share/OGAgent/opengnsys/oglive/daemon.py
|
||||
a316e2cd56e19eb453215d441f9589aa usr/share/OGAgent/opengnsys/oglive/operations.py
|
||||
7b20385f798d849dd546eb1c25149d76 usr/share/OGAgent/opengnsys/operations.py
|
||||
e5c055043531b106f345f63ffbfb3b44 usr/share/OGAgent/opengnsys/scriptThread.py
|
||||
296542d5654398781ec99c1ec2c0db7f usr/share/OGAgent/opengnsys/service.py
|
||||
68b513e35d67d3e783947ec35b60cfa7 usr/share/OGAgent/opengnsys/utils.py
|
||||
99858b8c43e60816ce3d50888f6bdf21 usr/share/OGAgent/opengnsys/windows/OGAgentService.py
|
||||
5c4822b3e4cef2d5ec2225ad8adcfeed usr/share/OGAgent/opengnsys/windows/__init__.py
|
||||
737d49175b1b4045adfd3bd8a66b66b7 usr/share/OGAgent/opengnsys/windows/log.py
|
||||
f6e9945941b22caf066617abc25b47e2 usr/share/OGAgent/opengnsys/windows/operations.py
|
||||
4eeead8170a612cedd5ac16f043695f8 usr/share/OGAgent/opengnsys/workers/__init__.py
|
||||
7e0279e56a6730c74c4b525916a06f57 usr/share/OGAgent/opengnsys/workers/client_worker.py
|
||||
6e6ff6fc0e398882d8696ae00cf48bb9 usr/share/OGAgent/opengnsys/workers/server_worker.py
|
||||
c25929437b6c40d24ff965ca588dd089 usr/share/doc/ogagent-oglive/changelog.Debian.gz
|
||||
172f344346f66d22a8bf1888b1811c06 usr/share/doc/ogagent-oglive/copyright
|
||||
a7d8603a8cfc9a45e52129a5e5e8664b usr/share/doc/ogagent-oglive/readme.txt
|
|
@ -0,0 +1,21 @@
|
|||
#!/bin/sh
|
||||
|
||||
. /usr/share/debconf/confmodule
|
||||
|
||||
set -e
|
||||
case "$1" in
|
||||
configure)
|
||||
chmod 600 /usr/share/OGAgent/cfg/ogagent.cfg
|
||||
;;
|
||||
abort-upgrade|abort-remove|abort-deconfigure)
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "postinst called with unknown argument \`$1'" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,10 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
. /usr/share/debconf/confmodule
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$1" = "purge" ] ; then
|
||||
rm -rf /usr/share/OGAgent || true > /dev/null 2>&1
|
||||
fi
|
||||
|
|
@ -0,0 +1 @@
|
|||
/usr/share/OGAgent/cfg/ogagent.cfg
|
|
@ -0,0 +1 @@
|
|||
/usr/share/OGAgent/cfg/ogclient.cfg
|
|
@ -0,0 +1,6 @@
|
|||
#!/bin/sh
|
||||
|
||||
FOLDER=/usr/share/OGAgent
|
||||
|
||||
cd $FOLDER
|
||||
python -m opengnsys.linux.OGAgentService $@
|
|
@ -0,0 +1,27 @@
|
|||
[opengnsys]
|
||||
# Listen address & port of REST
|
||||
address=0.0.0.0
|
||||
port=8000
|
||||
|
||||
# This is a comma separated list of paths where to look for modules to load
|
||||
path=test_modules/server
|
||||
|
||||
# Remote OpenGnsys Service (please change IP address)
|
||||
remote=https://192.168.2.10/opengnsys/rest/v3
|
||||
# Alternate OpenGnsys Service (comment out to enable this option)
|
||||
#altremote=https://10.0.2.2/opengnsys/rest
|
||||
|
||||
# Security tokens (please change)
|
||||
client=xxxxx
|
||||
secret=yyyyy
|
||||
|
||||
# Log Level, if ommited, will be set to INFO
|
||||
log=DEBUG
|
||||
|
||||
# Module specific
|
||||
# The sections must match the module name
|
||||
# This section will be passes on activation to module
|
||||
#[Sample1]
|
||||
#value1=Mariete
|
||||
#value2=Yo
|
||||
#remote=https://172.27.0.1:9999/rest
|
|
@ -0,0 +1,11 @@
|
|||
[opengnsys]
|
||||
# Log Level, if ommited, will be set to INFO
|
||||
log=DEBUG
|
||||
|
||||
# Module specific
|
||||
# The sections must match the module name
|
||||
# This section will be passes on activation to module
|
||||
#[Sample1]
|
||||
#value1=Mariete
|
||||
#value2=Yo
|
||||
#remote=https://172.27.0.1:9999/rest
|
|
@ -0,0 +1,196 @@
|
|||
# -*- 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 warnings
|
||||
|
||||
from .log import logger
|
||||
|
||||
from .utils import exceptionToMessage
|
||||
|
||||
VERIFY_CERT = False # Do not check server certificate
|
||||
TIMEOUT = 5 # Connection timout, in seconds
|
||||
|
||||
|
||||
class RESTError(Exception):
|
||||
ERRCODE = 0
|
||||
|
||||
|
||||
class ConnectionError(RESTError):
|
||||
ERRCODE = -1
|
||||
|
||||
|
||||
# Disable warnings log messages
|
||||
try:
|
||||
import urllib3 # @UnusedImport
|
||||
except Exception:
|
||||
from requests.packages import urllib3 # @Reimport
|
||||
|
||||
try:
|
||||
urllib3.disable_warnings() # @UndefinedVariable
|
||||
warnings.simplefilter("ignore")
|
||||
except Exception:
|
||||
pass # In fact, isn't too important, but wil log warns to logging file
|
||||
|
||||
|
||||
class REST(object):
|
||||
"""
|
||||
Simple interface to remote REST apis.
|
||||
The constructor expects the "base url" as parameter, that is, the url that will be common on all REST requests
|
||||
Remember that this is a helper for "easy of use". You can provide your owns using requests lib for example.
|
||||
Examples:
|
||||
v = REST('https://example.com/rest/v1/') (Can omit trailing / if desired)
|
||||
v.sendMessage('hello?param1=1¶m2=2')
|
||||
This will generate a GET message to https://example.com/rest/v1/hello?param1=1¶m2=2, and return the
|
||||
deserialized JSON result or an exception
|
||||
v.sendMessage('hello?param1=1¶m2=2', {'name': 'mario' })
|
||||
This will generate a POST message to https://example.com/rest/v1/hello?param1=1¶m2=2, with json encoded
|
||||
body {'name': 'mario' }, and also returns
|
||||
the deserialized JSON result or raises an exception in case of error
|
||||
"""
|
||||
access_token = None
|
||||
refresh_token = None
|
||||
|
||||
def __init__(self, url):
|
||||
"""
|
||||
Initializes the REST helper
|
||||
url is the full url of the REST API Base, as for example "https://example.com/rest/v1".
|
||||
@param url The url of the REST API Base. The trailing '/' can be included or omitted, as desired.
|
||||
"""
|
||||
self.endpoint = url
|
||||
|
||||
if self.endpoint[-1] != '/':
|
||||
self.endpoint += '/'
|
||||
|
||||
# Some OSs ships very old python requests lib implementations, workaround them...
|
||||
try:
|
||||
self.newerRequestLib = requests.__version__.split('.')[0] >= '1'
|
||||
except Exception:
|
||||
self.newerRequestLib = False # I no version, guess this must be an old requests
|
||||
|
||||
# Disable logging requests messages except for errors, ...
|
||||
logging.getLogger("requests").setLevel(logging.CRITICAL)
|
||||
# Tries to disable all warnings
|
||||
try:
|
||||
warnings.simplefilter("ignore") # Disables all warnings
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _getUrl(self, method):
|
||||
"""
|
||||
Internal method
|
||||
Composes the URL based on "method"
|
||||
@param method: Method to append to base url for composition
|
||||
"""
|
||||
url = self.endpoint + method
|
||||
|
||||
return url
|
||||
|
||||
def _request(self, url, data=None, patch=False):
|
||||
"""
|
||||
Launches the request
|
||||
@param url: The url to obtain
|
||||
@param data: if None, the request will be sent as a GET request. If != None, the request will be sent as a POST,
|
||||
with data serialized as JSON in the body.
|
||||
"""
|
||||
try:
|
||||
# Prepare the header content
|
||||
headers = {'content-type': 'application/json'}
|
||||
if self.access_token is not None:
|
||||
headers['Authorization'] = 'Bearer ' + self.access_token
|
||||
|
||||
if data is None:
|
||||
logger.debug('REST - Requesting using GET (no data provided) {}'.format(url))
|
||||
# Old requests version does not support verify, but it do not checks ssl certificate by default
|
||||
if self.newerRequestLib:
|
||||
r = requests.get(url, verify=VERIFY_CERT, timeout=TIMEOUT)
|
||||
else:
|
||||
r = requests.get(url)
|
||||
else: # POST / PATCH
|
||||
if patch is False:
|
||||
logger.debug('REST - Requesting using POST {}, data: {}'.format(url, data))
|
||||
if self.newerRequestLib:
|
||||
r = requests.post(url, data=data, headers=headers,
|
||||
verify=VERIFY_CERT, timeout=TIMEOUT)
|
||||
else:
|
||||
r = requests.post(url, data=data, headers=headers)
|
||||
else:
|
||||
logger.debug('REST - Requesting using PATCH {}, data: {}'.format(url, data))
|
||||
if self.newerRequestLib:
|
||||
r = requests.patch(url, data=data, headers=headers,
|
||||
verify=VERIFY_CERT, timeout=TIMEOUT)
|
||||
else:
|
||||
r = requests.patch(url, data=data, headers=headers)
|
||||
|
||||
# la respuesta puede venir sin contenido, para ello miramos el codigo devuelto
|
||||
if r.status_code == 204:
|
||||
r = json.loads('{"response": "OK"}')
|
||||
else:
|
||||
r = json.loads(r.content) # Using instead of r.json() to make compatible with old requests lib versions
|
||||
logger.debug("REST - response - " + json.dumps(r))
|
||||
except requests.exceptions.RequestException as e:
|
||||
raise ConnectionError(e)
|
||||
except Exception as e:
|
||||
raise ConnectionError(exceptionToMessage(e))
|
||||
|
||||
return r
|
||||
|
||||
def sendMessage(self, msg, data=None, processData=True, patch=False):
|
||||
"""
|
||||
Sends a message to remote REST server
|
||||
@param data: if None or omitted, message will be a GET, else it will send a POST
|
||||
@param processData: if True, data will be serialized to json before sending, else, data will be sent as "raw"
|
||||
"""
|
||||
logger.debug('Invoking post message {} with data {}'.format(msg, data))
|
||||
|
||||
if processData and data is not None:
|
||||
data = json.dumps(data)
|
||||
|
||||
url = self._getUrl(msg)
|
||||
logger.debug('Requesting {}'.format(url))
|
||||
|
||||
return self._request(url, data, patch)
|
||||
|
||||
def set_authorization_headers(self, access_token = None, refresh_token = None):
|
||||
"""
|
||||
Set access token and refresh token for use in REST Api with authorization headers
|
||||
@param access_token: if None, no authorization headers will be sent in each request, else, and authorization header will be sent
|
||||
@param refresh_token: if not None, when access_token expires, it will be used to obtain new access token
|
||||
"""
|
||||
self.access_token = access_token
|
||||
self.refresh_token = refresh_token
|
|
@ -0,0 +1,60 @@
|
|||
# -*- 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
|
||||
|
||||
# On centos, old six release does not includes byte2int, nor six.PY2
|
||||
import six
|
||||
|
||||
import modules
|
||||
from RESTApi import REST, RESTError
|
||||
|
||||
try:
|
||||
with open('VERSION', 'r') as v:
|
||||
VERSION = v.read()
|
||||
except IOError:
|
||||
VERSION = '1.1.0'
|
||||
|
||||
__title__ = 'OpenGnsys Agent'
|
||||
__version__ = VERSION
|
||||
__build__ = 0x010750
|
||||
__author__ = 'Adolfo Gómez'
|
||||
__license__ = "BSD 3-clause"
|
||||
__copyright__ = "Copyright VirtualCable S.L.U."
|
||||
|
||||
if not hasattr(six, 'byte2int'):
|
||||
if six.PY3:
|
||||
import operator
|
||||
six.byte2int = operator.itemgetter(0)
|
||||
else:
|
||||
def _byte2int(bs):
|
||||
return ord(bs[0])
|
||||
six.byte2int = _byte2int
|
|
@ -0,0 +1,101 @@
|
|||
# -*- 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 = 'OGAgent.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
|
|
@ -0,0 +1,58 @@
|
|||
# -*- 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
|
||||
|
||||
from ConfigParser import SafeConfigParser
|
||||
|
||||
config = None
|
||||
|
||||
def readConfig(client=False):
|
||||
'''
|
||||
Reads configuration file
|
||||
If client is False, will read ogagent.cfg as configuration
|
||||
If client is True, will read ogclient.cfg as configuration
|
||||
|
||||
This is this way so we can protect ogagent.cfg against reading for non admin users on all platforms.
|
||||
'''
|
||||
cfg = SafeConfigParser()
|
||||
if client is True:
|
||||
fname = 'ogclient.cfg'
|
||||
else:
|
||||
fname = 'ogagent.cfg'
|
||||
|
||||
if len(cfg.read('cfg/{}'.format(fname))) == 0:
|
||||
# No configuration found
|
||||
return None
|
||||
|
||||
return cfg
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2015 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, print_function
|
||||
|
||||
# Pydev can't parse "six.moves.xxxx" because it is loaded lazy
|
||||
import six
|
||||
from six.moves.socketserver import ThreadingMixIn # @UnresolvedImport
|
||||
from six.moves.BaseHTTPServer import BaseHTTPRequestHandler # @UnresolvedImport
|
||||
from six.moves.BaseHTTPServer import HTTPServer # @UnresolvedImport
|
||||
from six.moves.urllib.parse import unquote # @UnresolvedImport
|
||||
|
||||
import json
|
||||
import threading
|
||||
import ssl
|
||||
|
||||
from .utils import exceptionToMessage
|
||||
from .certs import createSelfSignedCert
|
||||
from .log import logger
|
||||
|
||||
class HTTPServerHandler(BaseHTTPRequestHandler):
|
||||
service = None
|
||||
protocol_version = 'HTTP/1.0'
|
||||
server_version = 'OpenGnsys Agent Server'
|
||||
sys_version = ''
|
||||
|
||||
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 sendJsonResponse(self, data):
|
||||
self.send_response(200)
|
||||
data = json.dumps(data)
|
||||
self.send_header('Content-type', 'application/json')
|
||||
self.send_header('Content-Length', len(data))
|
||||
self.end_headers()
|
||||
# Send the html message
|
||||
self.wfile.write(data)
|
||||
|
||||
|
||||
# parseURL
|
||||
def parseUrl(self):
|
||||
# Very simple path & params splitter
|
||||
path = self.path.split('?')[0][1:].split('/')
|
||||
|
||||
try:
|
||||
params = dict((v[0], unquote(v[1])) for v in (v.split('=') for v in self.path.split('?')[1].split('&')))
|
||||
except Exception:
|
||||
params = {}
|
||||
|
||||
for v in self.service.modules:
|
||||
if v.name == path[0]: # Case Sensitive!!!!
|
||||
return (v, path[1:], params)
|
||||
|
||||
return (None, path, params)
|
||||
|
||||
def notifyMessage(self, module, path, getParams, postParams):
|
||||
'''
|
||||
Locates witch module will process the message based on path (first folder on url path)
|
||||
'''
|
||||
try:
|
||||
data = module.processServerMessage(path, getParams, postParams, self)
|
||||
self.sendJsonResponse(data)
|
||||
except Exception as e:
|
||||
logger.exception()
|
||||
self.sendJsonError(500, exceptionToMessage(e))
|
||||
|
||||
def do_GET(self):
|
||||
module, path, params = self.parseUrl()
|
||||
|
||||
self.notifyMessage(module, path, params, None)
|
||||
|
||||
def do_POST(self):
|
||||
module, path, getParams = self.parseUrl()
|
||||
|
||||
# Tries to get JSON content (UTF-8 encoded)
|
||||
try:
|
||||
length = int(self.headers.getheader('content-length'))
|
||||
content = self.rfile.read(length).decode('utf-8')
|
||||
logger.debug('length: {}, content >>{}<<'.format(length, content))
|
||||
postParams = json.loads(content)
|
||||
except Exception as e:
|
||||
self.sendJsonError(500, exceptionToMessage(e))
|
||||
|
||||
self.notifyMessage(module, path, getParams, postParams)
|
||||
|
||||
|
||||
def log_error(self, fmt, *args):
|
||||
logger.error('HTTP ' + fmt % args)
|
||||
|
||||
def log_message(self, fmt, *args):
|
||||
logger.info('HTTP ' + fmt % args)
|
||||
|
||||
|
||||
class HTTPThreadingServer(ThreadingMixIn, HTTPServer):
|
||||
pass
|
||||
|
||||
class HTTPServerThread(threading.Thread):
|
||||
def __init__(self, address, service):
|
||||
super(self.__class__, self).__init__()
|
||||
|
||||
HTTPServerHandler.service = service # Keep tracking of service so we can intercact with it
|
||||
|
||||
self.certFile = createSelfSignedCert()
|
||||
self.server = HTTPThreadingServer(address, HTTPServerHandler)
|
||||
self.server.socket = ssl.wrap_socket(self.server.socket, certfile=self.certFile, server_side=True)
|
||||
|
||||
logger.debug('Initialized HTTPS Server thread on {}'.format(address))
|
||||
|
||||
def getServerUrl(self):
|
||||
return 'https://{}:{}/'.format(self.server.server_address[0], self.server.server_address[1])
|
||||
|
||||
def stop(self):
|
||||
self.server.shutdown()
|
||||
|
||||
def run(self):
|
||||
self.server.serve_forever()
|
||||
|
||||
|
|
@ -0,0 +1,423 @@
|
|||
# -*- 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 six
|
||||
import traceback
|
||||
import json
|
||||
|
||||
from opengnsys.utils import toUnicode
|
||||
from opengnsys.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_POPUP title,message Display a popup box with a title
|
||||
# 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
|
||||
|
||||
# Client messages
|
||||
MSG_LOGOFF = 0xA1 # Request log off from an user
|
||||
MSG_MESSAGE = 0xB2
|
||||
MSG_POPUP = 0xB3
|
||||
MSG_SCRIPT = 0xC3
|
||||
|
||||
# Request messages
|
||||
REQ_MESSAGE = 0xD4
|
||||
REQ_POPUP = 0xD5
|
||||
REQ_LOGIN = 0xE5
|
||||
REQ_LOGOUT = 0xF6
|
||||
|
||||
# Reverse msgs dict for debugging
|
||||
REV_DICT = {
|
||||
MSG_LOGOFF: 'MSG_LOGOFF',
|
||||
MSG_MESSAGE: 'MSG_MESSAGE',
|
||||
MSG_POPUP: 'MSG_POPUP',
|
||||
MSG_SCRIPT: 'MSG_SCRIPT',
|
||||
REQ_LOGIN: 'REQ_LOGIN',
|
||||
REQ_LOGOUT: 'REQ_LOGOUT',
|
||||
REQ_MESSAGE: 'REQ_MESSAGE'
|
||||
}
|
||||
|
||||
MAGIC = b'\x4F\x47\x41\x00' # OGA in hexa with a padded 0 to the right
|
||||
|
||||
|
||||
# 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):
|
||||
logger.debug('Got Client message {}={}'.format(msg, REV_DICT.get(msg)))
|
||||
if self.parent.clientMessageProcessor is not None:
|
||||
self.parent.clientMessageProcessor(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'':
|
||||
# Client disconnected
|
||||
self.running = False
|
||||
break
|
||||
buf = six.byte2int(b) # Empty buffer, this is set as non-blocking
|
||||
if state is None:
|
||||
if buf in (REQ_MESSAGE, REQ_LOGIN, REQ_LOGOUT):
|
||||
logger.debug('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_MESSAGE, REQ_LOGIN, REQ_LOGOUT):
|
||||
logger.debug('First length byte is {}'.format(buf))
|
||||
msg_len = buf
|
||||
state = ST_SECOND_BYTE
|
||||
continue
|
||||
elif state == ST_SECOND_BYTE:
|
||||
msg_len += buf << 8
|
||||
logger.debug('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
|
||||
except Exception as e:
|
||||
tb = traceback.format_exc()
|
||||
logger.error('Error: {}, trace: {}'.format(e, tb))
|
||||
|
||||
if self.running is False:
|
||||
break
|
||||
|
||||
try:
|
||||
msg = self.messages.get(block=True, timeout=1)
|
||||
except six.moves.queue.Empty: # No message got in time @UndefinedVariable
|
||||
continue
|
||||
|
||||
logger.debug('Got message {}={}'.format(msg, REV_DICT.get(msg[0])))
|
||||
|
||||
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))
|
||||
|
||||
logger.debug('Client processor stopped')
|
||||
try:
|
||||
self.clientSocket.close()
|
||||
except Exception:
|
||||
pass # If can't close, nothing happens, just end thread
|
||||
|
||||
|
||||
class ServerIPC(threading.Thread):
|
||||
|
||||
def __init__(self, listenPort, clientMessageProcessor=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.clientMessageProcessor = clientMessageProcessor
|
||||
|
||||
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, REV_DICT.get(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 sendPopupMessage(self, title, message):
|
||||
self.sendMessage(MSG_POPUP, {'title':title, '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 processing if thread is mean to stop
|
||||
if self.running is False:
|
||||
break
|
||||
logger.debug('Got connection from {}'.format(address))
|
||||
|
||||
self.cleanupFinishedThreads() # House keeping
|
||||
|
||||
logger.debug('Starting new thread, current: {}'.format(self.threads))
|
||||
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):
|
||||
logger.debug('Sending request for msg: {}({}), {}'.format(msg, REV_DICT.get(msg), data))
|
||||
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 sendLogin(self, username, language):
|
||||
self.sendRequestMessage(REQ_LOGIN, username+','+language)
|
||||
|
||||
def sendLogout(self, username):
|
||||
self.sendRequestMessage(REQ_LOGOUT, username)
|
||||
|
||||
def sendMessage(self, module, message, data=None):
|
||||
'''
|
||||
Sends a message "message" with data (data will be encoded as json, so ensure that it is serializable)
|
||||
@param module: Module that will receive this message
|
||||
@param message: Message to send. This message is "customized", and understand by modules
|
||||
@param data: Data to be send as message companion
|
||||
'''
|
||||
msg = '\0'.join((module, message, json.dumps(data)))
|
||||
self.sendRequestMessage(REQ_MESSAGE, msg)
|
||||
|
||||
def messageReceived(self):
|
||||
'''
|
||||
Override this method to automatically get notified on new message
|
||||
received. Message is at self.messages queue
|
||||
'''
|
||||
pass
|
||||
|
||||
def receiveBytes(self, number):
|
||||
msg = b''
|
||||
while self.running and len(msg) < number:
|
||||
try:
|
||||
buf = self.clientSocket.recv(number - len(msg))
|
||||
if buf == b'':
|
||||
logger.debug('Buf {}, msg {}({})'.format(buf, msg, REV_DICT.get(msg)))
|
||||
self.running = False
|
||||
break
|
||||
msg += buf
|
||||
except socket.timeout:
|
||||
pass
|
||||
|
||||
if self.running is False:
|
||||
logger.debug('Not running, returning None')
|
||||
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) # Static, custom socket timeout of 2 seconds for local connection (no network)
|
||||
|
||||
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
|
||||
|
||||
if self.running is False:
|
||||
break
|
||||
|
||||
# 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 (MSG_LOGOFF, MSG_MESSAGE, MSG_SCRIPT):
|
||||
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:
|
||||
tb = traceback.format_exc()
|
||||
logger.error('Error: {}, trace: {}'.format(e, tb))
|
||||
|
||||
try:
|
||||
self.clientSocket.close()
|
||||
except Exception:
|
||||
pass # If can't close, nothing happens, just end thread
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
# -*- 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 opengnsys.service import CommonService
|
||||
from opengnsys.service import IPC_PORT
|
||||
from opengnsys import ipc
|
||||
|
||||
from opengnsys.log import logger
|
||||
|
||||
from opengnsys.linux.daemon import Daemon
|
||||
|
||||
import sys
|
||||
import signal
|
||||
import json
|
||||
|
||||
try:
|
||||
from prctl import set_proctitle # @UnresolvedImport
|
||||
except Exception: # Platform may not include prctl, so in case it's not available, we let the "name" as is
|
||||
def set_proctitle(_):
|
||||
pass
|
||||
|
||||
|
||||
class OGAgentSvc(Daemon, CommonService):
|
||||
def __init__(self, args=None):
|
||||
Daemon.__init__(self, '/var/run/opengnsys-agent.pid')
|
||||
CommonService.__init__(self)
|
||||
|
||||
def run(self):
|
||||
logger.debug('** Running Daemon **')
|
||||
set_proctitle('OGAgent')
|
||||
|
||||
self.initialize()
|
||||
|
||||
# Call modules initialization
|
||||
# They are called in sequence, no threading is done at this point, so ensure modules onActivate always returns
|
||||
|
||||
|
||||
# *********************
|
||||
# * Main Service loop *
|
||||
# *********************
|
||||
# Counter used to check ip changes only once every 10 seconds, for
|
||||
# example
|
||||
try:
|
||||
while self.isAlive:
|
||||
# In milliseconds, will break
|
||||
self.doWait(1000)
|
||||
except (KeyboardInterrupt, SystemExit) as e:
|
||||
logger.error('Requested exit of main loop')
|
||||
except Exception as e:
|
||||
logger.exception()
|
||||
logger.error('Caught exception on main loop: {}'.format(e))
|
||||
|
||||
self.terminate()
|
||||
|
||||
self.notifyStop()
|
||||
|
||||
def signal_handler(self, signal, frame):
|
||||
self.isAlive = False
|
||||
sys.stderr.write("signal handler: {}".format(signal))
|
||||
|
||||
|
||||
def usage():
|
||||
sys.stderr.write("usage: {} start|stop|restart|fg|login 'username'|logout 'username'|message 'module' 'message' 'json'\n".format(sys.argv[0]))
|
||||
sys.exit(2)
|
||||
|
||||
if __name__ == '__main__':
|
||||
logger.setLevel('INFO')
|
||||
|
||||
if len(sys.argv) == 5 and sys.argv[1] == 'message':
|
||||
logger.debug('Running client opengnsys')
|
||||
client = None
|
||||
try:
|
||||
client = ipc.ClientIPC(IPC_PORT)
|
||||
client.sendMessage(sys.argv[2], sys.argv[3], json.loads(sys.argv[4]))
|
||||
sys.exit(0)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
|
||||
|
||||
if len(sys.argv) == 3 and sys.argv[1] in ('login', 'logout'):
|
||||
logger.debug('Running client opengnsys')
|
||||
client = None
|
||||
try:
|
||||
client = ipc.ClientIPC(IPC_PORT)
|
||||
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)
|
||||
elif len(sys.argv) != 2:
|
||||
usage()
|
||||
|
||||
logger.debug('Executing actor')
|
||||
daemon = OGAgentSvc()
|
||||
|
||||
signal.signal(signal.SIGTERM, daemon.signal_handler)
|
||||
signal.signal(signal.SIGINT, daemon.signal_handler)
|
||||
|
||||
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()
|
||||
elif 'fg' == sys.argv[1]:
|
||||
daemon.run()
|
||||
else:
|
||||
usage()
|
||||
sys.exit(0)
|
||||
else:
|
||||
usage()
|
|
@ -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: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
from __future__ import unicode_literals
|
|
@ -0,0 +1,182 @@
|
|||
# -*- 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 opengnsys.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):
|
||||
try:
|
||||
os.remove(self.pidfile)
|
||||
except Exception:
|
||||
# Not found/not permissions or whatever...
|
||||
pass
|
||||
|
||||
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))
|
||||
|
||||
if os.path.exists(self.pidfile):
|
||||
os.remove(self.pidfile)
|
||||
|
||||
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.info(message)
|
||||
# sys.stderr.write(message)
|
||||
return # not an error in a restart
|
||||
|
||||
# Try killing the daemon process
|
||||
try:
|
||||
for i in range(10):
|
||||
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().
|
||||
"""
|
|
@ -0,0 +1,80 @@
|
|||
# -*- 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, 'opengnsys.log')
|
||||
logging.basicConfig(
|
||||
filename=fname,
|
||||
filemode='a',
|
||||
format='%(levelname)s %(asctime)s %(message)s',
|
||||
level=logging.DEBUG
|
||||
)
|
||||
self.logger = logging.getLogger('opengnsys')
|
||||
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
|
|
@ -0,0 +1,286 @@
|
|||
# -*- 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 locale
|
||||
import ctypes # @UnusedImport
|
||||
import ctypes.util
|
||||
import subprocess
|
||||
import struct
|
||||
import array
|
||||
import six
|
||||
from opengnsys import utils
|
||||
from .renamer import rename
|
||||
|
||||
|
||||
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():
|
||||
'''
|
||||
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 getLinuxVersion():
|
||||
lv = platform.linux_distribution()
|
||||
return lv[0] + ', ' + lv[1]
|
||||
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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 getSessionLanguage():
|
||||
'''
|
||||
Returns the user's session language
|
||||
'''
|
||||
return locale.getdefaultlocale()[0]
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
def get_etc_path():
|
||||
"""
|
||||
:return:
|
||||
Returns etc directory path.
|
||||
"""
|
||||
return os.sep + 'etc'
|
|
@ -0,0 +1,61 @@
|
|||
# -*- 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 opengnsys.log import logger
|
||||
|
||||
renamers = {}
|
||||
|
||||
|
||||
# Renamers now are for IPv4 only addresses
|
||||
def rename(newName):
|
||||
distribution = platform.linux_distribution()[0].lower().strip()
|
||||
if distribution in renamers:
|
||||
return renamers[distribution](newName)
|
||||
|
||||
# Try Debian renamer, simplest one
|
||||
logger.info('Renamer for platform "{0}" not found, tryin debian renamer'.format(distribution))
|
||||
return renamers['debian'](newName)
|
||||
|
||||
|
||||
# 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()
|
|
@ -0,0 +1,68 @@
|
|||
# -*- 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 opengnsys.linux.renamer import renamers
|
||||
from opengnsys.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
|
|
@ -0,0 +1,66 @@
|
|||
# -*- 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 opengnsys.linux.renamer import renamers
|
||||
from opengnsys.log import logger
|
||||
|
||||
import os
|
||||
|
||||
|
||||
def rename(newName):
|
||||
'''
|
||||
RH, Centos, Fedora Renamer
|
||||
Expects new host name on newName
|
||||
Host does not needs to be rebooted after renaming
|
||||
'''
|
||||
logger.debug('using SUSE 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{}\n".format(newName))
|
||||
for l in lines:
|
||||
if l[:9] != '127.0.1.1': # Skips existing 127.0.1.1. if it already exists
|
||||
hosts.write(l)
|
||||
|
||||
return True
|
||||
|
||||
# All names in lower case
|
||||
renamers['opensuse'] = rename
|
||||
renamers['suse'] = rename
|
|
@ -0,0 +1,74 @@
|
|||
# -*- 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 opengnsys.linux.renamer import renamers
|
||||
from opengnsys.log import logger
|
||||
|
||||
import os
|
||||
|
||||
|
||||
def rename(newName):
|
||||
'''
|
||||
RH, Centos, Fedora Renamer
|
||||
Expects new host name on newName
|
||||
Host does not needs to be rebooted after renaming
|
||||
'''
|
||||
logger.debug('using RH 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{}\n".format(newName))
|
||||
for l in lines:
|
||||
if l[:9] != '127.0.1.1': # Skips existing 127.0.1.1. if it already exists
|
||||
hosts.write(l)
|
||||
|
||||
with open('/etc/sysconfig/network', 'r') as net:
|
||||
lines = net.readlines()
|
||||
with open('/etc/sysconfig/network', 'w') as net:
|
||||
net.write('HOSTNAME={}\n'.format(newName))
|
||||
for l in lines:
|
||||
if l[:8] != 'HOSTNAME':
|
||||
net.write(l)
|
||||
|
||||
return True
|
||||
|
||||
# All names in lower case
|
||||
renamers['centos linux'] = rename
|
||||
renamers['fedora'] = rename
|
|
@ -0,0 +1,111 @@
|
|||
# -*- 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
|
||||
|
||||
# This is a simple module loader, so we can add "external opengnsys" modules as addons
|
||||
# Modules under "opengsnsys/modules" are always autoloaded
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import pkgutil
|
||||
import os.path
|
||||
|
||||
from opengnsys.workers import ServerWorker
|
||||
from opengnsys.workers import ClientWorker
|
||||
from .log import logger
|
||||
|
||||
|
||||
def loadModules(controller, client=False):
|
||||
'''
|
||||
Load own provided modules plus the modules that are in the configuration path.
|
||||
The loading order is not defined (they are loaded as found, because modules MUST be "standalone" modules
|
||||
@param service: The service that:
|
||||
* Holds the configuration
|
||||
* Will be used to initialize modules.
|
||||
'''
|
||||
|
||||
ogModules = []
|
||||
|
||||
if client is False:
|
||||
from opengnsys.modules.server import OpenGnSys # @UnusedImport
|
||||
from .modules import server # @UnusedImport, just used to ensure opengnsys modules are initialized
|
||||
modPath = 'opengnsys.modules.server'
|
||||
modType = ServerWorker
|
||||
else:
|
||||
from opengnsys.modules.client import OpenGnSys # @UnusedImport @Reimport
|
||||
from .modules import client # @UnusedImport, just used to ensure opengnsys modules are initialized
|
||||
modPath = 'opengnsys.modules.client'
|
||||
modType = ClientWorker
|
||||
|
||||
def addCls(cls):
|
||||
logger.debug('Found module class {}'.format(cls))
|
||||
try:
|
||||
if cls.name is None:
|
||||
# Error, cls has no name
|
||||
# Log the issue and
|
||||
logger.error('Class {} has no name attribute'.format(cls))
|
||||
return
|
||||
ogModules.append(cls(controller))
|
||||
except Exception as e:
|
||||
logger.error('Error loading module {}'.format(e))
|
||||
|
||||
def recursiveAdd(p):
|
||||
subcls = p.__subclasses__()
|
||||
|
||||
if len(subcls) == 0:
|
||||
addCls(p)
|
||||
else:
|
||||
for c in subcls:
|
||||
recursiveAdd(c)
|
||||
|
||||
def doLoad(paths):
|
||||
for (module_loader, name, ispkg) in pkgutil.iter_modules(paths, modPath + '.'):
|
||||
if ispkg:
|
||||
logger.debug('Found module package {}'.format(name))
|
||||
module_loader.find_module(name).load_module(name)
|
||||
|
||||
|
||||
if controller.config.has_option('opengnsys', 'path') is True:
|
||||
paths = tuple(os.path.abspath(v) for v in controller.config.get('opengnsys', 'path').split(','))
|
||||
else:
|
||||
paths = ()
|
||||
|
||||
# paths += (os.path.dirname(sys.modules[modPath].__file__),)
|
||||
|
||||
logger.debug('Loading modules from {}'.format(paths))
|
||||
|
||||
# Load modules
|
||||
doLoad(paths)
|
||||
|
||||
# Add to list of available modules
|
||||
recursiveAdd(modType)
|
||||
|
||||
return ogModules
|
|
@ -0,0 +1,103 @@
|
|||
# -*- 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 traceback
|
||||
import sys
|
||||
import six
|
||||
|
||||
if sys.platform == 'win32':
|
||||
from opengnsys.windows.log import LocalLogger # @UnusedImport
|
||||
else:
|
||||
from opengnsys.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
|
||||
|
||||
_levelName = {
|
||||
'OTHER': OTHER,
|
||||
'DEBUG': DEBUG,
|
||||
'INFO': INFO,
|
||||
'WARN': WARN,
|
||||
'ERROR': ERROR,
|
||||
'FATAL': FATAL
|
||||
}
|
||||
|
||||
class Logger(object):
|
||||
def __init__(self):
|
||||
self.logLevel = INFO
|
||||
self.logger = LocalLogger()
|
||||
|
||||
def setLevel(self, level):
|
||||
'''
|
||||
Sets log level filter (minimum level required for a log message to be processed)
|
||||
:param level: Any message with a level below this will be filtered out
|
||||
'''
|
||||
if isinstance(level, six.string_types):
|
||||
level = _levelName.get(level, INFO)
|
||||
|
||||
self.logLevel = level # Ensures level is an integer or fails
|
||||
|
||||
def log(self, level, message):
|
||||
if level < self.logLevel: # Skip not wanted messages
|
||||
return
|
||||
|
||||
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(INFO, message)
|
||||
|
||||
def error(self, message):
|
||||
self.log(ERROR, message)
|
||||
|
||||
def fatal(self, message):
|
||||
self.log(FATAL, message)
|
||||
|
||||
def exception(self):
|
||||
try:
|
||||
tb = traceback.format_exc()
|
||||
except Exception:
|
||||
tb = '(could not get traceback!)'
|
||||
|
||||
self.log(DEBUG, tb)
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
|
||||
logger = Logger()
|
|
@ -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
|
|
@ -0,0 +1,263 @@
|
|||
# -*- 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 locale
|
||||
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:
|
||||
return netifaces.ifaddresses(ifname)[18][0]['addr']
|
||||
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.ifaddresses(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 != None and ip != None: # 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 command
|
||||
'''
|
||||
# Workaround for dummy thread
|
||||
if six.PY3 is False:
|
||||
import threading
|
||||
threading._DummyThread._Thread__stop = lambda x: 42
|
||||
|
||||
# Exec reboot command
|
||||
subprocess.call('/sbin/shutdown -r now', shell=True)
|
||||
|
||||
|
||||
def poweroff(flags=0):
|
||||
'''
|
||||
Simple poweroff command
|
||||
'''
|
||||
# Workaround for dummy thread
|
||||
if six.PY3 is False:
|
||||
import threading
|
||||
threading._DummyThread._Thread__stop = lambda x: 42
|
||||
|
||||
# Exec shutdown command
|
||||
subprocess.call('/sbin/shutdown -h now', shell=True)
|
||||
|
||||
|
||||
def logoff():
|
||||
'''
|
||||
Simple logout using AppleScript
|
||||
'''
|
||||
# Workaround for dummy thread
|
||||
if six.PY3 is False:
|
||||
import threading
|
||||
threading._DummyThread._Thread__stop = lambda x: 42
|
||||
|
||||
# Exec logout using AppleSctipt
|
||||
subprocess.call('/usr/bin/osascript -e \'tell app "System Events" to «event aevtrlgo»\'', shell=True)
|
||||
|
||||
|
||||
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 getSessionLanguage():
|
||||
'''
|
||||
Returns the user's session language
|
||||
'''
|
||||
return locale.getdefaultlocale()[0]
|
||||
|
||||
|
||||
def showPopup(title, message):
|
||||
'''
|
||||
Displays a message box on user's session (during 1 min).
|
||||
'''
|
||||
# Show a dialog using AppleSctipt
|
||||
return subprocess.call('/usr/bin/osascript -e \'display notification "{}" with title "{}"\''.format(message, title), shell=True)
|
||||
|
||||
|
||||
def get_etc_path():
|
||||
"""
|
||||
:return:
|
||||
Returns etc directory path.
|
||||
"""
|
||||
return os.sep + 'etc'
|
|
@ -0,0 +1,63 @@
|
|||
# -*- 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
|
||||
|
||||
from opengnsys.workers import ClientWorker
|
||||
|
||||
from opengnsys import operations
|
||||
from opengnsys.log import logger
|
||||
from opengnsys.scriptThread import ScriptExecutorThread
|
||||
|
||||
class OpenGnSysWorker(ClientWorker):
|
||||
name = 'opengnsys'
|
||||
|
||||
def onActivation(self):
|
||||
logger.debug('Activate invoked')
|
||||
|
||||
def onDeactivation(self):
|
||||
logger.debug('Deactivate invoked')
|
||||
|
||||
# Processes script execution
|
||||
def process_script(self, jsonParams):
|
||||
logger.debug('Processed message: script({})'.format(jsonParams))
|
||||
thr = ScriptExecutorThread(jsonParams['code'])
|
||||
thr.start()
|
||||
#self.sendServerMessage('script', {'op', 'launched'})
|
||||
|
||||
def process_logoff(self, jsonParams):
|
||||
logger.debug('Processed message: logoff({})'.format(jsonParams))
|
||||
operations.logoff()
|
||||
|
||||
def process_popup(self, jsonParams):
|
||||
logger.debug('Processed message: popup({})'.format(jsonParams))
|
||||
ret = operations.showPopup(jsonParams['title'], jsonParams['message'])
|
||||
#self.sendServerMessage('popup', {'op', ret})
|
||||
|
|
@ -0,0 +1,558 @@
|
|||
# -*- 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
|
||||
|
||||
import os
|
||||
import random
|
||||
import shutil
|
||||
import string
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
import urllib
|
||||
import signal
|
||||
|
||||
from opengnsys import REST
|
||||
from opengnsys import operations
|
||||
from opengnsys.log import logger
|
||||
from opengnsys.workers import ServerWorker
|
||||
from six.moves.urllib import parse
|
||||
|
||||
|
||||
# Check authorization header decorator
|
||||
def check_secret(fnc):
|
||||
"""
|
||||
Decorator to check for received secret key and raise exception if it isn't valid.
|
||||
"""
|
||||
def wrapper(*args, **kwargs):
|
||||
try:
|
||||
this, path, get_params, post_params, server = args # @UnusedVariable
|
||||
if this.random == server.headers['Authorization']:
|
||||
fnc(*args, **kwargs)
|
||||
else:
|
||||
raise Exception('Unauthorized operation')
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
raise Exception(e)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
# Error handler decorator.
|
||||
def catch_background_error(fnc):
|
||||
def wrapper(*args, **kwargs):
|
||||
this = args[0]
|
||||
try:
|
||||
fnc(*args, **kwargs)
|
||||
except Exception as e:
|
||||
this.REST.sendMessage('error?id={}'.format(kwargs.get('requestId', 'error')), {'error': '{}'.format(e)})
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def check_locked_partition(sync=False):
|
||||
"""
|
||||
Decorator to check if a partition is locked
|
||||
"""
|
||||
|
||||
def outer(fnc):
|
||||
def wrapper(*args, **kwargs):
|
||||
part_id = 'None'
|
||||
try:
|
||||
this, path, get_params, post_params, server = args # @UnusedVariable
|
||||
part_id = post_params['disk'] + post_params['part']
|
||||
if this.locked.get(part_id, False):
|
||||
this.locked[part_id] = True
|
||||
fnc(*args, **kwargs)
|
||||
else:
|
||||
return 'partition locked'
|
||||
except Exception as e:
|
||||
this.locked[part_id] = False
|
||||
return 'error {}'.format(e)
|
||||
finally:
|
||||
if sync is True:
|
||||
this.locked[part_id] = False
|
||||
logger.debug('Lock status: {} {}'.format(fnc, this.locked))
|
||||
|
||||
return wrapper
|
||||
|
||||
return outer
|
||||
|
||||
|
||||
class OpenGnSysWorker(ServerWorker):
|
||||
name = 'opengnsys'
|
||||
interface = None # Bound interface for OpenGnsys
|
||||
REST = None # REST object
|
||||
logged_in = False # User session flag
|
||||
browser = {} # Browser info
|
||||
commands = [] # Running commands
|
||||
random = None # Random string for secure connections
|
||||
length = 32 # Random string length
|
||||
access_token = refresh_token = None # Server authorization tokens
|
||||
grant_type = 'http://opengnsys.es/grants/og_client'
|
||||
|
||||
def _launch_browser(self, url):
|
||||
"""
|
||||
Launchs the Browser with specified URL
|
||||
:param url: URL to show
|
||||
"""
|
||||
logger.debug('Launching browser with URL: {}'.format(url))
|
||||
# Trying to kill an old browser
|
||||
try:
|
||||
os.kill(self.browser['process'].pid, signal.SIGKILL)
|
||||
except OSError:
|
||||
logger.warn('Cannot kill the old browser process')
|
||||
except KeyError:
|
||||
# There is no previous browser
|
||||
pass
|
||||
self.browser['url'] = url
|
||||
self.browser['process'] = subprocess.Popen(['browser', '-qws', url])
|
||||
|
||||
def _task_command(self, route, code, op_id, send_config=False):
|
||||
"""
|
||||
Task to execute a command and return results to a server URI
|
||||
:param route: server callback REST route to return results
|
||||
:param code: code to execute
|
||||
:param op_id: operation id.
|
||||
"""
|
||||
menu_url = ''
|
||||
# Show execution tacking log, if OGAgent runs on ogLive
|
||||
os_type = operations.os_type.lower()
|
||||
if os_type == 'oglive':
|
||||
menu_url = self.browser['url']
|
||||
self._launch_browser('http://localhost/cgi-bin/httpd-log.sh')
|
||||
# Execute the code
|
||||
(stat, out, err) = operations.exec_command(code)
|
||||
# Remove command from the list
|
||||
for c in self.commands:
|
||||
if c.getName() == op_id:
|
||||
self.commands.remove(c)
|
||||
# Remove the REST API prefix, if needed
|
||||
if route.startswith(self.REST.endpoint):
|
||||
route = route[len(self.REST.endpoint):]
|
||||
# Send back exit status and outputs (base64-encoded)
|
||||
self.REST.sendMessage(route, {'mac': self.interface.mac, 'ip': self.interface.ip, 'trace': op_id,
|
||||
'status': stat, 'output': out.encode('base64'), 'error': err.encode('base64')})
|
||||
# Show latest menu, if OGAgent runs on ogLive
|
||||
if os_type == 'oglive':
|
||||
# Send configuration data, if needed
|
||||
if send_config:
|
||||
self.REST.sendMessage('clients/configs', {'mac': self.interface.mac, 'ip': self.interface.ip,
|
||||
'config': operations.get_configuration()})
|
||||
self._launch_browser(menu_url)
|
||||
|
||||
def onActivation(self):
|
||||
"""
|
||||
Sends OGAgent activation notification to OpenGnsys server
|
||||
"""
|
||||
t = 0
|
||||
# Generate random secret to send on activation
|
||||
self.random = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(self.length))
|
||||
# Ensure cfg has required configuration variables or an exception will be thrown
|
||||
url = self.service.config.get('opengnsys', 'remote')
|
||||
server_client = self.service.config.get('opengnsys', 'client')
|
||||
server_secret = self.service.config.get('opengnsys', 'secret')
|
||||
if operations.os_type == 'ogLive' and 'oglive' in os.environ:
|
||||
# Replacing server IP if its running on ogLive client
|
||||
logger.debug('Activating on ogLive client, new server is {}'.format(os.environ['oglive']))
|
||||
url = parse.urlsplit(url)._replace(netloc=os.environ['oglive']).geturl()
|
||||
if not url.endswith(os.path.sep):
|
||||
url += os.path.sep
|
||||
self.REST = REST(url)
|
||||
# Get network interfaces until they are active or timeout (5 minutes)
|
||||
for t in range(0, 300):
|
||||
try:
|
||||
self.interface = list(operations.getNetworkInfo())[0] # Get first network interface
|
||||
except Exception as e:
|
||||
# Wait 1 sec. and retry
|
||||
time.sleep(1)
|
||||
finally:
|
||||
# Exit loop if interface is active
|
||||
if self.interface:
|
||||
if t > 0:
|
||||
logger.debug("Fetch connection data after {} tries".format(t))
|
||||
break
|
||||
# Raise error after timeout
|
||||
if not self.interface:
|
||||
raise e
|
||||
# Compose login route
|
||||
login_route = 'oauth/v2/token?client_id=' + server_client + '&client_secret=' + server_secret + \
|
||||
'&grant_type=' + self.grant_type + '&ip=' + self.interface.ip + '&mac=' + self.interface.mac + \
|
||||
'&token=' + self.random
|
||||
# Send initialization login message
|
||||
response = None
|
||||
# Loop to send initialization message
|
||||
for t in range(0, 100):
|
||||
try:
|
||||
try:
|
||||
# New web compatibility.
|
||||
self.REST = REST(self.service.config.get('opengnsys', 'remote'))
|
||||
response = self.REST.sendMessage(login_route)
|
||||
break
|
||||
except:
|
||||
# Trying to initialize on alternative server, if defined
|
||||
# (used in "exam mode" from the University of Seville)
|
||||
self.REST = REST(self.service.config.get('opengnsys', 'altremote'))
|
||||
response = self.REST.sendMessage(login_route)
|
||||
break
|
||||
except:
|
||||
time.sleep(3)
|
||||
# Raise error after timeout or authentication failure
|
||||
if 0 < t < 100:
|
||||
logger.debug('Successful connection after {} tries'.format(t))
|
||||
elif t == 100:
|
||||
raise Exception('Initialization error: Cannot connect to remote server')
|
||||
if response['access_token'] is None:
|
||||
raise Exception('Initialization error: Cannot obtain access token')
|
||||
# Read access tokens
|
||||
self.access_token = response['access_token']
|
||||
self.refresh_token = response['refresh_token']
|
||||
# Once authenticated with the server, change the API URL for private request
|
||||
self.REST = REST(url + 'api/private')
|
||||
# Set authorization tokens in the REST object, so in each request this token will be used
|
||||
self.REST.set_authorization_headers(self.access_token, self.refresh_token)
|
||||
# Completing OGAgent initialization process
|
||||
os_type = operations.os_type.lower()
|
||||
if os_type == 'oglive':
|
||||
# Create HTML file (TEMPORARY)
|
||||
message = """
|
||||
<html>
|
||||
<head></head>
|
||||
<style>
|
||||
#barra { width: 20px; height: 10px; position: relative; background: darkslategrey; }
|
||||
</style>
|
||||
<body>
|
||||
<h1 style="margin: 5em 0 0 5em; font-size: 250%; color: darkslategrey;">
|
||||
<span id="opengnsys"><span style="font-weight: lighter;">Open</span>Gnsys 3</div>
|
||||
<div id="barra"></span>
|
||||
</h1>
|
||||
<script>
|
||||
var elem = document.getElementById("barra");
|
||||
var max = document.getElementById("opengnsys").offsetWidth;
|
||||
var pos = 0;
|
||||
var inc = true;
|
||||
var id = setInterval(frame, 5);
|
||||
function frame() {
|
||||
if (inc) {
|
||||
if (pos == max - 20) { inc = false; } else { pos++; }
|
||||
} else {
|
||||
if (pos == 0) { inc = true; } else { pos--; }
|
||||
}
|
||||
elem.style.left = pos + 'px';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>"""
|
||||
f = open('/tmp/init.html', 'w')
|
||||
f.write(message)
|
||||
f.close()
|
||||
# Launching Browser
|
||||
self._launch_browser('/tmp/init.html')
|
||||
self.REST.sendMessage('clients/statuses', {'mac': self.interface.mac, 'ip': self.interface.ip,
|
||||
'status': 'initializing'})
|
||||
# Send configuration message
|
||||
self.REST.sendMessage('clients/configs', {'mac': self.interface.mac, 'ip': self.interface.ip,
|
||||
'config': operations.get_configuration()})
|
||||
# Launching new Browser with client's menu
|
||||
# menu_url = self.REST.sendMessage('menus?mac' + self.interface.mac + '&ip=' + self.interface.ip)
|
||||
menu_url = '/opt/opengnsys/log/' + self.interface.ip + '.info.html' # TEMPORARY menu
|
||||
self._launch_browser(menu_url)
|
||||
else:
|
||||
# Delete marking files
|
||||
for f in ['ogboot.me', 'ogboot.firstboot', 'ogboot.secondboot']:
|
||||
try:
|
||||
os.remove(os.sep + f)
|
||||
except OSError:
|
||||
pass
|
||||
# Copy file "HostsFile.FirstOctetOfIPAddress" to "HostsFile", if it exists
|
||||
# (used in "exam mode" from the University of Seville)
|
||||
hosts_file = os.path.join(operations.get_etc_path(), 'hosts')
|
||||
new_file = hosts_file + '.' + self.interface.ip.split('.')[0]
|
||||
if os.path.isfile(new_file):
|
||||
shutil.copy2(new_file, hosts_file)
|
||||
# Return status message
|
||||
self.REST.sendMessage('clients/statuses', {'mac': self.interface.mac, 'ip': self.interface.ip,
|
||||
'status': os_type})
|
||||
|
||||
def onDeactivation(self):
|
||||
"""
|
||||
Sends OGAgent stopping notification to OpenGnsys server
|
||||
"""
|
||||
logger.debug('onDeactivation')
|
||||
self.REST.sendMessage('clients/statuses', {'mac': self.interface.mac, 'ip': self.interface.ip,
|
||||
'ostype': operations.os_type, 'osversion': operations.os_version,
|
||||
'status': 'off'})
|
||||
|
||||
def processClientMessage(self, message, data):
|
||||
logger.debug('Got OpenGnsys message from client: {}, data {}'.format(message, data))
|
||||
|
||||
def onLogin(self, data):
|
||||
"""
|
||||
Sends session login notification to OpenGnsys server
|
||||
"""
|
||||
user, sep, language = data.partition(',')
|
||||
logger.debug('Received login for {} with language {}'.format(user, language))
|
||||
self.logged_in = True
|
||||
self.REST.sendMessage('ogagent/loggedin', {'ip': self.interface.ip, 'user': user, 'language': language,
|
||||
'ostype': operations.os_type, 'osversion': operations.os_version})
|
||||
|
||||
def onLogout(self, user):
|
||||
"""
|
||||
Sends session logout notification to OpenGnsys server
|
||||
"""
|
||||
logger.debug('Received logout for {}'.format(user))
|
||||
self.logged_in = False
|
||||
self.REST.sendMessage('ogagent/loggedout', {'ip': self.interface.ip, 'user': user})
|
||||
|
||||
def process_ogclient(self, path, get_params, post_params, server):
|
||||
"""
|
||||
This method can be overridden to provide your own message processor, or better you can
|
||||
implement a method that is called exactly as "process_" + path[0] (module name has been removed from path
|
||||
array) and this default processMessage will invoke it
|
||||
* Example:
|
||||
Imagine this invocation url (no matter if GET or POST): http://example.com:9999/Sample/mazinger/Z
|
||||
The HTTP Server will remove "Sample" from path, parse arguments and invoke this method as this:
|
||||
module.processMessage(["mazinger","Z"], get_params, post_params)
|
||||
|
||||
This method will process "mazinger", and look for a "self" method that is called "process_mazinger",
|
||||
and invoke it this way:
|
||||
return self.process_mazinger(["Z"], get_params, post_params)
|
||||
|
||||
In the case path is empty (that is, the path is composed only by the module name, like in
|
||||
"http://example.com/Sample", the "process" method will be invoked directly
|
||||
|
||||
The methods must return data that can be serialized to json (i.e. Objects are not serializable to json,
|
||||
basic type are)
|
||||
"""
|
||||
if not path:
|
||||
return "ok"
|
||||
try:
|
||||
operation = getattr(self, 'ogclient_' + path[0])
|
||||
except Exception:
|
||||
raise Exception('Message processor for "{}" not found'.format(path[0]))
|
||||
return operation(path[1:], get_params, post_params)
|
||||
|
||||
def process_status(self, path, get_params, post_params, server):
|
||||
"""
|
||||
Returns client status (OS type or execution status) and login status
|
||||
:param path:
|
||||
:param get_params:
|
||||
:param post_params:
|
||||
:param server:
|
||||
:return: JSON object {"status": "status_code", "loggedin": boolean}
|
||||
"""
|
||||
res = {'loggedin': self.logged_in}
|
||||
try:
|
||||
res['status'] = operations.os_type.lower()
|
||||
except KeyError:
|
||||
res['status'] = ''
|
||||
# Check if OpenGnsys Client is busy
|
||||
if res['status'] == 'oglive' and len(self.commands) > 0:
|
||||
res['status'] = 'busy'
|
||||
return res
|
||||
|
||||
@check_secret
|
||||
def process_reboot(self, path, get_params, post_params, server):
|
||||
"""
|
||||
Launches a system reboot operation
|
||||
:param path:
|
||||
:param get_params:
|
||||
:param post_params:
|
||||
:param server: authorization header
|
||||
:return: JSON object {"op": "launched"}
|
||||
"""
|
||||
logger.debug('Received reboot operation')
|
||||
|
||||
# Rebooting thread
|
||||
def rebt():
|
||||
operations.reboot()
|
||||
|
||||
threading.Thread(target=rebt).start()
|
||||
return {'op': 'launched'}
|
||||
|
||||
@check_secret
|
||||
def process_poweroff(self, path, get_params, post_params, server):
|
||||
"""
|
||||
Launches a system power off operation
|
||||
:param path:
|
||||
:param get_params:
|
||||
:param post_params:
|
||||
:param server: authorization header
|
||||
:return: JSON object {"op": "launched"}
|
||||
"""
|
||||
logger.debug('Received poweroff operation')
|
||||
|
||||
# Powering off thread
|
||||
def pwoff():
|
||||
time.sleep(2)
|
||||
operations.poweroff()
|
||||
|
||||
threading.Thread(target=pwoff).start()
|
||||
return {'op': 'launched'}
|
||||
|
||||
@check_secret
|
||||
def process_script(self, path, get_params, post_params, server):
|
||||
"""
|
||||
Processes an script execution (script should be encoded in base64)
|
||||
:param path:
|
||||
:param get_params:
|
||||
:param post_params: JSON object {"redirect_uri, "uri", "script": "commands", "id": trace_id}
|
||||
:param server: authorization header
|
||||
:return: JSON object {"op": "launched"} or {"error": "message"}
|
||||
"""
|
||||
logger.debug('Processing script request')
|
||||
# Processing data
|
||||
try:
|
||||
script = urllib.unquote(post_params.get('script').decode('base64')).decode('utf8')
|
||||
op_id = post_params.get('id')
|
||||
route = post_params.get('redirectUri')
|
||||
send_config = (post_params.get('sendConfig', 'false') == 'true')
|
||||
# Check if the thread id. exists
|
||||
for c in self.commands:
|
||||
if c.getName() == str(op_id):
|
||||
raise Exception('Task id. already exists: {}'.format(op_id))
|
||||
if post_params.get('client', 'false') == 'false':
|
||||
# Launching a new thread
|
||||
thr = threading.Thread(name=op_id, target=self._task_command, args=(route, script, op_id, send_config))
|
||||
thr.start()
|
||||
self.commands.append(thr)
|
||||
else:
|
||||
# Executing as normal user
|
||||
self.sendClientMessage('script', {'code': script})
|
||||
except Exception as e:
|
||||
logger.error('Got exception {}'.format(e))
|
||||
return {'error': e}
|
||||
return {'op': 'launched'}
|
||||
|
||||
@check_secret
|
||||
def process_logoff(self, path, get_params, post_params, server):
|
||||
"""
|
||||
Closes user session
|
||||
"""
|
||||
logger.debug('Received logoff operation')
|
||||
# Send log off message to OGAgent client
|
||||
self.sendClientMessage('logoff', {})
|
||||
return {'op': 'sent to client'}
|
||||
|
||||
@check_secret
|
||||
def process_popup(self, path, get_params, post_params, server):
|
||||
"""
|
||||
Shows a message popup on the user's session
|
||||
"""
|
||||
logger.debug('Received message operation')
|
||||
# Send popup message to OGAgent client
|
||||
self.sendClientMessage('popup', post_params)
|
||||
return {'op': 'launched'}
|
||||
|
||||
def process_client_popup(self, params):
|
||||
self.REST.sendMessage('popup_done', params)
|
||||
|
||||
@check_secret
|
||||
def process_config(self, path, get_params, post_params, server):
|
||||
"""
|
||||
Returns client configuration
|
||||
:param path:
|
||||
:param get_params:
|
||||
:param post_params:
|
||||
:param server: authorization header
|
||||
:return: JSON object
|
||||
"""
|
||||
serialno = '' # Serial number
|
||||
storage = [] # Storage configuration
|
||||
warnings = 0 # Number of warnings
|
||||
logger.debug('Received getconfig operation')
|
||||
# Processing data
|
||||
for row in operations.get_configuration().split(';'):
|
||||
cols = row.split(':')
|
||||
if len(cols) == 1:
|
||||
if cols[0] != '':
|
||||
# Serial number
|
||||
serialno = cols[0]
|
||||
else:
|
||||
# Skip blank rows
|
||||
pass
|
||||
elif len(cols) == 7:
|
||||
disk, npart, tpart, fs, opsys, size, usage = cols
|
||||
try:
|
||||
if int(npart) == 0:
|
||||
# Disk information
|
||||
storage.append({'disk': int(disk), 'parttable': int(tpart), 'size': int(size)})
|
||||
else:
|
||||
# Partition information
|
||||
storage.append({'disk': int(disk), 'partition': int(npart), 'parttype': tpart,
|
||||
'filesystem': fs, 'operatingsystem': opsys, 'size': int(size),
|
||||
'usage': int(usage)})
|
||||
except ValueError:
|
||||
logger.warn('Configuration parameter error: {}'.format(cols))
|
||||
warnings += 1
|
||||
else:
|
||||
# Log warnings
|
||||
logger.warn('Configuration data error: {}'.format(cols))
|
||||
warnings += 1
|
||||
# Return configuration data and count of warnings
|
||||
return {'serialno': serialno, 'storage': storage, 'warnings': warnings}
|
||||
|
||||
@check_secret
|
||||
def process_execinfo(self, path, get_params, post_params, server):
|
||||
"""
|
||||
Returns running commands information
|
||||
:param path:
|
||||
:param get_params:
|
||||
:param post_params:
|
||||
:param server: authorization header
|
||||
:return: JSON array: [["callback_url", "commands", trace_id], ...]
|
||||
"""
|
||||
data = []
|
||||
logger.debug('Received execinfo operation')
|
||||
# Return the arguments of all running threads
|
||||
for c in self.commands:
|
||||
if c.is_alive():
|
||||
data.append(c.__dict__['_Thread__args'])
|
||||
return data
|
||||
|
||||
@check_secret
|
||||
def process_stopcmd(self, path, get_params, post_params, server):
|
||||
"""
|
||||
Stops a running process identified by its trace id.
|
||||
:param path:
|
||||
:param get_params:
|
||||
:param post_params: JSON object {"trace": trace_id}
|
||||
:param server: authorization header
|
||||
:return: JSON object: {"stopped": trace_id}
|
||||
"""
|
||||
logger.debug('Received stopcmd operation with params {}:'.format(post_params))
|
||||
# Find operation id. and stop the thread
|
||||
op_id = post_params.get('trace')
|
||||
for c in self.commands:
|
||||
if c.is_alive() and c.getName() == str(op_id):
|
||||
c._Thread__stop()
|
||||
return {"stopped": op_id}
|
||||
return {}
|
|
@ -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: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
from __future__ import unicode_literals
|
|
@ -0,0 +1,182 @@
|
|||
# -*- 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 opengnsys.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):
|
||||
try:
|
||||
os.remove(self.pidfile)
|
||||
except Exception:
|
||||
# Not found/not permissions or whatever...
|
||||
pass
|
||||
|
||||
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))
|
||||
|
||||
if os.path.exists(self.pidfile):
|
||||
os.remove(self.pidfile)
|
||||
|
||||
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.info(message)
|
||||
# sys.stderr.write(message)
|
||||
return # not an error in a restart
|
||||
|
||||
# Try killing the daemon process
|
||||
try:
|
||||
for i in range(10):
|
||||
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().
|
||||
"""
|
|
@ -0,0 +1,258 @@
|
|||
# -*- 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
|
||||
|
||||
import socket
|
||||
import platform
|
||||
import os
|
||||
import fcntl
|
||||
import subprocess
|
||||
import struct
|
||||
import array
|
||||
import six
|
||||
import chardet
|
||||
from opengnsys import utils
|
||||
from opengnsys.log import logger
|
||||
|
||||
|
||||
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 _exec_ogcommand(ogcmd):
|
||||
"""
|
||||
Loads OpenGnsys environment variables, executes the command and returns the result
|
||||
"""
|
||||
ret = subprocess.check_output(ogcmd, shell=True)
|
||||
return ret
|
||||
|
||||
|
||||
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 get_oglive_version():
|
||||
"""
|
||||
Returns ogLive Kernel version and architecture
|
||||
:return: kernel version
|
||||
"""
|
||||
kv = platform.os.uname()
|
||||
return kv[2] + ', ' + kv[4]
|
||||
|
||||
|
||||
def reboot():
|
||||
"""
|
||||
Simple reboot using OpenGnsys script
|
||||
"""
|
||||
# Workaround for dummy thread
|
||||
if six.PY3 is False:
|
||||
import threading
|
||||
threading._DummyThread._Thread__stop = lambda x: 42
|
||||
|
||||
_exec_ogcommand('/opt/opengnsys/scripts/reboot')
|
||||
|
||||
|
||||
def poweroff():
|
||||
"""
|
||||
Simple power off using OpenGnsys script
|
||||
"""
|
||||
# Workaround for dummy thread
|
||||
if six.PY3 is False:
|
||||
import threading
|
||||
threading._DummyThread._Thread__stop = lambda x: 42
|
||||
|
||||
_exec_ogcommand('/opt/opengnsys/scripts/poweroff')
|
||||
|
||||
|
||||
def get_etc_path():
|
||||
"""
|
||||
Returns etc directory path.
|
||||
"""
|
||||
return os.sep + 'etc'
|
||||
|
||||
|
||||
def get_configuration():
|
||||
"""
|
||||
Returns client's configuration
|
||||
Warning: this operation may take some time
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
_exec_ogcommand('/opt/opengnsys/interfaceAdm/getConfiguration')
|
||||
# Returns content of configuration file
|
||||
cfgdata = open('/tmp/getconfig', 'r').read().strip()
|
||||
except IOError:
|
||||
cfgdata = ''
|
||||
return cfgdata
|
||||
|
||||
|
||||
def exec_command(cmd):
|
||||
"""
|
||||
Executing a shell command
|
||||
:param cmd:
|
||||
:return: object with components:
|
||||
output: standard output
|
||||
error: error output
|
||||
exit: exit code
|
||||
"""
|
||||
proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
(out, err) = proc.communicate()
|
||||
try:
|
||||
if out is not None:
|
||||
encoding = chardet.detect(out)["encoding"]
|
||||
if encoding is not None:
|
||||
out = out.decode(encoding).encode("utf8")
|
||||
if err is not None:
|
||||
encoding = chardet.detect(err)["encoding"]
|
||||
if encoding is not None:
|
||||
err = err.decode(encoding).encode("utf8")
|
||||
except Exception as e:
|
||||
logger.debug("ERROR EXEC COMMAND: {}".format(str(e)))
|
||||
|
||||
stat = proc.returncode
|
||||
return stat, out, err
|
||||
|
||||
|
||||
def get_hardware():
|
||||
"""
|
||||
Returns client's hardware list
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
filepath = _exec_ogcommand('/opt/opengnsys/scripts/listHardwareInfo').strip()
|
||||
# Returns content of configuration file, skipping the header line and newline characters
|
||||
with open(filepath, 'r') as f:
|
||||
harddata = map(str.strip, f.readlines()[1:])
|
||||
except IOError:
|
||||
harddata = ''
|
||||
return harddata
|
||||
|
||||
|
||||
def get_software(disk, part):
|
||||
"""
|
||||
Returns software list installed on an operating system
|
||||
:param disk:
|
||||
:param part:
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
filepath = _exec_ogcommand('/opt/opengnsys/scripts/listSoftwareInfo {} {}'.format(disk, part)).strip()
|
||||
# Returns content of configuration file, skipping the header line and newline characters
|
||||
with open(filepath, 'r') as f:
|
||||
softdata = map(str.strip, f.readlines())
|
||||
except IOError:
|
||||
softdata = ''
|
||||
return softdata
|
|
@ -0,0 +1,57 @@
|
|||
# -*- 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
|
||||
"""
|
||||
# pylint: disable=unused-wildcard-import,wildcard-import
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Importing platform operations and getting operating system data.
|
||||
if sys.platform == 'win32':
|
||||
from .windows.operations import * # @UnusedWildImport
|
||||
os_type = 'Windows'
|
||||
os_version = getWindowsVersion()
|
||||
else:
|
||||
if sys.platform == 'darwin':
|
||||
from .macos.operations import * # @UnusedWildImport
|
||||
os_type = 'MacOS'
|
||||
os_version = getMacosVersion().replace(',', '')
|
||||
else:
|
||||
if os.path.exists('/scripts/oginit'):
|
||||
from .oglive.operations import * # @UnusedWildImport
|
||||
os_type = 'ogLive'
|
||||
os_version = get_oglive_version().replace(',', '')
|
||||
else:
|
||||
from .linux.operations import * # @UnusedWildImport
|
||||
os_type = 'Linux'
|
||||
os_version = getLinuxVersion().replace(',', '')
|
|
@ -0,0 +1,51 @@
|
|||
# -*- 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 opengnsys.log import logger
|
||||
|
||||
import threading
|
||||
import six
|
||||
|
||||
|
||||
class ScriptExecutorThread(threading.Thread):
|
||||
def __init__(self, script):
|
||||
super(ScriptExecutorThread, self).__init__()
|
||||
self.script = script
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
logger.debug('Executing script: {}'.format(self.script))
|
||||
six.exec_(self.script, globals(), None)
|
||||
except Exception as e:
|
||||
logger.error('Error executing script: {}'.format(e))
|
|
@ -0,0 +1,249 @@
|
|||
# -*- 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 .log import logger
|
||||
from .config import readConfig
|
||||
from .utils import exceptionToMessage
|
||||
|
||||
from . import ipc
|
||||
from . import httpserver
|
||||
from .loader import loadModules
|
||||
|
||||
import socket
|
||||
import time
|
||||
import json
|
||||
import six
|
||||
|
||||
IPC_PORT = 10398
|
||||
|
||||
|
||||
class CommonService(object):
|
||||
isAlive = True
|
||||
ipc = None
|
||||
httpServer = None
|
||||
modules = None
|
||||
|
||||
def __init__(self):
|
||||
logger.info('----------------------------------------')
|
||||
logger.info('Initializing OpenGnsys Agent')
|
||||
|
||||
# Read configuration file before proceding & ensures minimal config is there
|
||||
|
||||
self.config = readConfig()
|
||||
|
||||
# Get opengnsys section as dict
|
||||
cfg = dict(self.config.items('opengnsys'))
|
||||
|
||||
# Set up log level
|
||||
logger.setLevel(cfg.get('log', 'INFO'))
|
||||
|
||||
|
||||
logger.debug('Loaded configuration from opengnsys.cfg:')
|
||||
for section in self.config.sections():
|
||||
logger.debug('Section {} = {}'.format(section, self.config.items(section)))
|
||||
|
||||
|
||||
if logger.logger.isWindows():
|
||||
# Logs will also go to windows event log for services
|
||||
logger.logger.serviceLogger = True
|
||||
|
||||
self.address = (cfg.get('address', '0.0.0.0'), int(cfg.get('port', '10997')))
|
||||
self.ipcport = int(cfg.get('ipc_port', IPC_PORT))
|
||||
|
||||
self.timeout = int(cfg.get('timeout', '20'))
|
||||
|
||||
logger.debug('Socket timeout: {}'.format(self.timeout))
|
||||
socket.setdefaulttimeout(self.timeout)
|
||||
|
||||
# Now load modules
|
||||
self.modules = loadModules(self)
|
||||
logger.debug('Modules: {}'.format(list(v.name for v in self.modules)))
|
||||
|
||||
def stop(self):
|
||||
'''
|
||||
Requests service termination
|
||||
'''
|
||||
self.isAlive = False
|
||||
|
||||
# ********************************
|
||||
# * Internal messages processors *
|
||||
# ********************************
|
||||
def notifyLogin(self, username):
|
||||
for v in self.modules:
|
||||
try:
|
||||
logger.debug('Notifying login of user {} to module {}'.format(username, v.name))
|
||||
v.onLogin(username)
|
||||
except Exception as e:
|
||||
logger.error('Got exception {} processing login message on {}'.format(e, v.name))
|
||||
|
||||
def notifyLogout(self, username):
|
||||
for v in self.modules:
|
||||
try:
|
||||
logger.debug('Notifying logout of user {} to module {}'.format(username, v.name))
|
||||
v.onLogout(username)
|
||||
except Exception as e:
|
||||
logger.error('Got exception {} processing logout message on {}'.format(e, v.name))
|
||||
|
||||
def notifyMessage(self, data):
|
||||
module, message, data = data.split('\0')
|
||||
for v in self.modules:
|
||||
if v.name == module: # Case Sensitive!!!!
|
||||
try:
|
||||
logger.debug('Notifying message {} to module {} with json data {}'.format(message, v.name, data))
|
||||
v.processClientMessage(message, json.loads(data))
|
||||
return
|
||||
except Exception as e:
|
||||
logger.error('Got exception {} processing generic message on {}'.format(e, v.name))
|
||||
|
||||
logger.error('Module {} not found, messsage {} not sent'.format(module, message))
|
||||
|
||||
|
||||
def clientMessageProcessor(self, msg, data):
|
||||
'''
|
||||
Callback, invoked from IPC, on its own thread (not the main thread).
|
||||
This thread will "block" communication with agent untill finished, but this should be no problem
|
||||
'''
|
||||
logger.debug('Got message {}'.format(msg))
|
||||
|
||||
if msg == ipc.REQ_LOGIN:
|
||||
self.notifyLogin(data)
|
||||
elif msg == ipc.REQ_LOGOUT:
|
||||
self.notifyLogout(data)
|
||||
elif msg == ipc.REQ_MESSAGE:
|
||||
self.notifyMessage(data)
|
||||
|
||||
def initialize(self):
|
||||
# ******************************************
|
||||
# * Initialize listeners, modules, etc...
|
||||
# ******************************************
|
||||
|
||||
if six.PY3 is False:
|
||||
import threading
|
||||
threading._DummyThread._Thread__stop = lambda x: 42
|
||||
|
||||
logger.debug('Starting IPC listener at {}'.format(IPC_PORT))
|
||||
self.ipc = ipc.ServerIPC(self.ipcport, clientMessageProcessor=self.clientMessageProcessor)
|
||||
self.ipc.start()
|
||||
|
||||
# And http threaded server
|
||||
self.httpServer = httpserver.HTTPServerThread(self.address, self)
|
||||
self.httpServer.start()
|
||||
|
||||
# And lastly invoke modules activation
|
||||
validMods = []
|
||||
for mod in self.modules:
|
||||
try:
|
||||
logger.debug('Activating module {}'.format(mod.name))
|
||||
mod.activate()
|
||||
validMods.append(mod)
|
||||
except Exception as e:
|
||||
logger.exception()
|
||||
logger.error("Activation of {} failed: {}".format(mod.name, exceptionToMessage(e)))
|
||||
|
||||
self.modules[:] = validMods # copy instead of assignment
|
||||
|
||||
logger.debug('Modules after activation: {}'.format(list(v.name for v in self.modules)))
|
||||
|
||||
def terminate(self):
|
||||
# First invoke deactivate on modules
|
||||
for mod in reversed(self.modules):
|
||||
try:
|
||||
logger.debug('Deactivating module {}'.format(mod.name))
|
||||
mod.deactivate()
|
||||
except Exception as e:
|
||||
logger.exception()
|
||||
logger.error("Deactivation of {} failed: {}".format(mod.name, exceptionToMessage(e)))
|
||||
|
||||
# Remove IPC threads
|
||||
if self.ipc is not None:
|
||||
try:
|
||||
self.ipc.stop()
|
||||
except Exception:
|
||||
logger.error('Couln\'t stop ipc server')
|
||||
|
||||
if self.httpServer is not None:
|
||||
try:
|
||||
self.httpServer.stop()
|
||||
except Exception:
|
||||
logger.error('Couln\'t stop RESTApi server')
|
||||
|
||||
self.notifyStop()
|
||||
|
||||
# ****************************************
|
||||
# Methods that CAN BE overridden by agents
|
||||
# ****************************************
|
||||
def doWait(self, miliseconds):
|
||||
'''
|
||||
Invoked to wait a bit
|
||||
CAN be OVERRIDDEN
|
||||
'''
|
||||
time.sleep(float(miliseconds) / 1000)
|
||||
|
||||
def notifyStop(self):
|
||||
'''
|
||||
Overridden to log stop
|
||||
'''
|
||||
logger.info('Service is being stopped')
|
||||
|
||||
# ***************************************************
|
||||
# * Helpers, convenient methods to facilitate comms *
|
||||
# ***************************************************
|
||||
def sendClientMessage(self, toModule, message, data):
|
||||
'''
|
||||
Sends a message to the clients using IPC
|
||||
The data is converted to json, so ensure that it is serializable.
|
||||
All IPC is asynchronous, so if you expect a response, this will be sent by client using another message
|
||||
|
||||
@param toModule: Module that will receive this message
|
||||
@param message: Message to send
|
||||
@param data: data to send
|
||||
'''
|
||||
self.ipc.sendMessageMessage('\0'.join((toModule, message, json.dumps(data))))
|
||||
|
||||
def sendScriptMessage(self, script):
|
||||
'''
|
||||
Sends an script to be executed by client
|
||||
'''
|
||||
self.ipc.sendScriptMessage(script)
|
||||
|
||||
def sendLogoffMessage(self):
|
||||
'''
|
||||
Sends a logoff message to client
|
||||
'''
|
||||
self.ipc.sendLoggofMessage()
|
||||
|
||||
def sendPopupMessage(self, title, message):
|
||||
'''
|
||||
Sends a poup box to be displayed by client
|
||||
'''
|
||||
self.ipc.sendPopupMessage(title, message)
|
|
@ -0,0 +1,72 @@
|
|||
# -*- 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
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
# -*- 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 win32security # @UnresolvedImport, pylint: disable=import-error
|
||||
import win32net # @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
|
||||
import os
|
||||
|
||||
from opengnsys import operations
|
||||
from opengnsys.service import CommonService
|
||||
|
||||
from opengnsys.log import logger
|
||||
|
||||
class OGAgentSvc(win32serviceutil.ServiceFramework, CommonService):
|
||||
'''
|
||||
This class represents a Windows Service for managing Agent interactions
|
||||
with OpenGnsys Server
|
||||
'''
|
||||
_svc_name_ = "OGAgent"
|
||||
_svc_display_name_ = "OpenGnsys Agent Service"
|
||||
_svc_description_ = "OpenGnsys Agent for Operating Systems"
|
||||
# 'System Event Notification' is the SENS service
|
||||
_svc_deps_ = ['EventLog']
|
||||
|
||||
def __init__(self, args):
|
||||
win32serviceutil.ServiceFramework.__init__(self, args)
|
||||
CommonService.__init__(self)
|
||||
self.hWaitStop = win32event.CreateEvent(None, 1, 0, None)
|
||||
self._user = 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 SvcDoRun(self):
|
||||
'''
|
||||
Main service loop
|
||||
'''
|
||||
try:
|
||||
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()
|
||||
|
||||
# Initialize remaining service data
|
||||
self.initialize()
|
||||
except Exception: # Any init exception wil be caught, service must be then restarted
|
||||
logger.exception()
|
||||
logger.debug('Exiting service with failure status')
|
||||
os._exit(-1) # pylint: disable=protected-access
|
||||
|
||||
# *********************
|
||||
# * Main Service loop *
|
||||
# *********************
|
||||
try:
|
||||
while self.isAlive:
|
||||
# Pumps & processes any waiting messages
|
||||
pythoncom.PumpWaitingMessages()
|
||||
win32event.WaitForSingleObject(self.hWaitStop, 1000)
|
||||
except Exception as e:
|
||||
logger.error('Caught exception on main loop: {}'.format(e))
|
||||
|
||||
logger.debug('Exited main loop, deregistering SENS')
|
||||
|
||||
self.terminate() # Ends IPC servers
|
||||
|
||||
self.notifyStop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
win32serviceutil.HandleCommandLine(OGAgentSvc)
|
|
@ -0,0 +1,39 @@
|
|||
# -*- 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 os
|
||||
import sys
|
||||
|
||||
# Change to application directory.
|
||||
os.chdir(os.path.dirname(sys.argv[0]))
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
# -*- 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 range(6))
|
||||
|
||||
|
||||
class LocalLogger(object):
|
||||
def __init__(self):
|
||||
# tempdir is different for "user application" and "service"
|
||||
# service wil get c:\windows\temp, while user will get c:\users\XXX\temp
|
||||
logging.basicConfig(
|
||||
filename=os.path.join(tempfile.gettempdir(), 'opengnsys.log'),
|
||||
filemode='a',
|
||||
format='%(levelname)s %(asctime)s %(message)s',
|
||||
level=logging.DEBUG
|
||||
)
|
||||
self.logger = logging.getLogger('opengnsys')
|
||||
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
|
|
@ -0,0 +1,269 @@
|
|||
# -*- 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 os
|
||||
import locale
|
||||
import subprocess
|
||||
import ctypes
|
||||
from ctypes.wintypes import DWORD, LPCWSTR
|
||||
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
|
||||
|
||||
from opengnsys import utils
|
||||
from opengnsys.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():
|
||||
'''
|
||||
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
|
||||
'''
|
||||
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:
|
||||
if obj.DefaultIPGateway is None: # Skip adapters without default router
|
||||
continue
|
||||
for ip in obj.IPAddress:
|
||||
if ':' in ip: # Is IPV6, skip this
|
||||
continue
|
||||
if ip is None or ip == '' or ip.startswith('169.254') or ip.startswith('0.'): # If single link ip, or no ip
|
||||
continue
|
||||
logger.debug('Net config found: {}=({}, {})'.format(obj.Caption, obj.MACAddress, ip))
|
||||
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():
|
||||
'''
|
||||
Returns Windows version.
|
||||
'''
|
||||
import _winreg
|
||||
reg = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion')
|
||||
try:
|
||||
data = '{} {}'.format(_winreg.QueryValueEx(reg, 'ProductName')[0], _winreg.QueryValueEx(reg, 'ReleaseId')[0])
|
||||
except Exception:
|
||||
data = '{} {}'.format(_winreg.QueryValueEx(reg, 'ProductName')[0], _winreg.QueryValueEx(reg, 'CurrentBuildNumber')[0])
|
||||
reg.Close()
|
||||
return data
|
||||
|
||||
|
||||
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 poweroff(flags=0):
|
||||
'''
|
||||
Simple poweroff command.
|
||||
'''
|
||||
reboot(flags=EWX_FORCEIFHUNG | EWX_SHUTDOWN)
|
||||
|
||||
def logoff():
|
||||
win32api.ExitWindowsEx(EWX_LOGOFF)
|
||||
|
||||
|
||||
def renameComputer(newName):
|
||||
# Needs admin privileges to work
|
||||
if ctypes.windll.kernel32.SetComputerNameExW(DWORD(win32con.ComputerNamePhysicalDnsHostname), LPCWSTR(newName)) == 0: # @UndefinedVariable
|
||||
# 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):
|
||||
'''
|
||||
Joins machine to a windows domain
|
||||
:param domain: Domain to join to
|
||||
:param ou: Ou that will hold machine
|
||||
:param account: Account used to join domain
|
||||
:param password: Password of account used to join domain
|
||||
:param executeInOneStep: If true, means that this machine has been renamed and wants to add NETSETUP_JOIN_WITH_NEW_NAME to request so we can do rename/join in one step.
|
||||
'''
|
||||
# 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)
|
||||
if res == 1355:
|
||||
error = "DC Is not reachable"
|
||||
print('{} {}'.format(res, error))
|
||||
raise Exception('Error joining domain {}, with credentials {}/*****{}: {}, {}'.format(domain.value, account.value, ', under OU {}'.format(ou.value) if ou.value is not 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 initIdleDuration(atLeastSeconds):
|
||||
'''
|
||||
In windows, there is no need to set screensaver
|
||||
'''
|
||||
pass
|
||||
|
||||
|
||||
def getIdleDuration():
|
||||
lastInputInfo = LASTINPUTINFO()
|
||||
lastInputInfo.cbSize = ctypes.sizeof(lastInputInfo)
|
||||
ctypes.windll.user32.GetLastInputInfo(ctypes.byref(lastInputInfo))
|
||||
millis = ctypes.windll.kernel32.GetTickCount() - lastInputInfo.dwTime # @UndefinedVariable
|
||||
return millis / 1000.0
|
||||
|
||||
|
||||
def getCurrentUser():
|
||||
'''
|
||||
Returns current logged in username
|
||||
'''
|
||||
return os.environ['USERNAME']
|
||||
|
||||
|
||||
def getSessionLanguage():
|
||||
'''
|
||||
Returns the user's session language
|
||||
'''
|
||||
return locale.getdefaultlocale()[0]
|
||||
|
||||
|
||||
def showPopup(title, message):
|
||||
'''
|
||||
Displays a message box on user's session (during 1 min).
|
||||
'''
|
||||
return subprocess.call('mshta "javascript:var sh=new ActiveXObject(\'WScript.Shell\'); sh.Popup( \'{}\', 60, \'{}\', 64); close()"'.format(message.encode('unicode_escape'), title.encode('unicode_escape')), shell=True)
|
||||
|
||||
|
||||
def get_etc_path():
|
||||
"""
|
||||
:return:
|
||||
Returns etc directory path.
|
||||
"""
|
||||
return os.path.join('C:', os.sep, 'Windows', 'System32', 'drivers', 'etc')
|
|
@ -0,0 +1,2 @@
|
|||
from .server_worker import ServerWorker
|
||||
from .client_worker import ClientWorker
|
|
@ -0,0 +1,114 @@
|
|||
# -*- 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
|
||||
|
||||
class ClientWorker(object):
|
||||
'''
|
||||
A ServerWorker is a server module that "works" for service
|
||||
Most method are invoked inside their own thread, except onActivation & onDeactivation.
|
||||
This two methods are invoked inside main service thread, take that into account when creating them
|
||||
|
||||
* You must provide a module name (override name on your class), so we can identify the module by a "valid" name.
|
||||
A valid name is like a valid python variable (do not use spaces, etc...)
|
||||
* The name of the module is used as REST message destination id:
|
||||
https://sampleserver:8888/[name]/....
|
||||
Remember that module names and REST path are case sensitive!!!
|
||||
|
||||
'''
|
||||
name = None
|
||||
service = None
|
||||
|
||||
def __init__(self, service):
|
||||
self.service = service
|
||||
|
||||
def activate(self):
|
||||
'''
|
||||
Convenient method to wrap onActivation, so we can include easyly custom common logic for activation in a future
|
||||
'''
|
||||
self.onActivation()
|
||||
|
||||
def deactivate(self):
|
||||
'''
|
||||
Convenient method to wrap onActivation, so we can include easyly custom common logic for deactivation in a future
|
||||
'''
|
||||
self.onDeactivation()
|
||||
|
||||
def processMessage(self, message, params):
|
||||
'''
|
||||
This method can be overriden to provide your own message proccessor, or better you can
|
||||
implement a method that is called "process_" + message and this default processMessage will invoke it
|
||||
* Example:
|
||||
We got a message from OGAgent "Mazinger", with json params
|
||||
module.processMessage("mazinger", jsonParams)
|
||||
|
||||
This method will process "mazinguer", and look for a "self" method that is called "process_mazinger", and invoke it this way:
|
||||
return self.process_mazinger(jsonParams)
|
||||
|
||||
The methods must return data that can be serialized to json (i.e. Ojects are not serializable to json, basic type are)
|
||||
'''
|
||||
try:
|
||||
operation = getattr(self, 'process_' + message)
|
||||
except Exception:
|
||||
raise Exception('Message processor for "{}" not found'.format(message))
|
||||
|
||||
return operation(params)
|
||||
|
||||
def onActivation(self):
|
||||
'''
|
||||
Invoked by Service for activation.
|
||||
This MUST be overridden by modules!
|
||||
This method is invoked inside main thread, so if it "hangs", complete service will hang
|
||||
This should be no problem, but be advised about this
|
||||
'''
|
||||
pass
|
||||
|
||||
def onDeactivation(self):
|
||||
'''
|
||||
Invoked by Service before unloading service
|
||||
This MUST be overridden by modules!
|
||||
This method is invoked inside main thread, so if it "hangs", complete service will hang
|
||||
This should be no problem, but be advised about this
|
||||
'''
|
||||
pass
|
||||
|
||||
# *************************************
|
||||
# * Helper, convenient helper methods *
|
||||
# *************************************
|
||||
def sendServerMessage(self, message, data):
|
||||
'''
|
||||
Sends a message to connected ipc clients
|
||||
By convenience, it uses the "current" moduel name as destination module name also.
|
||||
If you need to send a message to a different module, you can use self.service.sendClientMessage(module, message, data) instead
|
||||
og this helmer
|
||||
'''
|
||||
self.service.ipc.sendMessage(self.name, message, data)
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
# -*- 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
|
||||
|
||||
class ServerWorker(object):
|
||||
'''
|
||||
A ServerWorker is a server module that "works" for service
|
||||
Most method are invoked inside their own thread, except onActivation & onDeactivation.
|
||||
This two methods are invoked inside main service thread, take that into account when creating them
|
||||
|
||||
* You must provide a module name (override name on your class), so we can identify the module by a "valid" name.
|
||||
A valid name is like a valid python variable (do not use spaces, etc...)
|
||||
* The name of the module is used as REST message destination id:
|
||||
https://sampleserver:8888/[name]/....
|
||||
Remember that module names and REST path are case sensitive!!!
|
||||
|
||||
'''
|
||||
name = None
|
||||
service = None
|
||||
locked = False
|
||||
|
||||
def __init__(self, service):
|
||||
self.service = service
|
||||
|
||||
def activate(self):
|
||||
'''
|
||||
Convenient method to wrap onActivation, so we can include easyly custom common logic for activation in a future
|
||||
'''
|
||||
self.onActivation()
|
||||
|
||||
def deactivate(self):
|
||||
'''
|
||||
Convenient method to wrap onActivation, so we can include easyly custom common logic for deactivation in a future
|
||||
'''
|
||||
self.onDeactivation()
|
||||
|
||||
def process(self, getParams, postParams, server):
|
||||
'''
|
||||
This method is invoked on a message received with an empty path (that means a message with only the module name, like in "http://example.com/Sample"
|
||||
Override it if you expect messages with that pattern
|
||||
|
||||
Overriden method must return data that can be serialized to json (i.e. Ojects are not serializable to json, basic type are)
|
||||
'''
|
||||
raise NotImplementedError('Generic message processor is not supported')
|
||||
|
||||
def processServerMessage(self, path, getParams, postParams, server):
|
||||
'''
|
||||
This method can be overriden to provide your own message proccessor, or better you can
|
||||
implement a method that is called exactly as "process_" + path[0] (module name has been removed from path array) and this default processMessage will invoke it
|
||||
* Example:
|
||||
Imagine this invocation url (no matter if GET or POST): http://example.com:9999/Sample/mazinger/Z
|
||||
The HTTP Server will remove "Sample" from path, parse arguments and invoke this method as this:
|
||||
module.processMessage(["mazinger","Z"], getParams, postParams)
|
||||
|
||||
This method will process "mazinguer", and look for a "self" method that is called "process_mazinger", and invoke it this way:
|
||||
return self.process_mazinger(["Z"], getParams, postParams)
|
||||
|
||||
In the case path is empty (that is, the path is composed only by the module name, like in "http://example.com/Sample", the "process" method
|
||||
will be invoked directly
|
||||
|
||||
The methods must return data that can be serialized to json (i.e. Ojects are not serializable to json, basic type are)
|
||||
'''
|
||||
if self.locked is True:
|
||||
raise Exception('system is busy')
|
||||
|
||||
if len(path) == 0:
|
||||
return self.process(getParams, postParams, server)
|
||||
try:
|
||||
operation = getattr(self, 'process_' + path[0])
|
||||
except Exception:
|
||||
raise Exception('Message processor for "{}" not found'.format(path[0]))
|
||||
|
||||
return operation(path[1:], getParams, postParams, server)
|
||||
|
||||
|
||||
def processClientMessage(self, message, data):
|
||||
'''
|
||||
Invoked by Service when a client message is received (A message from user space Agent)
|
||||
|
||||
This method can be overriden to provide your own message proccessor, or better you can
|
||||
implement a method that is called exactly "process_client_" + message (module name has been removed from path) and this default processMessage will invoke it
|
||||
* Example:
|
||||
We got a message from OGAgent "Mazinger", with json params
|
||||
module.processClientMessage("mazinger", jsonParams)
|
||||
|
||||
This method will process "mazinguer", and look for a "self" method that is called "process_client_mazinger", and invoke it this way:
|
||||
self.process_client_mazinger(jsonParams)
|
||||
|
||||
The methods returns nothing (client communications are done asynchronously)
|
||||
'''
|
||||
try:
|
||||
operation = getattr(self, 'process_client_' + message)
|
||||
except Exception:
|
||||
raise Exception('Message processor for "{}" not found'.format(message))
|
||||
|
||||
operation(data)
|
||||
|
||||
# raise NotImplementedError('Got a client message but no proccessor is implemented')
|
||||
|
||||
|
||||
def onActivation(self):
|
||||
'''
|
||||
Invoked by Service for activation.
|
||||
This MUST be overridden by modules!
|
||||
This method is invoked inside main thread, so if it "hangs", complete service will hang
|
||||
This should be no problem, but be advised about this
|
||||
'''
|
||||
pass
|
||||
|
||||
def onDeactivation(self):
|
||||
'''
|
||||
Invoked by Service before unloading service
|
||||
This MUST be overridden by modules!
|
||||
This method is invoked inside main thread, so if it "hangs", complete service will hang
|
||||
This should be no problem, but be advised about this
|
||||
'''
|
||||
pass
|
||||
|
||||
|
||||
def onLogin(self, user):
|
||||
'''
|
||||
Invoked by Service when an user login is detected
|
||||
This CAN be overridden by modules
|
||||
This method is invoked whenever the client (user space agent) notifies the server (Service) that a user has logged in.
|
||||
This method is run on its own thread
|
||||
'''
|
||||
pass
|
||||
|
||||
def onLogout(self, user):
|
||||
'''
|
||||
Invoked by Service when an user login is detected
|
||||
This CAN be overridden by modules
|
||||
This method is invoked whenever the client (user space agent) notifies the server (Service) that a user has logged in.
|
||||
This method is run on its own thread
|
||||
'''
|
||||
pass
|
||||
|
||||
# *************************************
|
||||
# * Helper, convenient helper methods *
|
||||
# *************************************
|
||||
def sendClientMessage(self, message, data):
|
||||
'''
|
||||
Sends a message to connected ipc clients
|
||||
By convenience, it uses the "current" moduel name as destination module name also.
|
||||
If you need to send a message to a different module, you can use self.service.sendClientMessage(module, message, data) instead
|
||||
og this helmer
|
||||
'''
|
||||
self.service.sendClientMessage(self.name, message, data)
|
||||
|
||||
def sendScriptMessage(self, script):
|
||||
self.service.sendScriptMessage(script)
|
||||
|
||||
def sendLogoffMessage(self):
|
||||
self.service.sendLogoffMessage()
|
||||
|
||||
def sendPopupMessage(self):
|
||||
self.service.sendPopupMessage()
|
Binary file not shown.
|
@ -0,0 +1,26 @@
|
|||
Format-Specification: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?op=file&rev=135
|
||||
Name: ogagent
|
||||
Maintainer: Ramón M. Gómez
|
||||
Source: https://opengnsys.es
|
||||
|
||||
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'.
|
|
@ -0,0 +1,3 @@
|
|||
OGAgent is the agent intended for OpengGnsys interaction.
|
||||
|
||||
Please, visit https://opengnsys.es for more information
|
|
@ -0,0 +1,203 @@
|
|||
// ********************************************************************************************************
|
||||
// Nombre del fichero: Database.cpp
|
||||
// Autor: José Manuel Alonso (E.T.S.I.I.) Universidad de Sevilla
|
||||
// Fecha Creación: Marzo-2010
|
||||
// Fecha Última modificación: Marzo-2010
|
||||
// Descripción:
|
||||
// Fichero de implementación de la clase Database para funciones de manipulación
|
||||
// de bases de datos sobre un Servidor Mysql
|
||||
// ********************************************************************************************************
|
||||
#include "Database.h"
|
||||
// __________________________________________________________________________
|
||||
void ErrorHandler(Herror hr, char* ErrStr)
|
||||
{
|
||||
sprintf(ErrStr,"Error:\n");
|
||||
sprintf(ErrStr,"%sCode = %d\n",ErrStr ,hr.nError);
|
||||
sprintf(ErrStr,"%sDescription = %s",ErrStr, (char*) hr.dError);
|
||||
}
|
||||
// __________________________________________________________________________
|
||||
Database::Database()
|
||||
{
|
||||
m_Cnn=NULL;
|
||||
sprintf(m_ErrStr,"NULL POINTER");
|
||||
}
|
||||
// __________________________________________________________________________
|
||||
void Database::GetErrorErrStr(char* ErrStr)
|
||||
{
|
||||
sprintf(ErrStr,"%s",m_ErrStr);
|
||||
}
|
||||
// __________________________________________________________________________
|
||||
void Table::GetErrorErrStr(char* ErrStr)
|
||||
{
|
||||
sprintf(ErrStr,"%s",m_ErrStr);
|
||||
}
|
||||
// __________________________________________________________________________
|
||||
bool Database::Open(char* UserName, char* Pwd,char* server,char*Bd)
|
||||
{
|
||||
Herror hr;
|
||||
m_Cnn=mysql_init(NULL);
|
||||
if(m_Cnn==NULL){
|
||||
hr.nError=0;
|
||||
strcpy(hr.dError,"Error en la Creación del objeto MYSQL");
|
||||
ErrorHandler(hr,m_ErrStr);
|
||||
return(false); // Fallo de inicializaci<63>
|
||||
}
|
||||
|
||||
if(!mysql_real_connect(m_Cnn, server,UserName,Pwd,Bd, MYSQL_PORT,NULL,0)){
|
||||
mysql_error(m_Cnn);
|
||||
hr.nError=mysql_errno(m_Cnn);
|
||||
strcpy(hr.dError,mysql_error(m_Cnn));
|
||||
ErrorHandler(hr,m_ErrStr);
|
||||
return(false); // Fallo de conexi<78>
|
||||
}
|
||||
hr.nError=0;
|
||||
strcpy(hr.dError,"Success");
|
||||
ErrorHandler(hr,m_ErrStr);
|
||||
return (true);
|
||||
}
|
||||
// __________________________________________________________________________
|
||||
bool Database::Close()
|
||||
{
|
||||
mysql_close(m_Cnn);
|
||||
return(true);
|
||||
}
|
||||
// __________________________________________________________________________
|
||||
bool Database::Execute(char* CmdStr)
|
||||
{
|
||||
Herror hr;
|
||||
if (mysql_query(m_Cnn,CmdStr)){ // Ejecuta la consulta
|
||||
mysql_error(m_Cnn);
|
||||
hr.nError=mysql_errno(m_Cnn);
|
||||
strcpy(hr.dError,mysql_error(m_Cnn));
|
||||
ErrorHandler(hr,m_ErrStr);
|
||||
mysql_close(m_Cnn);
|
||||
return(false); // Fallo de conexión
|
||||
}
|
||||
hr.nError=0;
|
||||
strcpy(hr.dError,"Success");
|
||||
ErrorHandler(hr,m_ErrStr);
|
||||
return (true);
|
||||
}
|
||||
// __________________________________________________________________________
|
||||
bool Database::Execute(char* CmdStr, Table& Tbl)
|
||||
{
|
||||
Herror hr;
|
||||
if (mysql_query(m_Cnn,CmdStr)) { // Ejecuta la consulta
|
||||
mysql_error(m_Cnn);
|
||||
hr.nError=mysql_errno(m_Cnn);
|
||||
strcpy(hr.dError,mysql_error(m_Cnn));
|
||||
ErrorHandler(hr,m_ErrStr);
|
||||
mysql_close(m_Cnn);
|
||||
return(false); // Fallo de conexi<78>
|
||||
}
|
||||
|
||||
hr.nError=0;
|
||||
strcpy(hr.dError,"Success");
|
||||
ErrorHandler(hr,m_ErrStr);
|
||||
|
||||
Tbl.m_Rec = mysql_store_result(m_Cnn) ; // Toma el recordset
|
||||
if(Tbl.m_Rec){
|
||||
Tbl.row=mysql_fetch_row(Tbl.m_Rec);
|
||||
Tbl.fields = mysql_fetch_fields(Tbl.m_Rec);
|
||||
Tbl.num_fields = mysql_num_fields(Tbl.m_Rec);
|
||||
Tbl.numreg=mysql_num_rows(Tbl.m_Rec);
|
||||
Tbl.eof=Tbl.numreg==0; // Consulta vacia
|
||||
}
|
||||
return (true);
|
||||
}
|
||||
// __________________________________________________________________________
|
||||
void Database::liberaResult (Table& Tbl) {
|
||||
//Free resources after mysql_store_result
|
||||
mysql_free_result(Tbl.m_Rec);
|
||||
}
|
||||
// __________________________________________________________________________
|
||||
Table::Table()
|
||||
{
|
||||
m_Rec=NULL;
|
||||
}
|
||||
// __________________________________________________________________________
|
||||
bool Table::ISEOF()
|
||||
{
|
||||
return(eof);
|
||||
}
|
||||
// __________________________________________________________________________
|
||||
bool Table::Get(const char* FieldName, char *FieldValue)
|
||||
{
|
||||
char * aux;
|
||||
aux=tomadato(FieldName);
|
||||
if(aux)
|
||||
strcpy(FieldValue,aux);
|
||||
else
|
||||
strcpy(FieldValue,"");
|
||||
return(true);
|
||||
}
|
||||
// __________________________________________________________________________
|
||||
bool Table::Get(const char* FieldName,int &FieldValue)
|
||||
{
|
||||
char *aux;
|
||||
aux=tomadato(FieldName);
|
||||
if(aux)
|
||||
FieldValue=atoi(aux);
|
||||
else
|
||||
FieldValue=0;
|
||||
return(true);
|
||||
}
|
||||
// __________________________________________________________________________
|
||||
bool Table::Get(const char* FieldName,char &FieldValue)
|
||||
{
|
||||
char *aux;
|
||||
aux=tomadato(FieldName);
|
||||
FieldValue=aux[0];
|
||||
return(true);
|
||||
}
|
||||
// __________________________________________________________________________
|
||||
char* Table::tomadato(const char* FieldName)
|
||||
{
|
||||
Herror hr;
|
||||
unsigned int i;
|
||||
|
||||
for(i = 0; i < num_fields; i++){
|
||||
if(strcmp((char*)fields[i].name,FieldName)==0){
|
||||
sprintf(m_ErrStr,"Success");
|
||||
return((char*)row[i]);
|
||||
}
|
||||
}
|
||||
hr.nError=-1;
|
||||
strcpy(hr.dError,"El nombre del campo no existe");
|
||||
ErrorHandler(hr,m_ErrStr);
|
||||
return(NULL); // No existe el nombre del campo en la tabla
|
||||
}
|
||||
// __________________________________________________________________________
|
||||
|
||||
bool Table::MoveNext()
|
||||
{
|
||||
eof=false;
|
||||
row=mysql_fetch_row(m_Rec);
|
||||
if(row==NULL){
|
||||
if(!mysql_eof(m_Rec))
|
||||
return(false); // Fallo de lectura
|
||||
else
|
||||
eof=true; // Fin de fichero
|
||||
}
|
||||
return (true);
|
||||
}
|
||||
// __________________________________________________________________________
|
||||
bool Table::MoveFirst()
|
||||
{
|
||||
my_ulonglong auxnumreg;
|
||||
|
||||
auxnumreg=0;
|
||||
mysql_data_seek(m_Rec,auxnumreg);
|
||||
return (MoveNext());
|
||||
}
|
||||
// __________________________________________________________________________
|
||||
bool Table::MoveLast()
|
||||
{
|
||||
my_ulonglong auxnumreg;
|
||||
auxnumreg=numreg;
|
||||
auxnumreg--;
|
||||
if(auxnumreg<0) auxnumreg=0; // Principio de fichero
|
||||
mysql_data_seek(m_Rec,auxnumreg);
|
||||
return (MoveNext());
|
||||
return (true);
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
// ******************************************************************************************************
|
||||
// Aplicación HIDRA
|
||||
// Copyright 2004 Jos<6F>Manuel Alonso. Todos los derechos reservados.
|
||||
// Fichero: Database.h
|
||||
// Descripción:
|
||||
// Fichero de cabecera de la clase Database para implementar funciones de manipulaci<63>
|
||||
// de bases de datos sobre un Servidor Mysql
|
||||
// ******************************************************************************************************
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include </usr/include/mysql/mysql.h>
|
||||
// __________________________________________________________________________
|
||||
class Database;
|
||||
class Table;
|
||||
// __________________________________________________________________________
|
||||
class Database
|
||||
{
|
||||
public:
|
||||
MYSQL *m_Cnn;
|
||||
char m_ErrStr[500];
|
||||
Database();
|
||||
bool Open(char* UserName, char* Pwd,char* server,char*Database);
|
||||
bool OpenTbl(int Mode, char* CmdStr, Table& Tbl);
|
||||
bool Close(void);
|
||||
bool Execute(char* CmdStr);
|
||||
bool Execute(char* CmdStr, Table& Tbl);
|
||||
void liberaResult(Table& Tbl);
|
||||
void GetErrorErrStr(char* ErrStr);
|
||||
};
|
||||
// __________________________________________________________________________
|
||||
class Table{
|
||||
char* tomadato(const char* FieldName);
|
||||
public:
|
||||
bool eof,bof;
|
||||
MYSQL_RES * m_Rec ;
|
||||
MYSQL_FIELD *fields;
|
||||
unsigned int num_fields;
|
||||
MYSQL_ROW row ;
|
||||
MYSQL_ROW_OFFSET ptr;
|
||||
my_ulonglong numreg;
|
||||
char m_ErrStr[500];
|
||||
Table();
|
||||
void GetErrorErrStr(char* ErrStr);
|
||||
bool ISEOF();
|
||||
bool MoveNext();
|
||||
bool MovePrevious();
|
||||
bool MoveFirst();
|
||||
bool MoveLast();
|
||||
|
||||
bool Get(const char* FieldName, char* FieldValue);
|
||||
bool Get(const char* FieldName,int &FieldValue);
|
||||
bool Get(const char* FieldName,char &FieldValue);
|
||||
};
|
||||
// __________________________________________________________________________
|
||||
class Herror
|
||||
{
|
||||
public:
|
||||
int nError; // C<>igo del error
|
||||
char dError[500]; // Descripción del error
|
||||
};
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,319 @@
|
|||
// **************************************************************************************************************************************************
|
||||
// Libreria: ogAdmLib
|
||||
// Autor: José Manuel Alonso (E.T.S.I.I.) Universidad de Sevilla
|
||||
// Fecha Creación: Marzo-2010
|
||||
// Fecha Última modificación: Marzo-2010
|
||||
// Nombre del fichero: ogAdmLib.h
|
||||
// Descripción: Este fichero implementa el archivo de cabecera de la libreria ogAdmLib
|
||||
// **************************************************************************************************************************************************
|
||||
// ________________________________________________________________________________________________________
|
||||
// Valores definidos
|
||||
// ________________________________________________________________________________________________________
|
||||
#define LONSTD 1024 // Longitud de memoria estandar
|
||||
#define LONINT 16 // Longitud de memoria estandar para un número entero
|
||||
#define LONFIL 1024 // Longitud de memoria estandar para nombres de archivo completos (incluido path)
|
||||
#define LONIP 16 // Longitud de memoria estandar para cadenas que contiene una dirección IP
|
||||
#define LONMAC 16 // Longitud de memoria estandar para cadenas que contiene una dirección MAC
|
||||
#define LONSQL 8192 // Longitud de memoria estandar para una sentencia SQL
|
||||
#define LONPRM 4098 // Longitud estandar de los parámetros del fichero de configuración del servicio
|
||||
#define LONSCP 4098 // Longitud estandar de los parámetros de las tramas
|
||||
#define LONFUN 512 // Longitud estandar de los nombres de las funciones que procesan las tramas
|
||||
#define LONSUC 4098 // Longitud de los mensajes de sucesos
|
||||
#define LONBLK 8192 // Longitud de los paquetes de tramas leidos cada vez
|
||||
#define MAXPRM 20 // Máximo número de parámeros del fichero de configuración del servicio
|
||||
#define MAXPAR 128 // Maximo numero de particiones manejadas por el sistema, ahora con GPT es 128
|
||||
#define MAXLONURL 1024 // Longitud máxima de una dirección url con parámetros
|
||||
|
||||
#define LONHEXPRM 5 // Longitud del campo que contiene el tamaño de la cadena de parámetros
|
||||
#define LONGITUD_CABECERATRAMA 16 // Longitud de la cabecera de las tramas
|
||||
#define LONGITUD_PARAMETROS 8192 // Longitud estandar de la información de la trama (parámetros)
|
||||
#define MAXCMD_PARAMETROS 200 // Máximo número de parámetros de una trama
|
||||
|
||||
#define MAXIMOS_CLIENTES 4000 // Máximo número de conexiones con ordenadores clientes
|
||||
#define MAXIMAS_FUNCIONES LONSTD // Máximo número de funciones que procesan los mensajes entre servicio y clientes
|
||||
#define MAXIMAS_LINEAS 3000 // Longitud máxima de lineas en un archivo de comandos
|
||||
|
||||
#define AUTOINCORPORACION_OFF 0x0000 // Los ordenadores no se pueden dar de alta automáticamente
|
||||
#define AUTOINCORPORACION_ONA 0x0001 // Los ordenadores se pueden dar de alta automáticamente si existe el aula
|
||||
#define AUTOINCORPORACION_ONX 0x0002 // Los ordenadores se pueden dar de alta automáticamentee y si no existe el aula la crea
|
||||
|
||||
#define DEBUG_BAJO 1 // Nivel de debug bajo
|
||||
#define DEBUG_MEDIO 2 // Nivel de debug medio
|
||||
#define DEBUG_ALTO 3 // Nivel de debug alto
|
||||
#define DEBUG_MAXIMO 4 // Nivel de debug máximo
|
||||
|
||||
#define CLIENTE_OCUPADO "BSY" // Cliente ocupado
|
||||
#define CLIENTE_APAGADO "OFF" // Cliente apagado
|
||||
#define CLIENTE_INICIANDO "INI" // Cliente iniciando
|
||||
|
||||
#define CLIENTE_OPENGNSYS "OPG" // Cliente Opengnsys
|
||||
|
||||
#define CLIENTE_WIN "WIN" // Cliente Windows genérico
|
||||
#define CLIENTE_WNT "WNT" // Windows NT
|
||||
#define CLIENTE_W2K "W2K" // Windows 2000
|
||||
#define CLIENTE_WS2 "WS2" // Windows Server 2003
|
||||
#define CLIENTE_WXP "WXP" // Cliente Windows XP
|
||||
#define CLIENTE_W95 "W95" // Windows 95
|
||||
#define CLIENTE_W98 "W98" // Windows 98
|
||||
#define CLIENTE_WML "WML" // Windows Milenium
|
||||
#define CLIENTE_MS2 "MS2" // MsDos
|
||||
#define CLIENTE_WVI "WVI" // Cliente Windows Vista
|
||||
#define CLIENTE_WI7 "WI7" // Cliente Windows 7
|
||||
|
||||
#define CLIENTE_LNX "LNX" // Cliente Linux
|
||||
|
||||
#define ACCION_SINRESULTADO 0 // Sin resultado
|
||||
#define ACCION_EXITOSA 1 // Finalizada con éxito
|
||||
#define ACCION_FALLIDA 2 // Finalizada con errores
|
||||
|
||||
#define ACCION_INICIADA 1 // Acción activa
|
||||
#define ACCION_DETENIDA 2 // Acción momentanemente parada
|
||||
#define ACCION_FINALIZADA 3 // Accion finalizada
|
||||
|
||||
#define EJECUCION_COMANDO 1
|
||||
#define EJECUCION_PROCEDIMIENTO 2
|
||||
#define EJECUCION_TAREA 3
|
||||
#define EJECUCION_RESERVA 4
|
||||
|
||||
#define AMBITO_CENTROS 0x01
|
||||
#define AMBITO_GRUPOSAULAS 0x02
|
||||
#define AMBITO_AULAS 0x04
|
||||
#define AMBITO_GRUPOSORDENADORES 0x08
|
||||
#define AMBITO_ORDENADORES 0x10
|
||||
|
||||
// Código de los tipos de mensajes
|
||||
#define MSG_COMANDO '1' // Mensaje del tipo comando
|
||||
#define MSG_NOTIFICACION '2' // Respuesta a la ejecución un comando
|
||||
#define MSG_PETICION '3' // Petición de cualquier actuación
|
||||
#define MSG_RESPUESTA '4' // Respuesta a una petición
|
||||
#define MSG_INFORMACION '5' // Envío de cualquier información sin espera de confirmación o respuesta
|
||||
|
||||
#define ANNOREF 2009 // Año de referencia base
|
||||
|
||||
#define LONGITUD_SCRIPTSALIDA 131072 // Longitud máxima de la información devuelta por una función de interface
|
||||
#define MAXARGS 16 // Número máximo de argumentos enviados a un scripts
|
||||
#define MAXCNX 5 // Máximos intentos de conexión al servidor de Administración
|
||||
|
||||
#define PUERTO_WAKEUP 9 // Puerto wake up
|
||||
|
||||
#define MAXHARDWARE 128 // Máximos elementos hardware a detectar
|
||||
#define MAXSOFTWARE 8096 // Máximos elementos software a detectar
|
||||
// ________________________________________________________________________________________________________
|
||||
// Tipos definidos
|
||||
// ________________________________________________________________________________________________________
|
||||
typedef unsigned long DWORD;
|
||||
typedef unsigned short WORD;
|
||||
typedef int BOOLEAN;
|
||||
typedef char BYTE;
|
||||
typedef int SOCKET;
|
||||
typedef void* LPVOID;
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
#define SOCKET_ERROR (-1)
|
||||
#define INVALID_SOCKET (SOCKET)(~0)
|
||||
|
||||
#define LEER 0
|
||||
#define ESCRIBIR 1
|
||||
|
||||
#define CHARNULL '\0'
|
||||
|
||||
// ________________________________________________________________________________________________________
|
||||
// Variables globales
|
||||
// ________________________________________________________________________________________________________
|
||||
char szPathFileCfg[LONSTD],szPathFileLog[LONSTD];
|
||||
int ndebug; // Nivel de debuger
|
||||
|
||||
typedef struct{ // Estructura de las tramas
|
||||
char arroba; // Caracter arroba siempre
|
||||
char identificador[14]; // Identificador de la trama, siempre JMMLCAMDJ_MCDJ
|
||||
char tipo; // Tipo de mensaje
|
||||
long lonprm; // Longitud en hexadecimal de los parámetros
|
||||
char *parametros; // Parámetros de la trama
|
||||
}TRAMA;
|
||||
// ________________________________________________________________________________________________________
|
||||
// Tabla de errores
|
||||
// ________________________________________________________________________________________________________
|
||||
const char* tbErrores[]={"Se han generado errores. No se puede continuar la ejecución de este módulo",\
|
||||
"001-El nombre del fichero de configuración del programa está vacío",\
|
||||
"002-No existe fichero de configuración del programa",\
|
||||
"003-No hay memoria suficiente para el buffer",\
|
||||
"004-Error en el fichero de configuración del programa. No se ha definido el parámetro SERVIDORADM",\
|
||||
"005-Error en el fichero de configuración del programa. No se ha definido el parámetro PUERTO",\
|
||||
"006-Error en el fichero de configuración del programa. No se ha definido el parámetro USUARIO",\
|
||||
"007-Error en el fichero de configuración del programa. No se ha definido el parámetro PASSWORD",\
|
||||
"008-Error en el fichero de configuración del programa. No se ha definido el parámetro DATASOURCE",\
|
||||
"009-Error en el fichero de configuración del programa. No se ha definido el parámetro CATALOG",\
|
||||
"010-Error en los parámetros de ejecución del programa. Debe especificar el fichero de configuración",\
|
||||
"011-Error en los parámetros de ejecución del programa. Debe especificar el fichero de log",\
|
||||
"012-Error de sintaxis en los parámetros de ejecución del programa: Debe especificar -f nombre_del_fichero_de_configuración_del_programa -l nombre_del_fichero_de_log_del_programa -d nivel de debug",\
|
||||
"013-Error al crear socket ***socket() fallo",\
|
||||
"014-Error al enlazar socket al interface ***bind() fallo",\
|
||||
"015-Error al acceptar conexión de clientes ***accept() fallo",\
|
||||
"016-Error al crear hebra de cliente en módulo main()",\
|
||||
"017-Error al recibir trama ***recv() fallo",\
|
||||
"018-No se reconoce el mensaje enviado",\
|
||||
"019-Trama recibida NO válida",\
|
||||
"020-No se puede establecer conexión con la base de datos",\
|
||||
"021-No se han podido recuperar los datos de la consulta o bien insertar, modificar o eliminar datos",\
|
||||
"022-El cliente no se ha sido dado de alta en la base de datos del sistema. Se rechaza su petición de inclusión",\
|
||||
"023-Ha habido algún problema en la incorporación automática del cliente",\
|
||||
"024-Ha habido algún problema en la actualización de la configuración del cliente",\
|
||||
"025-La tabla de clientes está llena, no pueden registrarse más clientes en el sistema",\
|
||||
"026-Error al enviar trama ***send() fallo",\
|
||||
"027-No se encuentra Repositorio del cliente",\
|
||||
"028-Ha ocurrido algún error al tomar las particiones",\
|
||||
"029-Ha ocurrido algún problema en el proceso de inclusión del cliente. Se rechaza su petición",\
|
||||
"030-Ha ocurrido algún problema en el proceso de respuesta al comando",\
|
||||
"031-No se ha encontrado la acción a notificar es posible que se haya eliminado el registro",\
|
||||
"032-Ha ocurrido algún problema en el envío del comando",\
|
||||
"033-Error en el fichero de configuración del programa. No se ha definido el parámetro PATHSCRIPTS",\
|
||||
"034-Error en el fichero de configuración del programa. No se ha definido el parámetro URLMENU",\
|
||||
"035-Error en el fichero de configuración del programa. No se ha definido el parámetro URLMSG",\
|
||||
"036-No se ha podido recuperar la configuración de las particiones del disco",\
|
||||
"037-Ha ocurrido algún problema en el proceso de inclusión del cliente",\
|
||||
"038-No se ha podido establecer conexión con el Servidor de Administración",\
|
||||
"039-Ha ocurrido algún problema al procesar la trama recibida",\
|
||||
"040-Se han recibido parámetros con valores no válidos",\
|
||||
"041-Ha ocurrido algún problema en el proceso de inclusión del cliente",\
|
||||
"042-Ha ocurrido algún problema al enviar una petición de comandos o tareas pendientes al Servidor de Administración",\
|
||||
"043-Ha ocurrido algún problema al enviar una petición de comandos interactivos al Servidor de Administración",\
|
||||
"044-Ha ocurrido algún problema al enviar una respuesta de comandos al servidor",\
|
||||
"045-Ha ocurrido algún problema al recibir una petición de comandos o tareas pendientes desde el Servidor de Administración",\
|
||||
"046-Ha ocurrido algún problema al recibir un comando interactivo desde el Servidor de Administración",\
|
||||
"047-El cliente no está registrado en la tabla de sockest del sistema",\
|
||||
"048-Error al configurar opción BROADCAST para socket: setsockopt(SO_BROADCAST)",\
|
||||
"049-Error al enviar trama magic packet",\
|
||||
"050-Ha ocurrido algún problema al enviar un fichero por la red",\
|
||||
"051-Error en el fichero de configuración del programa. No se ha definido el parámetro PATHLOGFIL",\
|
||||
"052-No se puede crear archivo temporal para ejecución de Comandos",\
|
||||
"053-Ha ocurrido algún problema al procesar el Inventario Hardware del cliente",\
|
||||
"054-Existe un tipo de hardware que no está registrado",\
|
||||
"055-Ha ocurrido algún problema al actualizar el hardware del cliente",\
|
||||
"056-Error en el fichero de configuración del programa. No se ha definido el parámetro PATHINTERFACE",\
|
||||
"057-Ha ocurrido algún problema al enviar un archivo por la red",\
|
||||
"058-Ha ocurrido algún problema al recibir un archivo por la red",\
|
||||
"059-Error al crear la hebra DHCP o BOOTP",\
|
||||
"060-Error al crear la hebra TFTP",\
|
||||
"061-Error al crear socket para servicio DHCP",\
|
||||
"062-Error al enlazar socket con interface para servicio DHCP",\
|
||||
"063-No hay puertos libres para la hebra del servicio",\
|
||||
"064-Error al crear estructura de control para protocolo DHCP",\
|
||||
"065-Error al recibir mensaje DHCP. Se para el servicio",\
|
||||
"066-Error al crear la hebra cliente DHCP",\
|
||||
"067-Error al crear socket para servicio BOOTP",\
|
||||
"068-Error al enlazar socket con interface para servicio BOOTP",\
|
||||
"069-Error al crear estructura de control para protocolo BOOTP",\
|
||||
"070-Error al recibir mensaje BOOTP. Se para el servicio",\
|
||||
"071-Error al crear la hebra cliente BOOTP",\
|
||||
"072-Error al crear socket para servicio TFTP",\
|
||||
"073-Error al enlazar socket con interface para servicio TFTP",\
|
||||
"074-Error al crear estructura de control para protocolo TFTP",\
|
||||
"075-Error al recibir mensaje TFTP. Se para el servicio",\
|
||||
"076-Error al crear la hebra cliente TFTP",\
|
||||
"077-No se encontró opción DHCP",\
|
||||
"078-ERROR TFTP",\
|
||||
"079-Error al recibir mensaje TFTP en hebra cliente",\
|
||||
"080-Error al recibir mensaje DHCP",\
|
||||
"081-Error al crear socket de usuario para hebra",\
|
||||
"082-Ha ocurrido algún problema al procesar el Inventario software del cliente",\
|
||||
"083-Ha ocurrido algún problema al actualizar el software del cliente",\
|
||||
"084-Ha ocurrido algún problema al reiniciar la sesión del cliente",\
|
||||
"085-No se ha podido recuperar la dirección IP del cliente",\
|
||||
"086-Error al ejecutar el comando",\
|
||||
"087-Error al leer o escribir el contenido del archivo de eco de consola remota",\
|
||||
"088-Ha habido algún problerma al procesar la caché",\
|
||||
"089-Error en el fichero de configuración del programa. No se ha definido el parámetro URLMENU",\
|
||||
"090-Error en el fichero de configuración del programa. No se ha definido el parámetro URLMSG",\
|
||||
"091-Ha habido algún problema al enviar un mensaje de tipo petición al Servidor",\
|
||||
"092-Error en el fichero de configuración del programa. No se ha definido el parámetro IPLOCAL",\
|
||||
"093-No se puede cargar la librería Windows para trabajar con sockets",\
|
||||
"094-Ha habido algún problerma al procesar la actualización después de crear una imagen",\
|
||||
"095-Ha habido algún problerma al procesar la actualización después de restaurar una imagen",\
|
||||
"096-Ha habido algún problerma al procesar la actualización después de crear un software incremental",\
|
||||
"097-Este fichero de log está obsoleto, este proceso usa ahora syslog para gestionar los mensajes de log",\
|
||||
};
|
||||
// ________________________________________________________________________________________________________
|
||||
// Tabla de mensajes
|
||||
// ________________________________________________________________________________________________________
|
||||
const char* tbMensajes[]={"",\
|
||||
"001-Inicio de sesion",\
|
||||
"002-Petición de inclusión de cliente",\
|
||||
"003-Abriendo sesión en el servidor de Administración",\
|
||||
"004-Cliente iniciado",\
|
||||
"005-Ejecución de archivo Autoexec",\
|
||||
"006-Procesa comandos pendientes",\
|
||||
"007-Acciones pendientes procesadas",\
|
||||
"008-Ejecución del script",\
|
||||
"009-Parámetro del script",\
|
||||
"010-Ha ocurrido algún error en la creación del proceso hijo",\
|
||||
"011-Aviso: La información de salida del script excede de la longitud permitida. Puede haberse truncado",\
|
||||
"012-Información devuelta por el script",\
|
||||
"013-Estatus de finalización del script",\
|
||||
"014-Configuración de particiones",\
|
||||
"015-Enviando petición de inclusión en el sistema al Servidor de Administración",\
|
||||
"016-Recibiendo respuesta de inclusión desde el Servidor de Administración",\
|
||||
"017-Enviando petición de comandos o tareas pendientes al Servidor de Administración",\
|
||||
"018-Recibiendo respuesta de comandos o tareas pendientes desde el Servidor de Administración",\
|
||||
"019-Disponibilidad de comandos activada",\
|
||||
"020-Disponibilidad de comandos desactivada",\
|
||||
"021-Ejecución de comando",\
|
||||
"022-Sin eco",\
|
||||
"023-Procesando caché",\
|
||||
"024-Repositorio iniciado",\
|
||||
|
||||
};
|
||||
// ________________________________________________________________________________________________________
|
||||
// Prototipo de funciones
|
||||
// ________________________________________________________________________________________________________
|
||||
struct tm * tomaHora();
|
||||
void registraLog(const char *,const char *,int );
|
||||
void errorLog(const char *,int ,int);
|
||||
#define og_log(err, swe) errorLog(__FUNCTION__, err, swe)
|
||||
void errorInfo(const char *,char *);
|
||||
#define og_info(err) errorInfo(__FUNCTION__, err)
|
||||
void infoLog(int);
|
||||
void infoDebug(char*);
|
||||
BOOLEAN validacionParametros(int,char**,int);
|
||||
char* reservaMemoria(int);
|
||||
char* ampliaMemoria(char*,int);
|
||||
void liberaMemoria(void*);
|
||||
BOOLEAN initParametros(TRAMA*,int);
|
||||
int splitCadena(char **,char *, char);
|
||||
void sustituir(char *,char ,char );
|
||||
char* StrToUpper(char *);
|
||||
char* StrToLower(char *);
|
||||
void INTROaFINCAD(TRAMA*);
|
||||
void FINCADaINTRO(TRAMA*);
|
||||
int cuentaIPES(char*);
|
||||
char *tomaParametro(const char*,TRAMA*);
|
||||
char *copiaParametro(const char*,TRAMA *);
|
||||
BOOLEAN contieneIP(char *,char *);
|
||||
char* rTrim(char *);
|
||||
SOCKET TCPConnect(char *,char *);
|
||||
SOCKET abreConexion(void);
|
||||
BOOLEAN enviaMensaje(SOCKET *,TRAMA *,char);
|
||||
TRAMA* recibeMensaje(SOCKET *);
|
||||
BOOLEAN mandaTrama(SOCKET*,TRAMA*);
|
||||
BOOLEAN sendData(SOCKET *, char* ,int );
|
||||
BOOLEAN enviaTrama(SOCKET *,TRAMA *);
|
||||
TRAMA* recibeTrama(SOCKET*);
|
||||
BOOLEAN recData(SOCKET *,char*,int,int*);
|
||||
BOOLEAN sendFlag(SOCKET *, char* ,int );
|
||||
BOOLEAN recibeFlag(SOCKET*,TRAMA*);
|
||||
char* URLEncode(char *);
|
||||
char* URLDecode(char *);
|
||||
char* leeArchivo(char*);
|
||||
int lonArchivo(char *);
|
||||
BOOLEAN escribeArchivo(char *,char*);
|
||||
BOOLEAN sendArchivo(SOCKET *,char *);
|
||||
BOOLEAN recArchivo(SOCKET *,char *);
|
||||
SOCKET TCPConnect(char *,char*);
|
||||
|
||||
#include <stddef.h> /* for offsetof. */
|
||||
|
||||
#define container_of(ptr, type, member) ({ \
|
||||
typeof( ((type *)0)->member ) *__mptr = (ptr); \
|
||||
(type *)( (char *)__mptr - offsetof(type,member) );})
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
# makefile
|
||||
|
||||
# Nombre del proyecto
|
||||
PROYECTO := ogAdmAgent
|
||||
|
||||
# Directorio de instalación
|
||||
INSTALL_DIR := /opt/opengnsys
|
||||
|
||||
# Opciones de compilacion
|
||||
CFLAGS := $(shell mysql_config --cflags)
|
||||
CFLAGS += -g -Wall -I../../Includes
|
||||
CPPFLAGS := $(CFLAGS)
|
||||
|
||||
# Opciones de linkado
|
||||
LDFLAGS := -Wl,--no-as-needed $(shell mysql_config --libs) -lpthread
|
||||
|
||||
# Ficheros objetos
|
||||
OBJS := ../../Includes/Database.o sources/ogAdmAgent.o
|
||||
|
||||
|
||||
all: $(PROYECTO)
|
||||
|
||||
$(PROYECTO): $(OBJS)
|
||||
g++ $(LDFLAGS) $(OBJS) -o $(PROYECTO)
|
||||
|
||||
install: $(PROYECTO)
|
||||
cp $(PROYECTO) $(INSTALL_DIR)/sbin
|
||||
cp $(PROYECTO).cfg $(INSTALL_DIR)/etc
|
||||
|
||||
clean:
|
||||
rm -f $(PROYECTO) $(OBJS)
|
||||
|
||||
uninstall: clean
|
||||
rm -f /usr/local/sbin/$(PROYECTO) /usr/local/etc/$(PROYECTO).cfg
|
||||
|
||||
sources/%.o: sources/%.cpp
|
||||
g++ $(CPPFLAGS) -c -o"$@" "$<"
|
||||
|
||||
sources/%.o: sources/%.c
|
||||
gcc $(CFLAGS) -c -o"$@" "$<"
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
ServidorAdm=SERVERIP
|
||||
PUERTO=2008
|
||||
USUARIO=DBUSER
|
||||
PASSWORD=DBPASSWORD
|
||||
datasource=localhost
|
||||
CATALOG=DATABASE
|
||||
|
|
@ -0,0 +1,914 @@
|
|||
// ********************************************************************************************************
|
||||
// Servicio: ogAdmAgent
|
||||
// Autor: José Manuel Alonso (E.T.S.I.I.) Universidad de Sevilla
|
||||
// Fecha Creación: Marzo-2010
|
||||
// Fecha Última modificación: Marzo-2010
|
||||
// Nombre del fichero: ogAdmAgent.cpp
|
||||
// Descripción: Este fichero implementa el servicio agente del sistema. Revisa a intervalos
|
||||
// regulares la base de datos para comprobar si existen acciones programadas.
|
||||
// ********************************************************************************************************
|
||||
#include "ogAdmAgent.h"
|
||||
#include "ogAdmLib.c"
|
||||
//________________________________________________________________________________________________________
|
||||
// Función: tomaConfiguracion
|
||||
//
|
||||
// Descripción:
|
||||
// Lee el fichero de configuración del servicio
|
||||
// Parámetros:
|
||||
// filecfg : Ruta completa al fichero de configuración
|
||||
// Devuelve:
|
||||
// TRUE: Si el proceso es correcto
|
||||
// FALSE: En caso de ocurrir algún error
|
||||
//________________________________________________________________________________________________________
|
||||
BOOLEAN tomaConfiguracion(char* filecfg)
|
||||
{
|
||||
char modulo[] = "tomaConfiguracion()";
|
||||
|
||||
if (filecfg == NULL || strlen(filecfg) == 0) {
|
||||
errorLog(modulo, 1, FALSE); // Fichero de configuración del servicio vacío
|
||||
return (FALSE);
|
||||
}
|
||||
FILE *fcfg;
|
||||
long lSize;
|
||||
char * buffer, *lineas[MAXPRM], *dualparametro[2];
|
||||
int i, numlin, resul;
|
||||
|
||||
fcfg = fopen(filecfg, "rt");
|
||||
if (fcfg == NULL) {
|
||||
errorLog(modulo, 2, FALSE); // No existe fichero de configuración del servicio
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
fseek(fcfg, 0, SEEK_END);
|
||||
lSize = ftell(fcfg); // Obtiene tamaño del fichero.
|
||||
rewind(fcfg);
|
||||
buffer = (char*) reservaMemoria(lSize + 1); // Toma memoria para el buffer de lectura.
|
||||
if (buffer == NULL) { // No hay memoria suficiente para el buffer
|
||||
errorLog(modulo, 3, FALSE);
|
||||
return (FALSE);
|
||||
}
|
||||
fread(buffer, 1, lSize, fcfg); // Lee contenido del fichero
|
||||
buffer[lSize] = (char) NULL;
|
||||
fclose(fcfg);
|
||||
|
||||
servidoradm[0] = (char) NULL; //inicializar variables globales
|
||||
puerto[0] = (char) NULL;
|
||||
usuario[0] = (char) NULL;
|
||||
pasguor[0] = (char) NULL;
|
||||
datasource[0] = (char) NULL;
|
||||
catalog[0] = (char) NULL;
|
||||
|
||||
numlin = splitCadena(lineas, buffer, '\n');
|
||||
for (i = 0; i < numlin; i++) {
|
||||
splitCadena(dualparametro, lineas[i], '=');
|
||||
resul = strcmp(StrToUpper(dualparametro[0]), "SERVIDORADM");
|
||||
if (resul == 0)
|
||||
strcpy(servidoradm, dualparametro[1]);
|
||||
resul = strcmp(StrToUpper(dualparametro[0]), "PUERTO");
|
||||
if (resul == 0)
|
||||
strcpy(puerto, dualparametro[1]);
|
||||
resul = strcmp(StrToUpper(dualparametro[0]), "USUARIO");
|
||||
if (resul == 0)
|
||||
strcpy(usuario, dualparametro[1]);
|
||||
resul = strcmp(StrToUpper(dualparametro[0]), "PASSWORD");
|
||||
if (resul == 0)
|
||||
strcpy(pasguor, dualparametro[1]);
|
||||
resul = strcmp(StrToUpper(dualparametro[0]), "DATASOURCE");
|
||||
if (resul == 0)
|
||||
strcpy(datasource, dualparametro[1]);
|
||||
resul = strcmp(StrToUpper(dualparametro[0]), "CATALOG");
|
||||
if (resul == 0)
|
||||
strcpy(catalog, dualparametro[1]);
|
||||
}
|
||||
if (servidoradm[0] == (char) NULL) {
|
||||
errorLog(modulo, 4, FALSE); // Falta parámetro SERVIDORADM
|
||||
return (FALSE);
|
||||
}
|
||||
if (puerto[0] == (char) NULL) {
|
||||
errorLog(modulo, 5, FALSE); // Falta parámetro PUERTO
|
||||
return (FALSE);
|
||||
}
|
||||
if (usuario[0] == (char) NULL) {
|
||||
errorLog(modulo, 6, FALSE); // Falta parámetro USUARIO
|
||||
return (FALSE);
|
||||
}
|
||||
if (pasguor[0] == (char) NULL) {
|
||||
errorLog(modulo, 7, FALSE); // Falta parámetro PASSWORD
|
||||
return (FALSE);
|
||||
}
|
||||
if (datasource[0] == (char) NULL) {
|
||||
errorLog(modulo, 8, FALSE); // Falta parámetro DATASOURCE
|
||||
return (FALSE);
|
||||
}
|
||||
if (catalog[0] == (char) NULL) {
|
||||
errorLog(modulo, 9, FALSE); // Falta parámetro CATALOG
|
||||
return (FALSE);
|
||||
}
|
||||
return (TRUE);
|
||||
}
|
||||
// ________________________________________________________________________________________________________
|
||||
//
|
||||
// Función: diadelaSemana
|
||||
//
|
||||
// Descripción:
|
||||
// Calcula el número del día de la semana que corresponde a una fecha
|
||||
// Parámetros:
|
||||
// - dia: Un día
|
||||
// - mes: Un mes
|
||||
// - anno: Un año
|
||||
// Devuelve:
|
||||
// El número del día de la semana: 1=Lunes, 2=martes ... 6=sábado 7=domingo
|
||||
// ________________________________________________________________________________________________________
|
||||
|
||||
int diadelaSemana(WORD dia,WORD mes,WORD anno)
|
||||
{
|
||||
int i,cont,dias_anuales;
|
||||
int desplazamiento_dias=6;
|
||||
int orddiasem;
|
||||
|
||||
cont =0;
|
||||
for (i=1900;i<anno;i++){
|
||||
if (bisiesto(i)) dias_anuales=366; else dias_anuales=365;
|
||||
cont+=dias_anuales;
|
||||
}
|
||||
for (i=1;i<mes;i++){
|
||||
if (i!=2)
|
||||
cont+=dias_meses[i];
|
||||
else{
|
||||
if (bisiesto(anno))
|
||||
cont+=29;
|
||||
else
|
||||
cont+=28;
|
||||
}
|
||||
}
|
||||
cont+=dia+desplazamiento_dias;
|
||||
orddiasem=(cont%7);
|
||||
if(orddiasem==0) orddiasem=7;
|
||||
return(orddiasem);
|
||||
}
|
||||
// ________________________________________________________________________________________________________
|
||||
//
|
||||
// Función: bisiesto
|
||||
//
|
||||
// Descripción:
|
||||
// Calcula si un año es bisiesto o no lo es
|
||||
// Parámetros:
|
||||
// - anno: Un año
|
||||
// Devuelve:
|
||||
// TRUE si el año es bisiesto
|
||||
// FALSE si no es bisiesto
|
||||
// ________________________________________________________________________________________________________
|
||||
|
||||
BOOLEAN bisiesto(WORD anno){
|
||||
return(anno%4==0);
|
||||
}
|
||||
// ________________________________________________________________________________________________________
|
||||
//
|
||||
// Función: semanadelMes
|
||||
//
|
||||
// Descripción:
|
||||
// Calcula el número de semana perteneciente a un día del mes
|
||||
// Parámetros:
|
||||
// - ordiasem_1: Orden semanal (1,2...) del primer dia del mes que se pasa como parámetro
|
||||
// - diames: El mes concreto
|
||||
// Devuelve:
|
||||
// El número del día de la semana: 1=Lunes, 2=martes ... 6=sábado 7=domingo , de ese mes
|
||||
// ________________________________________________________________________________________________________
|
||||
|
||||
int semanadelMes(int ordiasem_1,int diames)
|
||||
{
|
||||
int nwdia,resto,cociente;
|
||||
|
||||
nwdia=diames+ordiasem_1-1;
|
||||
cociente=nwdia/7;
|
||||
resto=nwdia%7;
|
||||
if(resto>0) cociente++;
|
||||
return(cociente);
|
||||
}
|
||||
// ________________________________________________________________________________________________________
|
||||
//
|
||||
// Función: buscaAccion
|
||||
//
|
||||
// Descripción:
|
||||
// Busca en la base de datos, acciones programadas
|
||||
// Parámetros:
|
||||
// - db: Objeto base de datos (operativo)
|
||||
// - dia : Día actual del mes
|
||||
// - mes : mes en curso
|
||||
// - anno : Año en curso
|
||||
// - hora : Hora actual
|
||||
// - minutos : Minutos actuales
|
||||
// - diasemana : Dia de la semana 1=lunes,2=martes ... ( 0 Domingo)
|
||||
// Devuelve:
|
||||
// TRUE: Si el proceso es correcto
|
||||
// FALSE: En caso de ocurrir algún error
|
||||
// ________________________________________________________________________________________________________
|
||||
|
||||
BOOLEAN buscaAccion(Database db,WORD dia,WORD mes,WORD anno,WORD hora,WORD minutos,WORD diasemana)
|
||||
{
|
||||
char msglog[LONSTD], sqlstr[LONSQL];
|
||||
Table tbl;
|
||||
BYTE swampm,bitsemana;
|
||||
int ordsem,ordulsem,ordiasem_1,maxdias;
|
||||
int sesionprog;
|
||||
char modulo[] = "buscaAccion()";
|
||||
|
||||
/* Año de comienzo */
|
||||
anno=anno-ANNOREF; //
|
||||
/* Preparación hora */
|
||||
if(hora>11){
|
||||
hora-=12;
|
||||
swampm=1; // Es P.M.
|
||||
}
|
||||
else
|
||||
swampm=0; // Es am
|
||||
/* Preparación semana */
|
||||
if(diasemana==0) diasemana=7; // El domingo
|
||||
|
||||
// Cuestión semanas
|
||||
ordiasem_1=diadelaSemana(1,mes,anno+2009);
|
||||
ordsem=semanadelMes(ordiasem_1,dia); // Calcula el número de la semana
|
||||
if (mes!=2) // Toma el último día de ese mes
|
||||
maxdias=dias_meses[mes];
|
||||
else{
|
||||
if (bisiesto(anno+ANNOREF))
|
||||
maxdias=29;
|
||||
else
|
||||
maxdias=28;
|
||||
}
|
||||
ordulsem=semanadelMes(ordiasem_1,maxdias); // Calcula el número de la última semana
|
||||
bitsemana=HEX_semanas[ordsem];
|
||||
if(ordsem==ordulsem) // Si es la última semana del mes
|
||||
bitsemana|=HEX_semanas[6];
|
||||
|
||||
sprintf(sqlstr,"SELECT DISTINCT idprogramacion,tipoaccion,identificador,sesion,idcentro,"\
|
||||
"tareas.descripcion as descritarea"\
|
||||
" FROM programaciones"\
|
||||
" LEFT OUTER JOIN tareas ON tareas.idtarea=programaciones.identificador"\
|
||||
" WHERE suspendida=0 "\
|
||||
" AND (annos & %d <> 0) "\
|
||||
" AND (meses & %d<>0) "\
|
||||
" AND ((diario & %d<>0) OR (dias & %d<>0) OR (semanas & %d<>0))"\
|
||||
" AND (horas & %d<>0) AND ampm=%d AND minutos=%d",\
|
||||
HEX_annos[anno],\
|
||||
HEX_meses[mes],\
|
||||
HEX_dias[dia],\
|
||||
HEX_diasemana[diasemana],\
|
||||
bitsemana,\
|
||||
HEX_horas[hora],\
|
||||
swampm,minutos);
|
||||
|
||||
if (!db.Execute(sqlstr, tbl)) { // Error al leer
|
||||
errorLog(modulo, 21, FALSE);
|
||||
db.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
if(tbl.ISEOF()){
|
||||
return(TRUE); // No hay acciones programadas
|
||||
}
|
||||
|
||||
while(!tbl.ISEOF()){
|
||||
if(!tbl.Get("idprogramacion",idprogramacion)){
|
||||
tbl.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
if(!tbl.Get("tipoaccion",tipoaccion)){
|
||||
tbl.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
if(!tbl.Get("identificador",idtipoaccion)){
|
||||
tbl.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
if(!tbl.Get("sesion",sesionprog)){
|
||||
tbl.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
if(!tbl.Get("idcentro",idcentro)){
|
||||
tbl.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
if(tipoaccion==EJECUCION_COMANDO){ // Es una programación de un comando
|
||||
return(ejecutarComando(db,idprogramacion,sesionprog));
|
||||
}
|
||||
else{
|
||||
|
||||
if(tipoaccion==EJECUCION_TAREA){
|
||||
if(!tbl.Get("descritarea",descriaccion)){
|
||||
tbl.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
return(ejecutarTarea(db,idprogramacion,idtipoaccion));
|
||||
}
|
||||
else{
|
||||
if(tipoaccion==EJECUCION_RESERVA){
|
||||
EjecutarReserva(idtipoaccion,db); // Es una programación de un trabajo
|
||||
}
|
||||
}
|
||||
}
|
||||
tbl.MoveNext();
|
||||
}
|
||||
return(TRUE);
|
||||
}
|
||||
// ________________________________________________________________________________________________________
|
||||
//
|
||||
// Función: ejecutarComando
|
||||
//
|
||||
// Descripción:
|
||||
// Ejecuta un comando programado
|
||||
// Parámetros:
|
||||
// - db: Objeto base de datos (operativo)
|
||||
// - idcomando: Identificador del comando
|
||||
// - sesion: Sesión correspondiente al comando cuando se grabó en la tabla acciones
|
||||
// Devuelve:
|
||||
// TRUE: Si el proceso es correcto
|
||||
// FALSE: En caso de ocurrir algún error
|
||||
// ________________________________________________________________________________________________________
|
||||
|
||||
BOOLEAN ejecutarComando(Database db,int idprogramacion,int sesion )
|
||||
{
|
||||
struct tm* st;
|
||||
char msglog[LONSTD], sqlstr[LONSQL];
|
||||
char fechahorareg[24];
|
||||
char modulo[] = "ejecutarComando()";
|
||||
|
||||
st = tomaHora();
|
||||
sprintf(fechahorareg,"%d/%d/%d %d:%d:%d", st->tm_year + 1900, st->tm_mon + 1,
|
||||
st->tm_mday, st->tm_hour, st->tm_min, st->tm_sec);
|
||||
|
||||
sprintf(sqlstr,"UPDATE acciones SET estado=%d,idprogramacion=%d,fechahorareg='%s'"\
|
||||
" WHERE sesion=%d", ACCION_INICIADA,idprogramacion,fechahorareg,sesion);
|
||||
|
||||
if (!db.Execute(sqlstr)) { // Error al recuperar los datos
|
||||
errorLog(modulo, 21, FALSE);
|
||||
db.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
return(enviaPeticion(idprogramacion));
|
||||
}
|
||||
// ________________________________________________________________________________________________________
|
||||
//
|
||||
// Función: ejecutarProcedimiento
|
||||
//
|
||||
// Descripción:
|
||||
// Ejecuta un procedimiento programado
|
||||
// Parámetros:
|
||||
// - db: Objeto base de datos (operativo)
|
||||
// - idprocedimiento: Identificador del procedimiento
|
||||
// - ambito: Ámbito de aplicación
|
||||
// - idambito: Identificador del ámbito
|
||||
// - restrambito: cadena con los identificadores de los ordenadores a los que se aplica la acción
|
||||
// Devuelve:
|
||||
// TRUE: Si el proceso es correcto
|
||||
// FALSE: En caso de ocurrir algún error
|
||||
// ________________________________________________________________________________________________________
|
||||
|
||||
BOOLEAN ejecutarProcedimiento(Database db,int idprocedimiento,int ambito,int idambito,char* restrambito)
|
||||
{
|
||||
char msglog[LONSTD], sqlstr[LONSQL],*parametros;
|
||||
Table tbl;
|
||||
int procedimientoid,idcomando,lonprm;
|
||||
char modulo[] = "ejecutarProcedimiento()";
|
||||
|
||||
sprintf(sqlstr,"SELECT idcomando,procedimientoid,parametros,length(parametros) as lonprm"\
|
||||
" FROM procedimientos_acciones"\
|
||||
" WHERE idprocedimiento=%d ORDER BY orden",idprocedimiento);
|
||||
|
||||
if (!db.Execute(sqlstr, tbl)) { // Error al leer
|
||||
errorLog(modulo, 21, FALSE);
|
||||
db.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
if(tbl.ISEOF()){
|
||||
return(TRUE); // No exustde tarea
|
||||
}
|
||||
while(!tbl.ISEOF()){
|
||||
if(!tbl.Get("procedimientoid",procedimientoid)){
|
||||
tbl.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
if(procedimientoid>0){ // Procedimiento recursivo
|
||||
if(!ejecutarProcedimiento(db,procedimientoid,ambito,idambito,restrambito)){
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(!tbl.Get("lonprm",lonprm)){
|
||||
tbl.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
parametros = reservaMemoria(lonprm+1); // Reserva para almacenar los parametros del procedimiento
|
||||
if (parametros == NULL) {
|
||||
errorLog(modulo, 3, FALSE);
|
||||
return (FALSE);
|
||||
}
|
||||
if(!tbl.Get("parametros",parametros)){
|
||||
tbl.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
liberaMemoria(parametros);
|
||||
return (FALSE);
|
||||
}
|
||||
if(!tbl.Get("idcomando",idcomando)){
|
||||
tbl.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
if(!insertaComando(db,idcomando,parametros,idprocedimiento,ambito,idambito,restrambito)) {
|
||||
|
||||
liberaMemoria(parametros);
|
||||
return(false);
|
||||
}
|
||||
liberaMemoria(parametros);
|
||||
}
|
||||
tbl.MoveNext();
|
||||
}
|
||||
return(TRUE);
|
||||
}
|
||||
// ________________________________________________________________________________________________________
|
||||
//
|
||||
// Función: ejecutarTarea
|
||||
//
|
||||
// Descripción:
|
||||
// Ejecuta una tarea programada
|
||||
// Parámetros:
|
||||
// - db: Objeto base de datos (operativo)
|
||||
// - idtarea: Identificador de la tarea
|
||||
// - idprogramacion: Identificador de la programación
|
||||
// Devuelve:
|
||||
// TRUE: Si el proceso es correcto
|
||||
// FALSE: En caso de ocurrir algún error
|
||||
// ________________________________________________________________________________________________________
|
||||
|
||||
BOOLEAN ejecutarTarea(Database db, int idprogramacion, int idtarea)
|
||||
{
|
||||
char msglog[LONSTD], sqlstr[LONSQL];
|
||||
Table tbl;
|
||||
int tareaid,ambito,idambito,idprocedimiento,lonrestrambito;
|
||||
char* restrambito;
|
||||
char modulo[] = "ejecutarTarea()";
|
||||
|
||||
sprintf(sqlstr,"SELECT tareas_acciones.orden,tareas_acciones.idprocedimiento,tareas_acciones.tareaid,"\
|
||||
" tareas.ambito,tareas.idambito,tareas.restrambito,length(tareas.restrambito) as lonrestrambito"\
|
||||
" FROM tareas"\
|
||||
" INNER JOIN tareas_acciones ON tareas_acciones.idtarea=tareas.idtarea"\
|
||||
" WHERE tareas_acciones.idtarea=%d ORDER BY tareas_acciones.orden",idtarea);
|
||||
|
||||
if (!db.Execute(sqlstr, tbl)) { // Error al leer
|
||||
errorLog(modulo, 21, FALSE);
|
||||
db.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
if(tbl.ISEOF()){
|
||||
return(TRUE); // No existe tarea
|
||||
}
|
||||
|
||||
while(!tbl.ISEOF()){
|
||||
if(!tbl.Get("tareaid",tareaid)){
|
||||
tbl.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
if(tareaid>0){ // Tarea recursiva
|
||||
if(!ejecutarTarea(db,idprogramacion,tareaid)){
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(!tbl.Get("ambito",ambito)){
|
||||
tbl.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
if(!tbl.Get("idambito",idambito)){
|
||||
tbl.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
if(!tbl.Get("lonrestrambito",lonrestrambito)){
|
||||
tbl.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
restrambito = reservaMemoria(lonrestrambito+1);
|
||||
if (restrambito == NULL) {
|
||||
errorLog(modulo, 3, FALSE);
|
||||
return (FALSE);
|
||||
}
|
||||
if(!tbl.Get("restrambito",restrambito)){
|
||||
tbl.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
liberaMemoria(restrambito);
|
||||
return (FALSE);
|
||||
}
|
||||
liberaMemoria(restrambito);
|
||||
RecopilaIpesMacs(db,ambito,idambito,restrambito); // Recopila Ipes del ámbito
|
||||
if(!tbl.Get("idprocedimiento",idprocedimiento)){
|
||||
tbl.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
sesion=time(NULL);
|
||||
|
||||
if(!ejecutarProcedimiento(db,idprocedimiento,ambito,idambito,restrambito))
|
||||
return(FALSE);
|
||||
}
|
||||
tbl.MoveNext();
|
||||
}
|
||||
return(enviaPeticion(idprogramacion));
|
||||
}
|
||||
// ________________________________________________________________________________________________________
|
||||
//
|
||||
// Función: ejecutarTarea
|
||||
//
|
||||
// Descripción:
|
||||
// Registra un procedimiento para un ambito concreto
|
||||
// Parámetros:
|
||||
// - db: Objeto base de datos (operativo)
|
||||
// - idcomando: Identificador del comando
|
||||
// - idprocedimiento: Identificador del procedimiento
|
||||
// - ambito: Ámbito de aplicación
|
||||
// - idambito: Identificador del ámbito
|
||||
// - restrambito: cadena con los identificadores de los ordenadores a los que se aplica la acción
|
||||
// Devuelve:
|
||||
// TRUE: Si el proceso es correcto
|
||||
// FALSE: En caso de ocurrir algún error
|
||||
//________________________________________________________________________________________________________
|
||||
BOOLEAN insertaComando(Database db,int idcomando,char*parametros,int idprocedimiento,int ambito,int idambito,char*restrambito)
|
||||
{
|
||||
char msglog[LONSTD], sqlstr[LONSQL];
|
||||
struct tm* st;
|
||||
char *auxID[MAXIMOS_CLIENTES],*auxIP[MAXIMOS_CLIENTES];
|
||||
char fechahorareg[24];
|
||||
int i;
|
||||
char modulo[] = "insertaComando()";
|
||||
|
||||
if(concli==0) return(TRUE); // No hay ordenadores en el ámbito
|
||||
|
||||
st = tomaHora();
|
||||
sprintf(fechahorareg,"%d/%d/%d %d:%d:%d", st->tm_year + 1900, st->tm_mon + 1, st->tm_mday, st->tm_hour, st->tm_min, st->tm_sec);
|
||||
|
||||
splitCadena(auxID,cadenaid,',');
|
||||
splitCadena(auxIP,cadenaip,';');
|
||||
|
||||
for (i=0;i<concli;i++){
|
||||
sprintf(sqlstr,"INSERT INTO acciones (idordenador,tipoaccion,idtipoaccion,descriaccion,ip,"\
|
||||
"sesion,idcomando,parametros,fechahorareg,estado,resultado,ambito,idambito,"\
|
||||
"restrambito,idprocedimiento,idcentro,idprogramacion)"\
|
||||
" VALUES (%s,%d,%d,'%s','%s',%d,%d,'%s','%s',%d,%d,%d,%d,'%s',%d,%d,%d)",\
|
||||
auxID[i],tipoaccion,idtipoaccion,descriaccion,auxIP[i],sesion,idcomando,parametros,fechahorareg,\
|
||||
ACCION_INICIADA,ACCION_SINRESULTADO,ambito,idambito,restrambito,idprocedimiento,idcentro,idprogramacion);
|
||||
if (!db.Execute(sqlstr)) { // Error al recuperar los datos
|
||||
errorLog(modulo, 21, FALSE);
|
||||
db.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
}
|
||||
return(TRUE);
|
||||
}
|
||||
// _____________________________________________________________________________________________________________
|
||||
// Función: EjecutarReserva
|
||||
//
|
||||
// Descripción:
|
||||
// Registra una acción (Tarea) y la envía para su ejecución
|
||||
// Parámetros:
|
||||
// - idreserva : Identificador de la reserva
|
||||
// - db: una conexion ADO operativa
|
||||
// - parametros: Parámetros de la acción
|
||||
// _____________________________________________________________________________________________________________
|
||||
BOOLEAN EjecutarReserva(int idreserva,Database db )
|
||||
{
|
||||
|
||||
|
||||
return(true);
|
||||
}
|
||||
// _____________________________________________________________________________________________________________
|
||||
// Función: enviaPeticion
|
||||
//
|
||||
// Descripción:
|
||||
// Hace una petición al servidor para que actualice los ordenadores implicados en la programación
|
||||
// Parámetros:
|
||||
// - idprogramacion: Identificador de la programación
|
||||
// _____________________________________________________________________________________________________________
|
||||
BOOLEAN enviaPeticion(int idprogramacion)
|
||||
{
|
||||
int lon;
|
||||
TRAMA *ptrTrama;
|
||||
SOCKET socket_c;
|
||||
char modulo[] = "enviaPeticion()";
|
||||
|
||||
/* Envio de comandos a clientes */
|
||||
ptrTrama=(TRAMA *)reservaMemoria(sizeof(TRAMA));
|
||||
if (ptrTrama == NULL) { // No hay memoria suficiente para el bufer de las tramas
|
||||
errorLog(modulo, 3, FALSE);
|
||||
return(FALSE);
|
||||
}
|
||||
initParametros(ptrTrama,0);
|
||||
lon=sprintf(ptrTrama->parametros,"nfn=envioProgramacion\r"); // Nombre de la función a ejecutar en el servidor
|
||||
lon+=sprintf(ptrTrama->parametros+lon,"idp=%d\r",idprogramacion); // Configuración de los Sistemas Operativos del cliente
|
||||
|
||||
if(!enviaMensaje(&socket_c,ptrTrama,MSG_PETICION)){
|
||||
errorLog(modulo,91,FALSE);
|
||||
liberaMemoria(ptrTrama);
|
||||
return(FALSE);
|
||||
}
|
||||
liberaMemoria(ptrTrama);
|
||||
return(TRUE);
|
||||
}
|
||||
// _____________________________________________________________________________________________________________
|
||||
//
|
||||
// Función: RecopilaIpesMacs
|
||||
//
|
||||
// Descripción :
|
||||
// Recopila las IPes, las Macs y los identificadores de ordenadores de un ámbito determinado
|
||||
//
|
||||
// Especificaciones:
|
||||
// Esta Función recibe tres parámatros:
|
||||
// db : Un objeto Base de datos totalmente operativo
|
||||
// ambito: Tipo de ámbito
|
||||
// idambito: Identificador del ámbito
|
||||
// Devuelve:
|
||||
// Todas los identificadores de ordenadores , las ipes y las macs de los ordenadores que componen el ámbito
|
||||
// Para ellos habrá que tener declarada tres variables globales :
|
||||
// cadenaid,cadenaip y cadenamac
|
||||
// _____________________________________________________________________________________________________________
|
||||
|
||||
BOOLEAN RecopilaIpesMacs(Database db,int ambito,int idambito,char *restrambito)
|
||||
{
|
||||
char sqlstr[LONSQL];
|
||||
|
||||
concli=0;
|
||||
/* Reserva memoria al meno para caracter nulo */
|
||||
cadenaid=(char*) reservaMemoria(1);
|
||||
cadenaip=(char*) reservaMemoria(1);
|
||||
cadenamac=(char*) reservaMemoria(1);
|
||||
|
||||
switch(ambito){
|
||||
case AMBITO_CENTROS :
|
||||
sprintf(sqlstr,"SELECT ip,mac,idordenador FROM ordenadores INNER JOIN aulas WHERE ordenadores.idaula=aulas.idaula AND idcentro=%d ORDER BY ordenadores.idaula, ordenadores.ip;",idambito);
|
||||
RecorreOrdenadores(db,sqlstr);
|
||||
break;
|
||||
case AMBITO_GRUPOSAULAS :
|
||||
sprintf(sqlstr,"SELECT idgrupo FROM grupos WHERE idgrupo=%d AND tipo=%d",idambito,AMBITO_GRUPOSAULAS);
|
||||
RecorreGruposAulas(db,sqlstr);
|
||||
break;
|
||||
case AMBITO_AULAS :
|
||||
sprintf(sqlstr,"SELECT ip,mac,idordenador FROM ordenadores WHERE idaula=%d ORDER BY ip;",idambito);
|
||||
RecorreOrdenadores(db,sqlstr);
|
||||
break;
|
||||
case AMBITO_GRUPOSORDENADORES :
|
||||
sprintf(sqlstr,"SELECT idgrupo FROM gruposordenadores WHERE idgrupo=%d",idambito);
|
||||
RecorreGruposOrdenadores(db,sqlstr);
|
||||
break;
|
||||
case AMBITO_ORDENADORES :
|
||||
sprintf(sqlstr,"SELECT ip,mac,idordenador FROM ordenadores WHERE idordenador=%d",idambito);
|
||||
RecorreOrdenadores(db,sqlstr);
|
||||
break;
|
||||
default: // Se trata de un conjunto aleatorio de ordenadores
|
||||
sprintf(sqlstr,"SELECT ip,mac,idordenador FROM ordenadores WHERE idordenador IN (%s)",restrambito);
|
||||
RecorreOrdenadores(db,sqlstr);
|
||||
|
||||
}
|
||||
return (TRUE);
|
||||
}
|
||||
//________________________________________________________________________________________________________
|
||||
|
||||
BOOLEAN RecorreCentro(Database db, char* sqlstr)
|
||||
{
|
||||
char msglog[LONSTD];
|
||||
Table tbl;
|
||||
int idcentro;
|
||||
char modulo[] = "RecorreCentro()";
|
||||
|
||||
if (!db.Execute(sqlstr, tbl)) { // Error al leer
|
||||
errorLog(modulo, 21, FALSE);
|
||||
db.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
if(!tbl.ISEOF()){
|
||||
if(!tbl.Get("idcentro",idcentro)){
|
||||
tbl.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
sprintf(sqlstr,"SELECT idgrupo FROM grupos WHERE idcentro=%d AND grupoid=0 AND tipo=%d",idcentro,AMBITO_GRUPOSAULAS);
|
||||
RecorreGruposAulas(db,sqlstr);
|
||||
sprintf(sqlstr,"SELECT idaula FROM aulas WHERE idcentro=%d AND grupoid=0",idcentro);
|
||||
RecorreAulas(db,sqlstr);
|
||||
}
|
||||
return (TRUE);
|
||||
}
|
||||
//________________________________________________________________________________________________________
|
||||
|
||||
BOOLEAN RecorreGruposAulas(Database db, char* sqlstr)
|
||||
{
|
||||
char msglog[LONSTD];
|
||||
Table tbl;
|
||||
int idgrupo;
|
||||
char modulo[] = "RecorreGruposAulas()";
|
||||
|
||||
if (!db.Execute(sqlstr, tbl)) { // Error al leer
|
||||
errorLog(modulo, 21, FALSE);
|
||||
db.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
while(!tbl.ISEOF()){
|
||||
if(!tbl.Get("idgrupo",idgrupo)){
|
||||
tbl.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
sprintf(sqlstr,"SELECT idgrupo FROM grupos WHERE grupoid=%d AND tipo=%d",idgrupo,AMBITO_GRUPOSAULAS);
|
||||
RecorreGruposAulas(db,sqlstr);
|
||||
sprintf(sqlstr,"SELECT idaula FROM aulas WHERE grupoid=%d",idgrupo);
|
||||
RecorreAulas(db,sqlstr);
|
||||
tbl.MoveNext();
|
||||
}
|
||||
return (TRUE);
|
||||
}
|
||||
//________________________________________________________________________________________________________
|
||||
|
||||
BOOLEAN RecorreAulas(Database db, char* sqlstr)
|
||||
{
|
||||
char msglog[LONSTD];
|
||||
Table tbl;
|
||||
int idaula;
|
||||
char modulo[] = "RecorreAulas()";
|
||||
|
||||
if (!db.Execute(sqlstr, tbl)) { // Error al leer
|
||||
errorLog(modulo, 21, FALSE);
|
||||
db.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
while(!tbl.ISEOF()){
|
||||
if(!tbl.Get("idaula",idaula)){
|
||||
tbl.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
sprintf(sqlstr,"SELECT idgrupo FROM gruposordenadores WHERE idaula=%d AND grupoid=0",idaula);
|
||||
RecorreGruposOrdenadores(db,sqlstr);
|
||||
sprintf(sqlstr,"SELECT ip,mac,idordenador FROM ordenadores WHERE idaula=%d AND grupoid=0",idaula);
|
||||
RecorreOrdenadores(db,sqlstr);
|
||||
tbl.MoveNext();
|
||||
}
|
||||
return (TRUE);
|
||||
}
|
||||
//________________________________________________________________________________________________________
|
||||
|
||||
BOOLEAN RecorreGruposOrdenadores(Database db, char* sqlstr)
|
||||
{
|
||||
char msglog[LONSTD];
|
||||
Table tbl;
|
||||
int idgrupo;
|
||||
char modulo[] = "RecorreGruposOrdenadores()";
|
||||
|
||||
if (!db.Execute(sqlstr, tbl)) { // Error al leer
|
||||
errorLog(modulo, 21, FALSE);
|
||||
db.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
while(!tbl.ISEOF()){
|
||||
if(!tbl.Get("idgrupo",idgrupo)){
|
||||
tbl.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
sprintf(sqlstr,"SELECT idgrupo FROM gruposordenadores WHERE grupoid=%d",idgrupo);
|
||||
RecorreGruposOrdenadores(db,sqlstr);
|
||||
sprintf(sqlstr,"SELECT ip,mac,idordenador FROM ordenadores WHERE grupoid=%d",idgrupo);
|
||||
RecorreOrdenadores(db,sqlstr);
|
||||
tbl.MoveNext();
|
||||
}
|
||||
return (TRUE);
|
||||
}
|
||||
//________________________________________________________________________________________________________
|
||||
|
||||
BOOLEAN RecorreOrdenadores(Database db, char* sqlstr)
|
||||
{
|
||||
char msglog[LONSTD];
|
||||
Table tbl;
|
||||
int idordenador,o,p,m,lon;
|
||||
char ido[16],ip[LONIP],mac[LONMAC];
|
||||
char modulo[] = "RecorreOrdenadores()";
|
||||
|
||||
if (!db.Execute(sqlstr, tbl)) { // Error al leer
|
||||
errorLog(modulo, 21, FALSE);
|
||||
db.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
o=p=m=0;
|
||||
while(!tbl.ISEOF()){
|
||||
if(!tbl.Get("idordenador",idordenador)){
|
||||
tbl.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
if(!tbl.Get("ip",ip)){
|
||||
tbl.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
if(!tbl.Get("mac",mac)){
|
||||
tbl.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
return (FALSE);
|
||||
}
|
||||
sprintf(ido,"%d",idordenador);
|
||||
lon=strlen(ido);
|
||||
if(lon>16) lon=16;
|
||||
cadenaid=(char*) ampliaMemoria(cadenaid,o+lon+1);
|
||||
memcpy(&cadenaid[o],ido,lon);
|
||||
o+=lon;
|
||||
cadenaid[o++]=',';
|
||||
|
||||
lon=strlen(ip);
|
||||
if(lon>16) lon=LONIP;
|
||||
cadenaip=(char*) ampliaMemoria(cadenaip,p+lon+1);
|
||||
memcpy(&cadenaip[p],ip,lon);
|
||||
p+=lon;
|
||||
cadenaip[p++]=';';
|
||||
|
||||
lon=strlen(mac);
|
||||
if(lon>16) lon=LONMAC;
|
||||
cadenamac=(char*) ampliaMemoria(cadenamac,m+lon+1);
|
||||
memcpy(&cadenamac[m],mac,lon);
|
||||
m+=lon;
|
||||
cadenamac[m++]=';';
|
||||
|
||||
concli++;
|
||||
tbl.MoveNext();
|
||||
}
|
||||
if(o>0) o--;
|
||||
if(p>0) p--;
|
||||
if(m>0) m--;
|
||||
cadenaid[o]='\0';
|
||||
cadenaip[p]='\0';
|
||||
cadenamac[m]='\0';
|
||||
|
||||
return (TRUE);
|
||||
}
|
||||
// ********************************************************************************************************
|
||||
// PROGRAMA PRINCIPAL (SERVICIO)
|
||||
// ********************************************************************************************************
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int pseg;
|
||||
char msglog[LONSTD];
|
||||
struct tm* st;
|
||||
Database db;
|
||||
char modulo[] = "main()";
|
||||
|
||||
/* Validación de parámetros de ejecución y lectura del fichero de configuración del servicio */
|
||||
|
||||
if (!validacionParametros(argc, argv, 5)) // Valida parámetros de ejecución
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
if (!tomaConfiguracion(szPathFileCfg)) { // Toma parametros de configuracion
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Bucle principal del servicio */
|
||||
|
||||
while (TRUE){
|
||||
st = tomaHora();
|
||||
pseg=65-st->tm_sec; // Calcula segundos de inactividad de la hebra
|
||||
sleep(pseg);
|
||||
|
||||
// Toma la hora
|
||||
st = tomaHora();
|
||||
|
||||
if (!db.Open(usuario, pasguor, datasource, catalog)) { // Error de conexion
|
||||
errorLog(modulo, 20, FALSE);
|
||||
db.GetErrorErrStr(msglog);
|
||||
errorInfo(modulo, msglog);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
buscaAccion(db,st->tm_mday,st->tm_mon+1,st->tm_year+1900,st->tm_hour,st->tm_min,st->tm_wday );
|
||||
db.Close(); // Cierra conexión
|
||||
}
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
// ********************************************************************************************************
|
||||
// Servicio: ogAdmAgent
|
||||
// Autor: José Manuel Alonso (E.T.S.I.I.) Universidad de Sevilla
|
||||
// Fecha Creación: Marzo-2010
|
||||
// Fecha Última modificación: Marzo-2010
|
||||
// Nombre del fichero: ogAdmAgent.h
|
||||
// Descripción: Este fichero implementa el servicio agente del sistema. Revisa a intervalos
|
||||
// regulares la base de datos para comprobar si existen acciones programadas.
|
||||
// ********************************************************************************************************
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include </usr/include/mysql/mysql.h>
|
||||
#include "Database.h"
|
||||
#include "ogAdmLib.h"
|
||||
|
||||
// ________________________________________________________________________________________________________
|
||||
//
|
||||
// Valores hexadecimales para consultas
|
||||
// ________________________________________________________________________________________________________
|
||||
|
||||
WORD HEX_annos[]={0,0x0001,0x0002,0x0004,0x0008,0x0010,0x0020,0x0040,0x0080,0x0100,0x0200,0x0400,0x0800,0x1000,0x2000,0x4000,0x8000};
|
||||
WORD HEX_meses[]={0,0x0001,0x0002,0x0004,0x0008,0x0010,0x0020,0x0040,0x0080,0x0100,0x0200,0x0400,0x0800};
|
||||
int HEX_dias[]={0,0x00000001,0x00000002,0x00000004,0x00000008,0x00000010,0x00000020,0x00000040,0x00000080,0x00000100,0x00000200,
|
||||
0x00000400,0x00000800,0x00001000,0x00002000,0x00004000,0x00008000,0x00010000,0x00020000,0x00040000,0x00080000,
|
||||
0x00100000,0x00200000,0x00400000,0x00800000,0x01000000,0x02000000,0x04000000,0x08000000,0x10000000,0x20000000,0x40000000};
|
||||
WORD HEX_horas[]={0x0001,0x0002,0x0004,0x0008,0x0010,0x0020,0x0040,0x0080,0x0100,0x0200,0x0400,0x0800 };
|
||||
BYTE HEX_diasemana[]={0,0x01,0x02,0x04,0x08,0x10,0x20,0x40};
|
||||
BYTE HEX_semanas[]={0,0x01,0x02,0x04,0x08,0x10,0x20};
|
||||
WORD dias_meses[]={0,31,28,31,30,31,30,31,31,30,31,30,31};
|
||||
|
||||
// ________________________________________________________________________________________________________
|
||||
//
|
||||
// Variables globales
|
||||
// ________________________________________________________________________________________________________
|
||||
|
||||
char servidoradm[LONPRM]; // Dirección IP del servidor de administración
|
||||
char puerto[LONPRM]; // Puerto de comunicación
|
||||
char usuario[LONPRM]; // Usuario de acceso a la base de datos
|
||||
char pasguor[LONPRM]; // Password del usuario
|
||||
char datasource[LONPRM]; // Dirección IP del gestor de base de datos
|
||||
char catalog[LONPRM]; // Nombre de la base de datos
|
||||
|
||||
int idprogramacion;
|
||||
int tipoaccion,idtipoaccion;
|
||||
char descriaccion[250];
|
||||
char *cadenaid;
|
||||
char *cadenaip;
|
||||
char *cadenamac;
|
||||
int concli;
|
||||
int sesion;
|
||||
int idcentro;
|
||||
|
||||
// ________________________________________________________________________________________________________
|
||||
// Prototipo de funciones
|
||||
// ________________________________________________________________________________________________________
|
||||
BOOLEAN tomaConfiguracion(char*);
|
||||
int diadelaSemana(WORD,WORD,WORD);
|
||||
BOOLEAN bisiesto(WORD);
|
||||
BOOLEAN buscaAccion(Database,WORD,WORD,WORD,WORD,WORD,WORD);
|
||||
BOOLEAN ejecutarComando(Database,int,int );
|
||||
BOOLEAN ejecutarProcedimiento(Database,int,int,int,char*);
|
||||
BOOLEAN ejecutarTarea(Database,int, int);
|
||||
BOOLEAN insertaComando(Database,int,char*,int,int,int,char*);
|
||||
BOOLEAN EjecutarReserva(int,Database);
|
||||
BOOLEAN enviaPeticion(int);
|
||||
BOOLEAN RecopilaIpesMacs(Database,int,int,char *);
|
||||
BOOLEAN RecorreCentro(Database, char*);
|
||||
BOOLEAN RecorreGruposAulas(Database, char*);
|
||||
BOOLEAN RecorreAulas(Database, char*);
|
||||
BOOLEAN RecorreGruposOrdenadores(Database, char*);
|
||||
BOOLEAN RecorreOrdenadores(Database, char*);
|
||||
|
Binary file not shown.
|
@ -0,0 +1,285 @@
|
|||
#!/bin/bash
|
||||
PARM=`cat`
|
||||
#PARM=$@
|
||||
|
||||
|
||||
#TODO: ticket 379
|
||||
#buscar parametro de identificador de operacion.
|
||||
#usar parametro de identificacion para anexarlo al nombre de log
|
||||
#Comprobar si la variable está seteas.
|
||||
#Si no lo está setearla.
|
||||
#Si esta seteada (en progreso) salir.
|
||||
|
||||
|
||||
TIME=$SECONDS
|
||||
|
||||
BASEDIR=/opt/opengnsys
|
||||
PATH=$PATH:$BASEDIR/bin
|
||||
REPONAME=ogAdmRepo
|
||||
REPODIR="$BASEDIR/images/"
|
||||
|
||||
# Para las sincronizadas
|
||||
# BACKUP: Define si se realiza copia de seguridad al crear una imagen (true|false).
|
||||
# IMGFS: Sistema de ficheros al crear las sincronizadas tipo archivo (EXT4|BTRFS).
|
||||
[ -z $OGENGINECONFIGURATE ] && source $BASEDIR/client/etc/engine.cfg
|
||||
# FS segun la configuracion y la version del kernel. ext4 para < 3.7, para >= BTRFS
|
||||
KERNEL=$(file -bkr /opt/opengnsys/tftpboot/ogclient/ogvmlinuz |awk '/Linux/ {for(i=1;i<=NF;i++) if($i~/version/) {v=$(i+1);printf("%d",v);sub(/[0-9]*\./,"",v);printf(".%02d",v)}}')
|
||||
[ $KERNEL \< 3.07 ] && IMGFS="EXT4" || IMGFS=${IMGFS:-"BTRFS"}
|
||||
|
||||
# Añade registro de incidencias.
|
||||
function echolog () {
|
||||
logger --tag $0 --priority local0.info "$*"
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
function mountImage () {
|
||||
#@param 1 image_file
|
||||
#@param 2 mount_dir
|
||||
#@param 3 openciones mount
|
||||
[ "$3" != "" ] && OPTMOUNT=" -o $3 "
|
||||
# Si está montado nada que hacer
|
||||
df |grep "$2$" 2>&1 >/dev/null && return 0
|
||||
# FS de la imagen segun el contenido del archivo .img
|
||||
if file "$1" |grep -i -e " ext4 filesystem " 2>&1 > /dev/null ; then
|
||||
echolog "mount $OPTMOUNT -t ext4 $1 $2"
|
||||
mount $OPTMOUNT -t ext4 $1 $2
|
||||
else
|
||||
echolog "mount $OPTMOUNT -o compress=lzo $1 $2"
|
||||
mount $OPTMOUNT -o compress=lzo "$1" "$2"
|
||||
fi
|
||||
# Si esta montado da error 32, lo damos como bueno
|
||||
RETVAL=$?
|
||||
[ $RETVAL -eq 32 ] && RETVAL=0
|
||||
return $RETVAL
|
||||
}
|
||||
|
||||
|
||||
PARM1=$(echo $PARM | cut -f1 -d" ")
|
||||
PARM2=$(echo $PARM | cut -f2 -d" ")
|
||||
PARM3=$(echo $PARM | cut -f3 -d" ")
|
||||
PARM4=$(echo $PARM | cut -f4 -d" ")
|
||||
|
||||
# Determinamos el tipo de sistema de fichero de las imagenes segun el kernel que tenga
|
||||
|
||||
|
||||
case "$PARM1" in
|
||||
START_MULTICAST)
|
||||
#1 START_MULTICAST
|
||||
#2 fichero a enviar
|
||||
#3 opciones de multicast
|
||||
FILE="$PARM2"
|
||||
MCASTOPT="$PARM3"
|
||||
echolog "Ejecutar $(which sendFileMcast) $FILE $MCASTOPT"
|
||||
sendFileMcast $FILE $MCASTOPT |logger --tag $0 --priority local0.info
|
||||
case $? in
|
||||
1) echolog "Parametros insuficientes"
|
||||
exit 1 ;;
|
||||
2) echolog "Fichero no accesible"
|
||||
exit 2 ;;
|
||||
3) echolog "Sesion multicast no valida"
|
||||
exit 3 ;;
|
||||
esac
|
||||
;;
|
||||
CREATE_IMAGE)
|
||||
# Creamos/Redimensionamos el fichero de imagen y lo montamos para que se pueda escribir sobre el
|
||||
#1 CREATE_IMAGE
|
||||
#2 nombre imagen
|
||||
#3 tipo de imagen [ img | diff ]
|
||||
#4 tamaño imagen
|
||||
LOOPDEVICE=$(losetup -f)
|
||||
DIRMOUNT="$REPODIR/mount/$PARM2"
|
||||
if [ "$PARM3" == "img" ] ; then
|
||||
IMGEXT="img"
|
||||
else
|
||||
IMGEXT="img.diff"
|
||||
DIRMOUNT="$DIRMOUNT.diff"
|
||||
fi
|
||||
IMGFILE="$REPODIR/$PARM2.$IMGEXT"
|
||||
IMGDIR="$(dirname $IMGFILE)"
|
||||
[ -d $IMGDIR ] || mkdir -p $IMGDIR
|
||||
mkdir -p "$DIRMOUNT"
|
||||
|
||||
LOCKFILE="$IMGFILE.lock"
|
||||
|
||||
SIZEREQUIRED="$PARM4"
|
||||
|
||||
# Si existe la imagen hacemos copia de seguridad y la redimesionamos
|
||||
if [ -f "$IMGFILE" ]; then
|
||||
echolog "La imagen $IMGFILE ya existe."
|
||||
# TODO modificar ogGetImageSize
|
||||
IMGSIZE=$(ls -l --block-size=1024 $IMGFILE| cut -f5 -d" ")
|
||||
|
||||
if [ "$BACKUP" == "true" -o "$BACKUP" == "TRUE" -o $IMGSIZE -lt $SIZEREQUIRED ]; then
|
||||
# Si la imagen esta montada la desmonto
|
||||
if [ -r "$DIRMOUNT/ogimg.info" ]; then
|
||||
echolog "umount $DIRMOUNT"
|
||||
umount "$DIRMOUNT"
|
||||
[ $? -ne 0 ] && echolog "Error: No podemos desmontar la imagen para hacer copia de seguridad o redimensionar" && exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Copia de seguridad de la imagen
|
||||
if [ "$BACKUP" == "true" -o "$BACKUP" == "TRUE" ]; then
|
||||
echolog "Copia de seguridad de la imagen anterior"
|
||||
echolog "cp $IMGFILE $IMGFILE.ant"
|
||||
cp "$IMGFILE" "$IMGFILE.ant"
|
||||
echolog mv -f "$IMGFILE.torrent" "$IMGFILE.torrent.ant" 2>/dev/null
|
||||
mv -f "$IMGFILE.torrent" "$IMGFILE.torrent.ant" 2>/dev/null
|
||||
fi
|
||||
|
||||
# Redimensionamos la imagen al tamaño necesario
|
||||
if [ $IMGSIZE -lt $SIZEREQUIRED ];then
|
||||
echolog "Redimensionamos la imagen $IMGFILE al tamaño necesario: $SIZEREQUIRED"
|
||||
echolog "truncate --size=\">$SIZEREQUIRED\"k $IMGFILE"
|
||||
truncate --size=">$SIZEREQUIRED"k $IMGFILE 2>&1 |logger --tag $0 --priority local0.info
|
||||
# FS de la imagen segun el contenido del archivo .img
|
||||
if file "$IMGFILE" |grep -i -e " ext4 filesystem " 2>&1 > /dev/null ; then
|
||||
losetup $LOOPDEVICE "$IMGFILE"
|
||||
echolog "resize2fs -f $LOOPDEVICE"
|
||||
resize2fs -f $LOOPDEVICE |logger --tag $0 --priority local0.info
|
||||
else
|
||||
mount -o compress=lzo "$IMGFILE" "$DIRMOUNT"
|
||||
echolog "btrfs filesystem resize max $DIRMOUNT"
|
||||
btrfs filesystem resize max "$DIRMOUNT" 2>&1 |logger --tag $0 --priority local0.info
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
# Si no existe la imagen creamos el fichero.
|
||||
else
|
||||
echolog "Creamos la imagen $IMGFILE al tamaño necesario: $SIZEREQUIRED"
|
||||
touch "$IMGFILE"
|
||||
echolog "truncate --size=\">$SIZEREQUIRED\"k $IMGFILE"
|
||||
truncate --size=">$SIZEREQUIRED"k $IMGFILE 2>&1 |logger --tag $0 --priority local0.info
|
||||
#Formateamos imagen
|
||||
echo losetup $LOOPDEVICE "$IMGFILE"
|
||||
losetup $LOOPDEVICE "$IMGFILE"
|
||||
if [ $IMGFS == "EXT4" ] ; then
|
||||
echolog " mkfs.ext4 -i 4096 -b 4096 -L ${PARM2##*\/} $LOOPDEVICE"
|
||||
mkfs.ext4 -i 4096 -b 4096 -L ${PARM2##*\/} $LOOPDEVICE
|
||||
else
|
||||
echolog mkfs.btrfs -L ${PARM2##*\/} $LOOPDEVICE
|
||||
mkfs.btrfs -L ${PARM2##*\/} $LOOPDEVICE #&> $OGLOGCOMMAND
|
||||
fi
|
||||
fi
|
||||
# Montamos la imagen.
|
||||
mountImage "$IMGFILE" "$DIRMOUNT"
|
||||
if [ $? -ne 0 ]; then
|
||||
rmdir "$DIRMOUNT"
|
||||
echolog "Error al crear/redimensionar la imagen"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
#touch "$DIRMOUNT/ogimg.info"
|
||||
echo "mounted"> "$LOCKFILE"
|
||||
TIME2=$[SECONDS-TIME]
|
||||
echolog "Fin creación/redimension de la imagen: $[TIME2/60]m $[TIME2%60]s"
|
||||
# Si existe dispositivo loop lo borramos.
|
||||
[ $LOOPDEVICE ] && losetup -a| grep $LOOPDEVICE &> /dev/null && losetup -d $LOOPDEVICE
|
||||
# TODO: comprobar que no se quede el losetup bloqueado.
|
||||
|
||||
;;
|
||||
MOUNT_IMAGE)
|
||||
# Montamos el fichero imagen para que se pueda
|
||||
#1 MOUNT_IMAGE
|
||||
#2 nombre imagen
|
||||
#3 tipo de imagen [ img | diff ]
|
||||
DIRMOUNT="$REPODIR""mount/$PARM2"
|
||||
if [ "$PARM3" == "img" ] ; then
|
||||
IMGEXT="img"
|
||||
else
|
||||
IMGEXT="img.diff"
|
||||
DIRMOUNT="$DIRMOUNT.diff"
|
||||
fi
|
||||
IMGFILE="$REPODIR/$PARM2.$IMGEXT"
|
||||
echolog "Montamos la imagen $IMGFILE "
|
||||
mkdir -p "$DIRMOUNT"
|
||||
mountImage "$IMGFILE" "$DIRMOUNT" ro || (echolog "Error al montar la imagen"; exit 1)
|
||||
;;
|
||||
UMOUNT_IMAGE)
|
||||
# Desmontamos el fichero imagen.
|
||||
# Si el directorio esta ocupado no se desmontará
|
||||
#1 UMOUNT_IMAGE
|
||||
#2 nombre imagen
|
||||
#3 tipo de imagen [ img | diff ]
|
||||
IMGTYPE="$PARM3"
|
||||
DIRMOUNT="$REPODIR/mount/$PARM2"
|
||||
if [ "$IMGTYPE" == "img" ]; then
|
||||
IMGEXT="img"
|
||||
else
|
||||
DIRMOUNT="$DIRMOUNT.$IMGTYPE"
|
||||
IMGEXT="img.diff"
|
||||
fi
|
||||
LOCKFILE="$REPODIR/$PARM2.$IMGEXT.lock"
|
||||
echolog "Desmontamos la imagen $PARM2 $PARM3 "
|
||||
umount $DIRMOUNT
|
||||
rmdir $DIRMOUNT
|
||||
[ -f $LOCKFILE ] && sed -i s/mounted//g $LOCKFILE
|
||||
|
||||
;;
|
||||
REDUCE_IMAGE)
|
||||
# Reduce el archivo de la imagen a tamaño datos + 500M
|
||||
#1 REDUCE_IMAGE
|
||||
#2 Nombre Imagen
|
||||
#3 Tipo de imagen [ img |diff ]
|
||||
DIRMOUNT="${REPODIR}mount/${PARM2}"
|
||||
if [ "$PARM3" == "img" ] ; then
|
||||
IMGEXT="img"
|
||||
else
|
||||
IMGEXT="img.diff"
|
||||
DIRMOUNT="$DIRMOUNT.diff"
|
||||
fi
|
||||
IMGFILE="$REPODIR$PARM2.$IMGEXT"
|
||||
LOCKFILE="$IMGFILE.lock"
|
||||
[ ! -f $IMGFILE ] && echolog "Imagen $IMGFILE no existe" && exit 1
|
||||
|
||||
# Para imagenes EXT4 reduzco, para BTRFS solo desmonto.
|
||||
if file $IMGFILE |grep -i -e " ext4 filesystem " 2>&1 > /dev/null ; then
|
||||
|
||||
[ -d $DIRMOUNT ] || mkdir $DIRMOUNT
|
||||
mountImage "$IMGFILE" "$DIRMOUNT" || (echolog "Error al montar la imagen $IMGFILE"; exit 1)
|
||||
|
||||
|
||||
# Si el espacio libre menor que 200Mb desmontamos la imagen y nos salimos
|
||||
AVAILABLE=$(df -k|grep $DIRMOUNT|awk '{print $4}')
|
||||
if [ $AVAILABLE -lt 200000 ]; then
|
||||
echolog "reducir imagen REPO $PARM2 $IMGEXT. tamaño minimo, nada que hacer"
|
||||
umount $DIRMOUNT || (echolog "Error al desmontar la imagen $IMGFILE"; exit 1)
|
||||
else
|
||||
|
||||
# Calculamos la diferencia entre el tamaño interno y externo
|
||||
EXTSIZE=$(ls -l --block-size=1024 $IMGFILE | cut -f5 -d" ")
|
||||
INTSIZE=$(df -k|grep "$DIRMOUNT"|awk '{print $2}')
|
||||
let EDGESIZE=$EXTSIZE-$INTSIZE
|
||||
|
||||
echolog "reducir imagen REPO $PARM2 $IMGEXT, tamaño final: $ENDSIZE"
|
||||
umount $DIRMOUNT
|
||||
LOOPDEVICE=$(losetup -f)
|
||||
losetup $LOOPDEVICE "$IMGFILE"
|
||||
|
||||
# Redimensiono sistema de ficheros
|
||||
echolog "resize2fs -fpM $LOOPDEVICE "
|
||||
resize2fs -fpM $LOOPDEVICE # 2>&1 |logger --tag $0 --priority local0.info
|
||||
mountImage "$IMGFILE" "$DIRMOUNT"
|
||||
# Calculamos el tamaño final del archivo
|
||||
INTSIZE=$(df -k|grep "$DIRMOUNT"|awk '{print $2}')
|
||||
let EXTSIZE=$INTSIZE+$EDGESIZE
|
||||
umount $DIRMOUNT || (echolog "Error al desmontar la imagen $IMGFILE"; exit 1)
|
||||
# Si existe dispositivo loop lo borramos.
|
||||
[ $LOOPDEVICE ] && losetup -a| grep $LOOPDEVICE &> /dev/null && losetup -d $LOOPDEVICE
|
||||
# Corto el archivo al tamaño del sistema de ficheros
|
||||
echo "truncate --size=\"$EXTSIZE\"k $IMGFILE"
|
||||
echolog "truncate --size=\"$EXTSIZE\"k $IMGFILE"
|
||||
truncate --size="$EXTSIZE"k $IMGFILE
|
||||
fi
|
||||
else
|
||||
umount $DIRMOUNT || (echolog "Error al desmontar la imagen $IMGFILE"; exit 1)
|
||||
fi
|
||||
rmdir $DIRMOUNT
|
||||
echo "reduced" >$LOCKFILE
|
||||
|
||||
;;
|
||||
default)
|
||||
echolog "Solicitud con parametros \"$PARM\" no realizada, no registrada o con errores"
|
||||
;;
|
||||
esac
|
|
@ -0,0 +1,38 @@
|
|||
# makefile
|
||||
|
||||
# Nombre del proyecto
|
||||
PROYECTO := ogAdmServer
|
||||
|
||||
# Directorio de instalación
|
||||
INSTALL_DIR := /opt/opengnsys
|
||||
|
||||
# Opciones de compilacion
|
||||
CFLAGS := $(shell mysql_config --cflags)
|
||||
CFLAGS += -g -Wall -I../../Includes
|
||||
|
||||
# Opciones de linkado
|
||||
LDFLAGS := -Wl,--no-as-needed $(shell mysql_config --libs) -lev -ljansson -ldbi
|
||||
|
||||
# Ficheros objetos
|
||||
OBJS := sources/ogAdmServer.o sources/dbi.o
|
||||
|
||||
|
||||
all: $(PROYECTO)
|
||||
|
||||
$(PROYECTO): $(OBJS)
|
||||
gcc $(LDFLAGS) $(CFLAGS) $(OBJS) -o $(PROYECTO)
|
||||
|
||||
install: $(PROYECTO)
|
||||
cp $(PROYECTO) $(INSTALL_DIR)/sbin
|
||||
cp $(PROYECTO).cfg $(INSTALL_DIR)/etc
|
||||
|
||||
clean:
|
||||
rm -f $(PROYECTO) $(OBJS)
|
||||
|
||||
uninstall: clean
|
||||
rm -f /usr/local/sbin/$(PROYECTO) /usr/local/etc/$(PROYECTO).cfg
|
||||
|
||||
sources/%.o: sources/%.c
|
||||
gcc $(CFLAGS) -c -o"$@" "$<"
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
Sources/Services/ogAdmServer
|
|
@ -0,0 +1,8 @@
|
|||
ServidorAdm=SERVERIP
|
||||
PUERTO=2008
|
||||
USUARIO=DBUSER
|
||||
PASSWORD=DBPASSWORD
|
||||
datasource=localhost
|
||||
CATALOG=DATABASE
|
||||
INTERFACE=eth0
|
||||
APITOKEN=REPOKEY
|
|
@ -0,0 +1,37 @@
|
|||
#include "dbi.h"
|
||||
|
||||
struct og_dbi *og_dbi_open(struct og_dbi_config *config)
|
||||
{
|
||||
struct og_dbi *dbi;
|
||||
|
||||
dbi = (struct og_dbi *)malloc(sizeof(struct og_dbi));
|
||||
if (!dbi)
|
||||
return NULL;
|
||||
|
||||
dbi_initialize_r(NULL, &dbi->inst);
|
||||
dbi->conn = dbi_conn_new_r("mysql", dbi->inst);
|
||||
if (!dbi->conn) {
|
||||
free(dbi);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dbi_conn_set_option(dbi->conn, "host", config->host);
|
||||
dbi_conn_set_option(dbi->conn, "username", config->user);
|
||||
dbi_conn_set_option(dbi->conn, "password", config->passwd);
|
||||
dbi_conn_set_option(dbi->conn, "dbname", config->database);
|
||||
dbi_conn_set_option(dbi->conn, "encoding", "UTF-8");
|
||||
|
||||
if (dbi_conn_connect(dbi->conn) < 0) {
|
||||
free(dbi);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dbi;
|
||||
}
|
||||
|
||||
void og_dbi_close(struct og_dbi *dbi)
|
||||
{
|
||||
dbi_conn_close(dbi->conn);
|
||||
dbi_shutdown_r(dbi->inst);
|
||||
free(dbi);
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
#ifndef __OG_DBI
|
||||
#define __OG_DBI
|
||||
|
||||
#include <dbi/dbi.h>
|
||||
|
||||
struct og_dbi_config {
|
||||
const char *user;
|
||||
const char *passwd;
|
||||
const char *host;
|
||||
const char *database;
|
||||
};
|
||||
|
||||
struct og_dbi {
|
||||
dbi_conn conn;
|
||||
dbi_inst inst;
|
||||
};
|
||||
|
||||
struct og_dbi *og_dbi_open(struct og_dbi_config *config);
|
||||
void og_dbi_close(struct og_dbi *db);
|
||||
#define OG_DB_IP_MAXLEN 15
|
||||
|
||||
#endif
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,60 @@
|
|||
// ********************************************************************************************************
|
||||
// Servicio: ogAdmServer
|
||||
// Autor: José Manuel Alonso (E.T.S.I.I.) Universidad de Sevilla
|
||||
// Fecha Creación: Marzo-2010
|
||||
// Fecha Última modificación: Marzo-2010
|
||||
// Nombre del fichero: ogAdmServer.h
|
||||
// Descripción: Este fichero implementa el servicio de administración general del sistema
|
||||
// ********************************************************************************************************
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdbool.h>
|
||||
#include </usr/include/mysql/mysql.h>
|
||||
#include "ogAdmLib.h"
|
||||
// ________________________________________________________________________________________________________
|
||||
// Variables globales
|
||||
// ________________________________________________________________________________________________________
|
||||
char servidoradm[LONPRM]; // Dirección IP del servidor de administración
|
||||
char puerto[LONPRM]; // Puerto de comunicación
|
||||
|
||||
struct og_client;
|
||||
|
||||
typedef struct{ // Estructura usada para guardar información de los clientes
|
||||
char ip[LONIP]; // IP del cliente
|
||||
char estado[4]; // Tipo de Sistema Operativo en que se encuentra el cliente
|
||||
struct og_client *cli;
|
||||
}SOCKETCL;
|
||||
SOCKETCL tbsockets[MAXIMOS_CLIENTES];
|
||||
|
||||
struct og_dbi;
|
||||
|
||||
bool registraCliente(char *);
|
||||
bool procesoInclusionClienteWinLnx(int socket, TRAMA*,int*,char*);
|
||||
bool procesoInclusionCliente(struct og_client *, TRAMA*);
|
||||
bool clienteExistente(char *,int *);
|
||||
bool clienteDisponible(char *,int *);
|
||||
bool actualizaConfiguracion(struct og_dbi *,char* ,int);
|
||||
bool recorreProcedimientos(struct og_dbi *,char* ,FILE*,char*);
|
||||
bool buscaComandos(char *,TRAMA *,int *);
|
||||
bool respuestaConsola(int socket, TRAMA *,int);
|
||||
bool enviaComando(TRAMA *ptrTrama,const char*);
|
||||
bool Levanta(char**, char**, char**, int, char*);
|
||||
bool WakeUp(int,char*,char*,char*,char*);
|
||||
void PasaHexBin(char *,char *);
|
||||
bool actualizaCreacionImagen(struct og_dbi *,char*,char*,char*,char*,char*,char*);
|
||||
bool actualizaRestauracionImagen(struct og_dbi *,char*,char*,char*,char*,char*);
|
||||
bool actualizaHardware(struct og_dbi *dbi, char* ,char*,char*,char*);
|
||||
bool cuestionPerfilHardware(struct og_dbi *dbi,char*,char*,int,char*,char*,int *,int);
|
||||
bool actualizaSoftware(struct og_dbi *, char* , char* , char*,char*,char*);
|
||||
bool cuestionPerfilSoftware(struct og_dbi *, char*, char*,int,int,char*,char*,char*,int *,int);
|
||||
|
||||
int checkDato(struct og_dbi *,char*,const char*,const char*,const char*);
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
curl -X GET http://127.0.0.1:8888/clients
|
|
@ -0,0 +1,8 @@
|
|||
ServidorAdm=localhost
|
||||
PUERTO=2008
|
||||
USUARIO=test-db
|
||||
PASSWORD=test-db
|
||||
datasource=localhost
|
||||
CATALOG=test-db
|
||||
INTERFACE=eth1
|
||||
APITOKEN=07b3bfe728954619b58f0107ad73acc1
|
|
@ -0,0 +1 @@
|
|||
{"clients":["192.168.56.11"],"disk":"1","partition":"1","code":"131","id":"8","name":"debianbasica","repository":"192.168.56.10","sync_params":{"sync":"1","diff":"0","remove":"1","compress":"0","cleanup":"0","cache":"0","cleanup_cache":"0","remove_dst":"0"}}
|
|
@ -0,0 +1 @@
|
|||
{ "clients" : [ "192.168.2.1" ], "disk" : "1", "partition" : "1", "code" : "1", "id" : "1", "name" : "test", "repository" : "192.168.2.4" }
|
|
@ -0,0 +1 @@
|
|||
{"clients":["192.168.56.11"], "disk":"1", "partition":"1", "id":"3", "name":"basica1", "repository":"192.168.56.10", "sync_params":{"sync":"1", "path":"", "diff":"0", "diff_id":"4", "diff_name":"p2", "remove":"1", "compress":"0", "cleanup":"0", "cache":"0", "cleanup_cache":"0", "remove_dst":"0"}}
|
|
@ -0,0 +1 @@
|
|||
{ "clients" : [ "192.168.2.1", "192.168.2.2" ] }
|
|
@ -0,0 +1 @@
|
|||
{ "clients" : [ "192.168.2.1", "192.168.2.2" ]}
|
|
@ -0,0 +1 @@
|
|||
{ "clients" : [ "192.168.2.1", "192.168.2.2" ], "run" : "ls" }
|
|
@ -0,0 +1 @@
|
|||
{ "clients" : [ "192.168.2.1", "192.168.2.2" ] }
|
|
@ -0,0 +1 @@
|
|||
{ "clients" : [ "192.168.2.1", "192.168.2.2" ] }
|
|
@ -0,0 +1 @@
|
|||
{"clients":["192.168.56.11"],"disk":"1","partition":"1","id":"9","name":"test","repository":"192.168.56.10","profile":"17","type":"UNICAST","sync_params":{"path":"","method":"1","sync":"1","diff":"0","remove":"1","compress":"0","cleanup":"0","cache":"0","cleanup_cache":"0","remove_dst":"0"}}
|
|
@ -0,0 +1 @@
|
|||
{ "clients" : [ "192.168.56.11" ], "disk" : "1", "partition" : "1", "name" : "test", "repository" : "192.168.56.10", "type" : "UNICAST", "profile": "1", "id": "1"}
|
|
@ -0,0 +1 @@
|
|||
{"clients":["192.168.56.11"],"disk":"1","partition":"1","id":"9","name":"test","repository":"192.168.56.10","profile":"17","type":"UNICAST","sync_params":{"diff_id":"1","diff_name":"test","path":"","method":"1","sync":"1","diff":"0","remove":"1","compress":"0","cleanup":"0","cache":"0","cleanup_cache":"0","remove_dst":"0"}}
|
|
@ -0,0 +1,41 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import subprocess, glob, os
|
||||
|
||||
sql_data = "INSERT INTO aulas (nombreaula, idcentro, urlfoto, grupoid, ubicacion, puestos, modomul, ipmul, pormul, velmul, router, netmask, ntp, dns, proxy, modp2p, timep2p) VALUES ('Aula virtual', 1, 'aula.jpg', 0, 'Despliegue virtual con Vagrant.', 5, 2, '239.194.2.11', 9000, 70, '192.168.56.1', '255.255.255.0', '', '', '', 'peer', 30); INSERT INTO ordenadores (nombreordenador, ip, mac, idaula, idrepositorio, idperfilhard, idmenu, idproautoexec, grupoid, router, mascara, arranque, netiface, netdriver, fotoord) VALUES ('pc2', '192.168.2.1', '0800270E6501', 1, 1, 0, 0, 0, 0, '192.168.56.1', '255.255.255.0', '00unknown', 'eth0', 'generic', 'fotoordenador.gif'), ('pc2', '192.168.2.2', '0800270E6502', 1, 1, 0, 0, 0, 0, '192.168.56.1', '255.255.255.0', '00unknown', 'eth0', 'generic', 'fotoordenador.gif');"
|
||||
|
||||
sql_create_user = "CREATE USER 'test-db'@'hostname'; GRANT ALL PRIVILEGES ON *.* To 'test-db'@'hostname' IDENTIFIED BY 'test-db';"
|
||||
|
||||
sql_delete_user = "DROP USER 'test-db'@'hostname';"
|
||||
|
||||
def start_mysql():
|
||||
|
||||
subprocess.run(['mysqladmin', 'drop', '-f', 'test-db'],
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
subprocess.run(['mysqladmin', 'create', 'test-db'])
|
||||
subprocess.run('mysql --default-character-set=utf8 test-db < ../../../../Database/ogAdmBD.sql', shell=True)
|
||||
subprocess.run(['mysql', '-D', 'test-db', '-e', sql_data])
|
||||
subprocess.run(['mysql', '-D', 'test-db', '-e', sql_create_user])
|
||||
|
||||
def stop_mysql():
|
||||
|
||||
subprocess.run(['mysql', '-D', 'test-db', '-e', sql_delete_user])
|
||||
subprocess.run(['mysqladmin', 'drop', '-f', 'test-db'])
|
||||
|
||||
if os.getuid() is not 0:
|
||||
print('You need to be root to run these tests :-)')
|
||||
exit()
|
||||
|
||||
if os.path.isfile('../ogAdmServer') is not True:
|
||||
print('You need to build the ogAdmServer binary to run these tests :-)')
|
||||
exit()
|
||||
|
||||
start_mysql();
|
||||
|
||||
subprocess.Popen(['../ogAdmServer', '-f', 'config/ogAdmServer.cfg'])
|
||||
|
||||
subprocess.run('python3 -m unittest discover -s units -v', shell=True)
|
||||
|
||||
stop_mysql();
|
||||
|
||||
subprocess.run(['pkill', 'ogAdmServer'])
|
|
@ -0,0 +1,22 @@
|
|||
API_KEY="07b3bfe728954619b58f0107ad73acc1"
|
||||
|
||||
curl -X POST -H "Authorization: $API_KEY" http://127.0.0.1:8888/clients -d @post_clients.json
|
||||
curl -X GET -H "Authorization: $API_KEY" http://127.0.0.1:8888/clients
|
||||
curl -X POST -H "Authorization: $API_KEY" http://127.0.0.1:8888/wol -d @wol.json
|
||||
curl -X POST -H "Authorization: $API_KEY" http://127.0.0.1:8888/shell/run -d @post_shell_run.json
|
||||
curl -X POST -H "Authorization: $API_KEY" http://127.0.0.1:8888/shell/output -d @post_shell_output.json
|
||||
curl -X POST -H "Authorization: $API_KEY" http://127.0.0.1:8888/session -d @session.json
|
||||
curl -X POST -H "Authorization: $API_KEY" http://127.0.0.1:8888/poweroff -d @poweroff.json
|
||||
curl -X POST -H "Authorization: $API_KEY" http://127.0.0.1:8888/reboot -d @reboot.json
|
||||
curl -X POST -H "Authorization: $API_KEY" http://127.0.0.1:8888/stop -d @stop.json
|
||||
curl -X POST -H "Authorization: $API_KEY" http://127.0.0.1:8888/refresh -d @refresh.json
|
||||
curl -X POST -H "Authorization: $API_KEY" http://127.0.0.1:8888/hardware -d @post_clients.json
|
||||
curl -X POST -H "Authorization: $API_KEY" http://127.0.0.1:8888/software -d @post_clients.json
|
||||
curl -X POST -H "Authorization: $API_KEY" http://127.0.0.1:8888/image/create -d @create_image.json
|
||||
curl -X POST -H "Authorization: $API_KEY" http://127.0.0.1:8888/image/restore -d @restore_image.json
|
||||
curl -X POST -H "Authorization: $API_KEY" http://127.0.0.1:8888/setup -d @setup_image.json
|
||||
curl -X POST -H "Authorization: $API_KEY" http://127.0.0.1:8888/image/create/basic -d @create_basic_image.json
|
||||
curl -X POST -H "Authorization: $API_KEY" http://127.0.0.1:8888/image/create/incremental -d @create_incremental_image.json
|
||||
curl -X POST -H "Authorization: $API_KEY" http://127.0.0.1:8888/image/restore/basic -d @restore_basic_image.json
|
||||
curl -X POST -H "Authorization: $API_KEY" http://127.0.0.1:8888/image/restore/incremental -d @restore_incremental_image.json
|
||||
curl -X POST -H "Authorization: $API_KEY" http://127.0.0.1:8888/run/schedule -d @run_schedule.json
|
|
@ -0,0 +1 @@
|
|||
{"clients":["192.168.56.11"]}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue