diff options
author | Jonathan Bauer | 2016-12-23 13:12:09 +0100 |
---|---|---|
committer | Jonathan Bauer | 2016-12-23 13:12:09 +0100 |
commit | 6806ae4a850fc7785a8c05304237cf53b5b8f951 (patch) | |
tree | b1dd8413d6c7b9a250251da7f0d49bb52b4ddc57 /core/modules/run-virt | |
parent | wrong kernel version variable used (diff) | |
download | mltk-6806ae4a850fc7785a8c05304237cf53b5b8f951.tar.gz mltk-6806ae4a850fc7785a8c05304237cf53b5b8f951.tar.xz mltk-6806ae4a850fc7785a8c05304237cf53b5b8f951.zip |
merge with latest dev version (tm-scripts commit f5a59daf8d70a9027118292cd40b18c221897408)
Diffstat (limited to 'core/modules/run-virt')
49 files changed, 3585 insertions, 154 deletions
diff --git a/core/modules/run-virt/data/opt/openslx/scripts/pam_script_auth.d/99-run_virt_credentials b/core/modules/run-virt/data/opt/openslx/scripts/pam_script_auth.d/99-run_virt_credentials new file mode 100644 index 00000000..a03d8886 --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/scripts/pam_script_auth.d/99-run_virt_credentials @@ -0,0 +1,41 @@ +#!/bin/ash +# This is being sourced and running in ash + +if [ -n "$TEMP_HOME_DIR" ]; then + if [ -z "$PAM_TTY" ] || [ "x$PAM_TTY" = "x:0" ]; then + # Pass on network path to home directory + if [ -z "$PERSISTENT_NETPATH" ]; then + PERSISTENT_NETPATH=$(grep -m1 -F " ${PERSISTENT_HOME_DIR} " "/proc/mounts" | awk '{print $1}') + fi + if [ -n "$PERSISTENT_NETPATH" ]; then + [ "x${PERSISTENT_NETPATH:0:2}" = "x//" ] && PERSISTENT_NETPATH=$(echo "$PERSISTENT_NETPATH" | tr '/' '\') + echo "${PERSISTENT_NETPATH}" > "${TEMP_HOME_DIR}/.home" + chmod 0644 "${TEMP_HOME_DIR}/.home" + fi + # pwdaemon + # Figure out username + XUSER="${REAL_ACCOUNT}" + [ -z "$XUSER" ] && XUSER="${PAM_USER}" + # Guess domain + XDOMAIN= + if [ -n "$PERSISTENT_HOME_DIR" ]; then + XDOMAIN=$(grep -F " ${PERSISTENT_HOME_DIR} " "/proc/mounts" | grep -m1 -F 'domain=' | sed -r 's/^.*[ ,]domain=([^ ,]+)[ ,].*$/\1/g') + fi + if [ -z "$XDOMAIN" ]; then + XDOMAIN=$(grep -m1 -i '^BASE\s*DC=' "/etc/ldap.conf" | sed -r 's/^BASE\s*DC=([^,;]+).*$/\1/I') + fi + if [ -z "$XDOMAIN" ]; then + XDOMAIN=$(grep -m1 -i '^ldap_search_base\s*=\s*DC=' "/etc/sssd/sssd.conf" | sed -r 's/^ldap_search_base\s*=\s*DC=([^,;]+).*$/\1/I') + fi + if [ -n "$XDOMAIN" ]; then + XDOMAIN=$(echo "$XDOMAIN" | tr '[a-z]' '[A-Z]') + else + XDOMAIN="WORKGROUP" + fi + USERNAME="$XDOMAIN\\$XUSER" PASSWORD="$PAM_AUTHTOK" PWSOCKET="${TEMP_HOME_DIR}/.pwsocket" su -c 'pwdaemon --daemon &' "${PAM_USER}" & + unset XUSER XDOMAIN + fi +fi + +true + diff --git a/core/modules/run-virt/data/opt/openslx/scripts/pam_script_ses_close.d/runvirt-firewall-clear b/core/modules/run-virt/data/opt/openslx/scripts/pam_script_ses_close.d/runvirt-firewall-clear new file mode 100644 index 00000000..dab08190 --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/scripts/pam_script_ses_close.d/runvirt-firewall-clear @@ -0,0 +1,17 @@ +#!/bin/ash + +# Sourced by pam_script_ses_close + +runvirt_fw_clear () { + iptables -w -F runvirt-INPUT + ip6tables -w -F runvirt-INPUT + iptables -w -F runvirt-OUTPUT + ip6tables -w -F runvirt-OUTPUT +} + +if [ "x$PAM_TTY" = "x:0" ]; then + runvirt_fw_clear > /dev/null 2>&1 +fi + +true + diff --git a/core/modules/run-virt/data/opt/openslx/scripts/systemd-mount_vm_store b/core/modules/run-virt/data/opt/openslx/scripts/systemd-mount_vm_store index 9d478918..235cf4be 100755 --- a/core/modules/run-virt/data/opt/openslx/scripts/systemd-mount_vm_store +++ b/core/modules/run-virt/data/opt/openslx/scripts/systemd-mount_vm_store @@ -13,8 +13,12 @@ OUTFILE=$(mktemp) # no bash, so this is a bit ugly... if [ "${SLX_VM_NFS#//}" = "${SLX_VM_NFS}" ]; then # doesn't start with '//' -> assume NFS - /opt/openslx/bin/timeout -t 10 -s 9 mount -v -t nfs -o ro,async,nolock "$SLX_VM_NFS" /mnt/vmstore + /opt/openslx/bin/timeout -t 6 -s 9 mount -v -t nfs -o ro,async,nolock "$SLX_VM_NFS" /mnt/vmstore RET=$? + if [ "$RET" != "0" ]; then + /opt/openslx/bin/timeout -t 6 -s 9 mount -v -t nfs -o vers=3,ro,async,nolock "$SLX_VM_NFS" /mnt/vmstore + RET=$? + fi else # starts with '//' -> assume CIFS export USER="$SLX_VM_NFS_USER" @@ -25,15 +29,14 @@ else RET=$? fi > "$OUTFILE" 2>&1 -if [ $RET -ne 0 ]; then +if [ "$RET" -ne "0" ]; then if [ -s "$OUTFILE" ]; then - slxlog "mount-vmstore-fail" "Mounting '$SLX_VM_NFS' failed. VMs will not boot." "$OUTFILE" - sleep 1 + slxlog --delete "mount-vmstore-fail" "Mounting '$SLX_VM_NFS' failed. VMs will not boot." "$OUTFILE" else slxlog "mount-vmstore-fail" "Mounting '$SLX_VM_NFS' failed. VMs will not boot." + rm -f -- "$OUTFILE" fi fi -rm -f -- "$OUTFILE" exit $RET diff --git a/core/modules/run-virt/data/opt/openslx/scripts/systemd-run_virt_env b/core/modules/run-virt/data/opt/openslx/scripts/systemd-run_virt_env index b8236600..a86b2d0b 100755 --- a/core/modules/run-virt/data/opt/openslx/scripts/systemd-run_virt_env +++ b/core/modules/run-virt/data/opt/openslx/scripts/systemd-run_virt_env @@ -28,27 +28,7 @@ export PATH=$PATH:/opt/openslx/bin:/opt/openslx/sbin # from plugins/vmchooser/XX_vmchooser.sh VMCHOOSER_DIR="/opt/openslx/vmchooser" VMCHOOSER_CONF_DIR="$VMCHOOSER_DIR/config" - -mkdir -p "${VMCHOOSER_DIR}/data/loopimg" -mkdir -p "${VMCHOOSER_DIR}/fd-loop" -m 1777 - -# mount a clean tempfs (bug in UnionFS prevents loopmount to work) -grep -qE "unionfs |aufs " /proc/mounts && \ - mount -n -o size=1500k -t tmpfs vm-loopimg "${VMCHOOSER_DIR}/data/loopimg" - -# create an empty floppy image of 1.44 MByte size -dd "if=/dev/zero" "of=${VMCHOOSER_DIR}/data/loopimg/fd.img" count=2880 bs=512 2>/dev/null -chmod 0777 "${VMCHOOSER_DIR}/data/loopimg/fd.img" - -# use dos formatter copied into stage3 -mkdosfs "${VMCHOOSER_DIR}/data/loopimg/fd.img" -mount -n -t msdos -o loop,umask=000 "${VMCHOOSER_DIR}/data/loopimg/fd.img" "${VMCHOOSER_DIR}/fd-loop" - -#pvs integration -#if [ $vmchooser_pvs -ne 0 ]; then -# sed -i /usr/share/xsessions/default.desktop \ -# -e "s,vmchooser$,vmchooser --pvs," -#fi +DHCP_NAT_CONF="/opt/openslx/vmchooser/config/udhcpd-nat1.conf" # setup more scratch space for virtual machines, if configured mkdir -p /tmp/virt /var/log/samba /run/samba @@ -110,10 +90,20 @@ fi # write mac if [ -n "$SLX_PXE_MAC" ]; then - echo "hostmacaddr=${SLX_PXE_MAC}" >> "${VMCHOOSER_CONF_DIR}/virtualization.conf" + hostmacaddr="${SLX_PXE_MAC}" else ## Fallback: - echo "hostmacaddr=$(ip a | grep ether | grep -o -E -i '([0-9a-f]{2}:){5}[0-9a-f]{2}' | head -n 1)" >> "${VMCHOOSER_CONF_DIR}/virtualization.conf" + hostmacaddr="$(ip a | grep ether | grep -o -E -i '([0-9a-f]{2}:){5}[0-9a-f]{2}' | head -n 1)" fi +echo "hostmacaddr='$hostmacaddr'" >> "${VMCHOOSER_CONF_DIR}/virtualization.conf" +# TODO: We should only generate the suffix here (last 3 octets) as the first 3 are +# dependant on the virtualizer/emulator. Check if any run-virt.include still relies on +# $macguestpart/$macaddr. If so, fix it to use its specific first 3 bytes +# and append $macaddrssuffix +macaddrprefix='00:50:56' +macaddrsuffix="$(echo "$hostmacaddr" | awk -F ":" '{print "%VMID%:" $(NF-1) ":" $NF}' | tr '[a-z]' '[A-Z]')" +echo "macaddrprefix='$macaddrprefix'" >> "${VMCHOOSER_CONF_DIR}/virtualization.conf" +echo "macaddrsuffix='$macaddrsuffix'" >> "${VMCHOOSER_CONF_DIR}/virtualization.conf" + # read in ip address echo "hostip=${SLX_PXE_CLIENT_IP}" >> "${VMCHOOSER_CONF_DIR}/virtualization.conf" # hostname @@ -132,6 +122,11 @@ for floppy in $(dmesg|grep -i "Floppy drive"|sed "s,.*(s): ,,;s, is .*,,"); do j=$(expr $j + 1) done +# Serial ports +echo "serial_ports='$(dmesg | grep -Eo 'ttyS[0-9]+' | sed 's,^,/dev/,' | tr '\n' ' ')'" >> "${VMCHOOSER_CONF_DIR}/virtualization.conf" +# Parallel ports +modprobe parport_pc + ################################################################################ ### Setup VM networking ################################################################################ @@ -163,19 +158,27 @@ echo "1" >/proc/sys/net/ipv4/conf/br0/forwarding 2>/dev/null ### iptables -t nat -A POSTROUTING -o br0 -s 192.168.0.0/16 -j MASQUERADE for wait in 1 1 2 2 3 end; do - grep '^SLX_DNS' "/opt/openslx/config" > /dev/null && break - [ "$wait" == "end" ] && echo "No DNS config found, using google dns" && break - echo "Waiting for DNS config.." + [ -n "$SLX_DNS" ] && [ -n "$SLX_NET_SEARCH" ] && break + if [ "$wait" == "end" ]; then + echo "No DNS config found, using google dns" + break + fi + echo "Waiting for DNS & search-domain config.." sleep "$wait" + . /opt/openslx/config done # read the DNS configuration and configure the udhcpd -[ -z "${SLX_DNS}" ] && SLX_DNS="8.8.8.8" -sed -i "s,DNSSERVER,${SLX_DNS},;s,DOMAIN,${SLX_NET_DOMAIN}," \ - /opt/openslx/vmchooser/config/udhcpd-nat1.conf +[ -z "${SLX_DNS}" ] && SLX_DNS="8.8.8.8 8.8.4.4" +[ -z "${SLX_NET_DOMAIN}" ] && SLX_NET_DOMAIN="virtual.site" +[ -z "${SLX_NET_SEARCH}" ] && SLX_NET_SEARCH="virtual.site" +sed -i "s#%DNSSERVER%#${SLX_DNS}#;s#%DOMAIN%#${SLX_NET_DOMAIN}#;s#%SEARCH%#${SLX_NET_SEARCH}#" "${DHCP_NAT_CONF}" + +# Make sure the primary vm running (we most likely never run more than one at a time anyways) always gets the same ip +echo "static_lease $(echo "$macaddrprefix:$macaddrsuffix" | sed 's/%VMID%/01/') 192.168.101.20" >> "${DHCP_NAT_CONF}" mkdir -p /var/lib/udhcpd -udhcpd -S /opt/openslx/vmchooser/config/udhcpd-nat1.conf +udhcpd -S "${DHCP_NAT_CONF}" # creating and configuring vsw2 brctl addbr vsw2 diff --git a/core/modules/run-virt/data/opt/openslx/scripts/vmchooser-run_virt b/core/modules/run-virt/data/opt/openslx/scripts/vmchooser-run_virt index 1e591389..447f98bc 100755..120000 --- a/core/modules/run-virt/data/opt/openslx/scripts/vmchooser-run_virt +++ b/core/modules/run-virt/data/opt/openslx/scripts/vmchooser-run_virt @@ -1,116 +1 @@ -#!/bin/bash -# Full bash required -# ----------------------------------------------------------------------------- -# Copyright (c) 2007..2010 - RZ Uni FR -# Copyright (c) 2007..2013 - OpenSLX GmbH -# -# This program is free software distributed under the GPL version 2. -# See http://openslx.org/COPYING -# -# If you have any feedback please consult http://openslx.org/feedback and -# send your suggestions, praise, or complaints to feedback@openslx.org -# -# General information about OpenSLX can be found at http://openslx.org/ -# ----------------------------------------------------------------------------- -# run-virt.sh -# - This is the generic wrapper for the several virtualization solutions. -# The idea is to setup a set of variables used by at least two different -# tools and then include the specific plugin which configures the speci- -# fied virtualization tool. -################################################################################ - -RUNVIRTINCLUDEDIR=/opt/openslx/scripts/includes -xmlfile="$1" - -# Functions needed by vmchooser-run_virt (writelog(), cleanexit(), rv_clean_string()) -source ${RUNVIRTINCLUDEDIR}/vmchooser_runvirt_functions.inc && trap 'trap "" SIGINT SIGTERM; cleanexit' SIGINT SIGTERM -# Define default dirs / get configs -source ${RUNVIRTINCLUDEDIR}/set_runvirt_variables.inc -# Function to detect whether we can use the new way (vmx via http) or the old way (legacy): -source ${RUNVIRTINCLUDEDIR}/detect_legacy.inc # This yields LEGACY, IMGUUID, IMGVMX -# For scanning for certain usb classes -source "${RUNVIRTINCLUDEDIR}/usb_detector.inc" - -if [ "$LEGACY" ]; then - # check for important files used (filecheck()) - vestigial? - source ${RUNVIRTINCLUDEDIR}/check_runvirt_needed_files.inc - # Get XML file and dir, legacy (old way) - source ${RUNVIRTINCLUDEDIR}/get_xml_file_dir_legacy.inc # xmlfile=$1 - # xml file sanity checks, legacy - source ${RUNVIRTINCLUDEDIR}/check_runvirt_xml_sanity_legacy.inc - # print console logo - source ${RUNVIRTINCLUDEDIR}/print_runvirt_console_logo.inc - # Read needed variables from XML file - source ${RUNVIRTINCLUDEDIR}/get_xml_file_variables_legacy.inc - # Declaration of hardware relatedt variables - source ${RUNVIRTINCLUDEDIR}/set_runvirt_hardware_variables_legacy.inc - # Sound setup the rest of the environment and run the configured vm - source ${RUNVIRTINCLUDEDIR}/setup_sound.inc - # Start printer daemon - source ${RUNVIRTINCLUDEDIR}/setup_printer_lpd.inc - # Setup virtual floppy b: for windows guests with config.xml, openslx.exe etc. - source ${RUNVIRTINCLUDEDIR}/setup_virtual_floppy.inc - # Try to use dnbd3 to access the image, nfs/cifs fallback - source ${RUNVIRTINCLUDEDIR}/setup_image_access.inc - # Get all virtual machine specific stuff from the respective include file - source ${RUNVIRTINCLUDEDIR}/setup_vm_hypervisor.inc - # start a windowmanager for easier handling - source ${RUNVIRTINCLUDEDIR}/start_windowmanager.inc - # Start poolvideoswitch if DO_PROFILE has been set (WARNING: fixed IP!) - source ${RUNVIRTINCLUDEDIR}/start_pvs.inc - # Check if tcpsvd is running. Do not check immediately after spawning, - # as this could result in success even if it's not really working. - source ${RUNVIRTINCLUDEDIR}/check_lpd.inc -else - # check for important files used (filecheck()) - vestigial? - # This include does not currently work. TODO. - # source ${RUNVIRTINCLUDEDIR}/check_runvirt_needed_files.inc && filecheck - - # Read needed variables from XML file - source ${RUNVIRTINCLUDEDIR}/get_xml_file_variables.inc - - # Sound setup the rest of the environment and run the configured vm - source ${RUNVIRTINCLUDEDIR}/setup_sound.inc - - # Declaration of hardware relatedt variables - source ${RUNVIRTINCLUDEDIR}/set_runvirt_hardware_variables.inc - - # Start printer daemon - source ${RUNVIRTINCLUDEDIR}/setup_printer_lpd.inc - - # Setup virtual floppy b: for windows guests with config.xml, openslx.exe etc. - source ${RUNVIRTINCLUDEDIR}/setup_virtual_floppy.inc - - # Get all virtual machine specific stuff from the respective include file - source ${RUNVIRTINCLUDEDIR}/setup_image_access.inc - - # start a windowmanager for easier handling - source ${RUNVIRTINCLUDEDIR}/setup_vm_hypervisor.inc - - # Try to use dnbd3 to access the image, nfs/cifs fallback - source ${RUNVIRTINCLUDEDIR}/start_windowmanager.inc - - # Start poolvideoswitch if DO_PROFILE has been set (WARNING: fixed IP!) - source ${RUNVIRTINCLUDEDIR}/start_pvs.inc - - # Check if tcpsvd is running. Do not check immediately after spawning, - # as this could result in success even if it's not really working. - source ${RUNVIRTINCLUDEDIR}/check_lpd.inc - -fi - -# This will start the VM -writelog "VM command: eval ${VIRTCMD} ${VIRTCMDOPTS}" - -# Transported from vmware-runvirt include -sync - -eval ${VIRTCMD} ${VIRTCMDOPTS} -writelog "Bye." - -# Postrun for commands after virtualization finishes -if [ -n "${POSTRUN}" ]; then - eval ${POSTRUN} >/dev/null 2>&1 -fi - -cleanexit 0 +/opt/openslx/vmchooser/vmchooser-run_virt
\ No newline at end of file diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/config/udhcpd-nat1.conf b/core/modules/run-virt/data/opt/openslx/vmchooser/config/udhcpd-nat1.conf index bca1397c..95d8ebc0 100644 --- a/core/modules/run-virt/data/opt/openslx/vmchooser/config/udhcpd-nat1.conf +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/config/udhcpd-nat1.conf @@ -31,11 +31,12 @@ lease_file /var/lib/udhcpd/udhcpd-nat1.leases # location of the pid file pidfile /var/run/udhcpd-nat1.pid -option dns DNSSERVER +option dns %DNSSERVER% option subnet 255.255.255.0 option router 192.168.101.1 option wins 192.168.101.10 -option domain DOMAIN virtual.site +option domain %DOMAIN% +option search %SEARCH% option lprsrv 192.168.101.1 #option ntpsrv NTPSERVER diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/LIESMICH b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/LIESMICH new file mode 100644 index 00000000..326af2a4 --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/LIESMICH @@ -0,0 +1,24 @@ +Dieses Paket besteht aus folgenden Skripten: + +vm_installer: Dieses Skript einmalig in einer lokalen, permanenten (also + nicht innerhalb des Poolsystems) Virtuellen Maschine ausführen. + Es schreibt eine systemd-Servicedatei und verlinkt diese, um + beim Systemstart per vm_runtime die Skripte zu Auflösungsan- + passung und Einhängung eventuell übergebener Netzlaufwerke + nach Hochladung der VM in das Poolsystem automatisch zu starten. + Das Skript kann von der gemounteten (Pseudo-)Diskette /dev/fd1 + aus oder einzelstehend gestartet werden. + Unterstützung für init-basierte Systeme folgt noch. +vm_runtime: Dieses Skript wird beim booten der VM aufgerufen und patcht + Xsetup, um beim grafischen Start der VM die Skripte shares + und resolution aufzurufen. +mnt_shares: Liest die auf Pseudodiskette gegebene shares.dat aus und hängt + ggf. darin angegebene Netzlaufwerke ein. +resolution: Stellt die Auflösung der VM anhand der übergebenen Auflösung + des Grundsystems ein. + +resolution_standalone: Einzelstehende Version des resolution-Skriptes, die + keine Vorarbeiten seitens systemd bzw. vm_runtime benötigt. + Für geeignete Einbindung innerhalb der VM muss daher selbst + gesorgt werden. + diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/README b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/README new file mode 100644 index 00000000..12777c67 --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/README @@ -0,0 +1,21 @@ +This package consists of the following scripts: + +vm_installer: This script is to be started once in a local, permanent (so + not within the pool system environment) virtual machine. It + writes a systemd service file and links it, so the scripts + for resolution setting and mounting network shares will be + started at boot time at boot time via vm_runtime. + It may be run from the mounted (pseudo) disk drive /dev/fd1 + or from elsewhere. + init will be supported at later time. +vm_runtime: Will be started at boot time and patches Xsetup, so the + scripts mnt_shares and resolution will be started at graphical + login. +mnt_shares: Reads the config file shares.dat from (pseudo) disk drive and + mounts therein given network shares. +resolution: Sets resolution within the virtual machine according to base + system resolution given via config file openslx.ini. + +resolution_standalone: Standalone version of resolution sctipt. Needs no + preliminary work done by systemd or vm_runtime. + One has to embed/start it via adequate means by hand. diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/00_vars.inc b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/00_vars.inc new file mode 100644 index 00000000..bb7ae3a1 --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/00_vars.inc @@ -0,0 +1,29 @@ +### Variablen ##################################### +LOCALUSER=student +USERHOME=/home/"$LOCALUSER" +USERHOMEDIR=/home/"$LOCALUSER"/PERSISTENT +DISKMOUNTDIR=/mnt/diskmount +SLXCONFIGFILE="$DISKMOUNTDIR"/openslx.ini +CONFIGFILE="$DISKMOUNTDIR"/shares.dat +KEYTEMP=$(mktemp -t XXXXXXXXXX.dat) +RAWKEYTEMP=$(mktemp -t XXXXXXXXXX.dat) +BYTES=256 +declare -a ROHSHARES + +if [ ! $(mount 2>/dev/null | grep -q "$DISKMOUNTDIR") ]; then + [ ! -d "$DISKMOUNTDIR" ] && mkdir -p "$DISKMOUNTDIR" + mount /dev/fd1 "$DISKMOUNTDIR" 2>/dev/null 1>&2 # Zu erl.: Fehlerfangen +fi + +NATADDR=$(head -n 1 "$CONFIGFILE" | cut -f 1 -d$'\t') +PORT=$(head -n 1 "$CONFIGFILE" | cut -f 2 -d$'\t') +SCHLUESSEL=$(head -n 1 "$CONFIGFILE" | cut -f 4 -d$'\t') +GLOBALDOMAINUSER=$(head -n 1 "$CONFIGFILE" | cut -f 5 -d$'\t') +GLOBALUSER=$(echo "$GLOBALDOMAINUSER" | cut -d '\' -f 2) + +REMAPMODE=$(grep 'remapMode=' "$SLXCONFIGFILE" | cut -d '=' -f 2) +CREATEMISSING=$(grep 'createMissingRemap=' "$SLXCONFIGFILE" | cut -d '=' -f 2) + +MOUNTOPTSCIFS="-v -t cifs -o uid=$(id --user "$LOCALUSER"),gid=$(id --group "$LOCALUSER"),forceuid,forcegid,file_mode=0700,dir_mode=0700,nobrl,noacl" +MOUNTOPTSNFS="-v -t nfs4 -o rw,nosuid,nodev,nolock,intr,hard,sloppy" +### Variablen Ende ################################ diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/10_functions.inc b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/10_functions.inc new file mode 100644 index 00000000..97767c51 --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/10_functions.inc @@ -0,0 +1,51 @@ +### Funktionen #################################### + +function xor() +{ local RES=($(echo "$1" | sed "s/../0x& /g")) + shift 1 + while [[ "$1" ]]; do + local ONE=($(echo "$1" | sed "s/../0x& /g")) + local COUNT1=${#RES[@]} + if [ $COUNT1 -lt ${#ONE[@]} ]; then + COUNT1=${#ONE[@]} + fi + for (( i = 0; i < $COUNT1; i++ )); do + RES[$i]=$((${ONE[$i]:-0} ^ ${RES[$i]:-0})) + done + shift 1 + done + printf "%02x" "${RES[@]}" +} + +function already_mounted() +{ + # Ausgabe: gemountet = true = 0, nicht gemountet = false = 1 + local AUSGANG + mount | grep -q " ${1} " && AUSGANG=0 || AUSGANG=1 + return $AUSGANG +} + +function mounter() +{ + # Ausgabe: konnte mounten: 0, konnte nicht mounten: 1, schon gemountet 2 + already_mounted "$3" + ERR=$? + if [ "$ERR" -eq 0 ]; then + logger "openslx sharemapper: $3 already mounted." + AUSGANG=2 + else + mount $1 $2 $3 2>/dev/null 1>&2 + ERR=$? + if [ "$ERR" -gt 0 ]; then + logger "openslx sharemapper: could not mount ${2} to ${3}." + AUSGANG=1 + else + logger "openslx sharemapper: ${2} mounted to ${3}." + AUSGANG=0 + fi + fi + return $AUSGANG +} + +### Funktionen Ende ############################### + diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/10_preliminaries.inc b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/10_preliminaries.inc new file mode 100644 index 00000000..bcbd6ec1 --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/10_preliminaries.inc @@ -0,0 +1,20 @@ +preliminaries_native() +{ + # USERHOMEDIR=~/PERSISTENT, not ~! ################ + [ -h "$USERHOMEDIR" ] && unlink "$USERHOMEDIR" + [ ! -d "$USERHOMEDIR" ] && mkdir -p "$USERHOMEDIR" +} + +preliminaries_vmware() +{ + # USERHOMEDIR=~/PERSISTENT, not ~! ################ + if [ -d "$USERHOMEDIR" ]; then + logger "openslx sharemapper: vmware mode: USERHOMEDIR ${USERHOMEDIR} is a directory, moving to ${USERHOMEDIR}_mov." + mv "$USERHOMEDIR" "${USERHOMEDIR}"_mov + fi + + [ -h "$USERHOMEDIR" ] && unlink "$USERHOMEDIR" + logger "openslx sharemapper: vmware mode: linking $USERHOMEDIR to /mnt/hgfs/home." + ln -s /mnt/hgfs/home "$USERHOMEDIR" || \ + logger "openslx sharemapper: vmware mode: Could not link to vmware hgfs mount dir." +} diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/15_set_trapping.inc b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/15_set_trapping.inc new file mode 100644 index 00000000..c49b99ff --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/15_set_trapping.inc @@ -0,0 +1,14 @@ +function ausgang() +{ rm -f "$KEYTEMP" + rm -f "$RAWKEYTEMP" + # umount "$DISKMOUNTDIR" # as mounted by systemd now. +} + +function set_trapping() +{ + ### Trap ########################################## + trap ausgang EXIT SIGHUP SIGINT SIGTERM + ################################################### +} + +set_trapping diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/20_get_creds.inc b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/20_get_creds.inc new file mode 100644 index 00000000..c6f6ffcb --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/20_get_creds.inc @@ -0,0 +1,27 @@ +function get_creds() +{ + # udp-Socket erzeugen Kanal erzeugen:: + exec 13<>/dev/udp/"$NATADDR"/"$PORT" + + # temp. Datei - wer hat Angst vor Nullbytes? Ich! Ich! Ich! + head -n 1 "$CONFIGFILE" |cut -f3 -d$'\t' | while read -n 2 CODE; do [ -n "$CODE" ] && printf "\x$CODE"; done >"$KEYTEMP" + # Auf Socket fuer Grundsystem schreiben: + cat "$KEYTEMP" >&13 + + # Serverantwort pwdaemon in temp. Datei schreiben: + timeout 2s dd bs=$BYTES count=1 of="$RAWKEYTEMP" <&13 2>/dev/null + + # Kanal zu Socket schließen: + exec 13<&- + exec 13>&- + + # Wieviele Zeichen (Bytes 1 und 2 der Paketnutzlast)? + ANZAHL=$((16#$(dd if=$RAWKEYTEMP bs=1 count=2 2>/dev/null|hexdump -e '1/1 "%02x"'))) + + # Und raus-xor-en: + PW=$( xor $(dd if="$RAWKEYTEMP" bs=1 skip=2 2>/dev/null|hexdump -e '1/1 "%02x"') \ + $(head -n 1 "$CONFIGFILE" | cut -f 4 -d$'\t') | sed 's/../\\x&/g') + + PW=$(echo -e "$PW") # \x...-Ausdrücke zu ASCII + PW="${PW:0:${ANZAHL}}" # ...und Rest nach ANZAHL abhacken. +} diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/30_get_shares.inc b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/30_get_shares.inc new file mode 100644 index 00000000..70b17236 --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/30_get_shares.inc @@ -0,0 +1,22 @@ +function get_shares() +{ + i=0 + while read -r LINIE; do + ROHSHARES[i]="$LINIE" + ((i++)) + done < "$CONFIGFILE" + +# echo "${#ROHSHARES[@]} eingelesen:" +# for (( i = 0; i < ${#ROHSHARES[@]}; i++ )); do +# echo -n "$i " +# echo "${ROHSHARES[i]}" +# done + + let SHAREZAHL=${#ROHSHARES[@]}-1 + if [ "$SHAREZAHL" -lt 1 ]; then + logger "openslx sharemapper: No shares to map/mount found in config file $CONFIGFILE." + exit 0 + else + logger "openslx sharemapper: $SHAREZAHL share(s) found." + fi +} diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/30_mount_shares.inc b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/30_mount_shares.inc new file mode 100644 index 00000000..b1320d01 --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/30_mount_shares.inc @@ -0,0 +1,66 @@ +function mount_shares() +{ + for (( CONFIGROW = 1; CONFIGROW < ${#ROHSHARES[@]}; CONFIGROW++ )); do + SHAREPATH=$(echo "${ROHSHARES[CONFIGROW]}" | cut -f 1 -d$'\t') # cifs- oder nfs-Share + SHARELETTER=$(echo "${ROHSHARES[CONFIGROW]}" | cut -f 2 -d$'\t' | \ + sed 's/://g') # Laufwerksbuchstabe ohne : + SHARENAME=$(echo "${ROHSHARES[CONFIGROW]}" | cut -f 3 -d$'\t' | sed 's/ /_/g') # Leerzeichen raus. + SHAREUSER=$(echo "${ROHSHARES[CONFIGROW]}" | cut -f 4 -d$'\t') # Username, bei Userhome nicht vorhanden + SHAREPASS=$(echo "${ROHSHARES[CONFIGROW]}" | cut -f 5 -d$'\t') # User-PW, bei Userhome nicht vorhanden + + # Sharetyp bestimmen: + if [ "${SHAREPATH:0:2}" == '\\' ]; then + USER=$(echo "$SHAREUSER"|cut -d '\' -f 2) + MOUNTOPTS="$MOUNTOPTSCIFS" + else + MOUNTOPTS="$MOUNTOPTSNFS" + fi + + # User-Homeverzeichnis? + if [ "${SHARENAME:0:5}" == "Home-" ]; then + logger "openslx sharemapper: home share \"$SHARENAME\" found (for PERSISTENT)." + export USER="${GLOBALUSER}" + export PASSWD="${PW}" + mounter "${MOUNTOPTS}" ${SHAREPATH} ${USERHOMEDIR} 2>/dev/null 1>&2 + unset USER + unset PASSWD + else + if [ -z "${SHARELETTER}" ]; then + logger "openslx sharemapper: $SHARELETTER not found. Do not know where to mount." + continue + else + USERSHAREDIR="${USERHOME}"/SHARE_"${CONFIGROW}" # ROHSHARES: Zeilennummer; Shares >=1 + if [ ! -d "$USERSHAREDIR" ]; then + mkdir -p "$USERSHAREDIR" 2>/dev/null + chown "$LOCALUSER":$(id --group "$LOCALUSER") "$USERSHAREDIR" + chmod 700 "$USERSHAREDIR" + logger "openslx sharemapper: share mount dir $USERSHAREDIR created." + fi + fi + # Wenn kein Homeverzeichnis, dann share zuerst mit den Credentials aus der share-Konfig + # versuchen zu mounten; wenn nicht, dann mit den Hauptcredentials nachversuchen. + logger "openslx sharemapper: non-home share \"$SHARENAME\" (${CONFIGROW}) found." + export USER="${SHAREUSER}" + export PASSWD="${SHAREPASS}" + mounter "${MOUNTOPTS}" ${SHAREPATH} ${USERSHAREDIR} 2>/dev/null 1>&2 + ERR=$? + if [ "$ERR" -eq 1 ]; then + export USER="${GLOBALUSER}" + export PASSWD="${PW}" + logger "openslx sharemapper: Could not mount ${USERSHAREDIR}, now trying using user credentials." + mounter "${MOUNTOPTS}" ${SHAREPATH} ${USERSHAREDIR} 2>/dev/null 1>&2 \ + ERR=$? # ERR merken wg. Links aus USERSHAREDIR + if [ "$ERR" -eq 1 ]; then + logger "openslx sharemapper: Could not mount ${USERSHAREDIR} even using user credentials; giving up." + fi + unset USER + unset PASSWD + fi + if [ "$ERR" -eq 0 ]; then + logger "openslx sharemapper: Linking ${SHARELETTER} and $SHARENAME, if possible." + [ -n "${SHARELETTER}" ] && ln -s "$USERSHAREDIR" "${USERHOME}"/"$SHARELETTER" + [ -n "${SHARELETTER}" ] && ln -s "$USERSHAREDIR" "${USERHOME}"/"$SHARENAME" + fi + fi + done +} diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/40_check_fallback.inc b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/40_check_fallback.inc new file mode 100644 index 00000000..14a13f77 --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/40_check_fallback.inc @@ -0,0 +1,8 @@ +check_fallback() { + if [ $(mount | grep -c " $USERHOMEDIR type cifs \| $USERHOMEDIR type nfs") -lt 1 ]; then + logger "openslx sharemapper: fallback mode: home share check failed, doing fallback to vmware mode." + preliminaries_vmware + else + logger "openslx sharemapper: fallback mode: home share check passed, no fallback necessary." + fi +} diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/50_postliminaries.inc b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/50_postliminaries.inc new file mode 100644 index 00000000..f6f1cf7d --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/50_postliminaries.inc @@ -0,0 +1,21 @@ +postliminaries_native() +{ + # USERHOMEDIR=~/PERSISTENT, not ~! ################ + rmdir "$USERHOMEDIR" && logger "openslx unmounter: home directory entry (PERSISTENT) deleted." \ + || logger "openslx unmounter: could not delete home directory (PERSISTENT) entry - not empty / unmounted!" +} + +postliminaries_vmware() +{ + unlink "$USERHOMEDIR" + ERR=$? + if [ "$ERR" -ne 0 ]; then + logger "openslx unmounter: vmware mode: could not unlink ${USERHOMEDIR}!" + logger "openslx unmounter: vmware mode: this is a severe problem; I do not know what to do - exiting!" + exit 1 + else + logger "openslx unmounter: vmware mode: ${USERHOMEDIR} unlinked." + # checking whether there was a directory moved out earlier: + [ -d "${USERHOMEDIR}"_mov ] && mv "${USERHOMEDIR}"_mov "${USERHOMEDIR}" + fi +} diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/50_umounter.inc b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/50_umounter.inc new file mode 100644 index 00000000..e161b961 --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/includes/50_umounter.inc @@ -0,0 +1,12 @@ +umount_home() { + umount /home/"${LOCALUSER}"/PERSISTENT && logger "openslx unmounter: umounted home (PERSISTENT)." \ + || logger "openslx unmounter: could not home (PERSISTENT)!" +} + +umount_shares() { + index=0 + for SHARE in $( mount | grep SHARE | tr -s ' ' | cut -f 3 -d " " ); do + umount "${SHARE}" && logger "openslx unmounter: umounted ${SHARE}." \ + || logger "openslx unmounter: could not umount ${SHARE}!" + done +} diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/mnt_shares b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/mnt_shares new file mode 100755 index 00000000..0c94f0aa --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/mnt_shares @@ -0,0 +1,62 @@ +#!/bin/bash + +# set -x + +logger "openslx sharemapper: Started." + +OWNDIR=$(dirname $0) +for i in "$(dirname $0)"/includes/*.inc; do + source "$i" +done + +# Wait for network connection to base system +#!/bin/bash + +x=1 +while ! ping -w 1 -c 1 "${NATADDR}" 2>/dev/null 1>&2; do + [ "$x" -gt 20 ] && { logger "openslx sharemapper: could not reach base system. Giving up."; exit 1; } + let x=x+1 + sleep 2 +done + +logger "openslx sharemapper: base system reaching; commencing." + +# REMAPMODE (remapMode): 0: None, 1 Native, 2 Native Fallback, 3 vmware +# CREATEMISSING (createMissingRemap): 0: Nichts tun, 1 xdg-Verzeichnisse + +case "$REMAPMODE" in + 0) logger "openslx sharemapper: remapMode 0 (do nothing) detected." + exit 0 + ;; + 1) logger "openslx sharemapper: remapMode 1 (native mode) detected." + preliminaries_native + get_creds # fills global var PW with (currently) decrypted password + get_shares # fills array ROHSHARES; row 0 global infos from (shares-)CONFIGFILE, + # following rows: column 1 share path, col 2 drive letter, col 3 share name, + # column 4 username, col 5 password. + mount_shares # mounts shares given in array ROHSHARES. + exit 0 + ;; + 2) logger "openslx sharemapper: remapMode 2 (fallback mode) detected." + preliminaries_native + get_creds # fills global var PW with (currently) decrypted password + get_shares # fills array ROHSHARES; row 0 global infos from (shares-)CONFIGFILE, + # following rows: column 1 share path, col 2 drive letter, col 3 share name, + # column 4 username, col 5 password. + mount_shares # mounts shares given in array ROHSHARES. + check_fallback # checks for a mounted home and fallbacks to vmware share, if none found. + exit 0 + ;; + 3) logger "openslx sharemapper: remapMode 3 (vmware mode) detected." + preliminaries_vmware + mount_shares # mounts shares given in array ROHSHARES - runvirt checks whether there's + # a home share given or not.. + exit 0 + ;; + *) logger "openslx sharemapper: unknown remapMode in $SLXCONFIGFILE; doing nothing end exiting with error." + exit 1. + ;; +esac + +exit 0 + diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/resolution b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/resolution new file mode 100755 index 00000000..24a0594f --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/resolution @@ -0,0 +1,102 @@ +#!/bin/bash + +# Dieses Skript liest die Auflösungsdate HOSTRES.TXT aus der Diskette 2 (/dev/fd1). In dieser +# Datei wird die native (maximale) Auflösung des Grundsystems bereitgestellt. fd1 wird vom +# openslx-Grundsystem geliefert. Es loggt nach SYSLOG (syslog oder messages). +# +# Zu erledigen: Jede Menge, zB anständiges Trapping, Fehler abfangen usw... +# set -x + +MOUNTDIR=/mnt/diskmount +RESFILE="${MOUNTDIR}"/openslx.ini +DISPLAY=:0.0 + +check_resolution() { + # Now, let's wait even more seconds to see if another process re-changes res.: + ACTRES=$(xrandr|grep '*'|tr -s " "|cut -f 2 -d " ") + if [ "$ACTRES" != "$RESOLUTION" ]; then + logger "openslx resolution utility: resolution changed; re-changing." + xrandr --output ${AUSGABE} --mode ${RESOLUTION} + else + logger "openslx resolution utility: resolution unchanged." + fi +} + +logger "openslx resolution utility started." + +# Da nun fremdgemountet wird, hier sicherheitshalber zur Kontrolle wg. Gross-/Kleinschreibung: +RESFILE=$(find "${MOUNTDIR}" -iwholename "${RESFILE}") + +if [ -f "$RESFILE" ]; then + RESOLUTION=$(grep 'resolution=' "$RESFILE" | cut -d '=' -f 2) + if [ -z $RESOLUTION ]; then + logger "openslx resolution utility: resolution file $RESFILE seems to be empty!" + exit 1 + fi + logger "openslx resolution utility: resolution file $RESFILE yields ${RESOLUTION}." +else + logger "openslx resolution utility: resolution file $RESFILE not found!" + exit 1 +fi + +# Das ist mitunter ein Problem, da die richtige zu finden - Distroabhängig! +# Hier ein Beispiel für openSuse 13.2, sollte auch für Debian, Ubuntu, Redhat usw. laufen:. +XAUTHDATEI=$(ps aux | grep -o "X.* \-auth *[^ ]*" | awk '{print $NF}') +if [ -n "$XAUTHDATEI" ]; then + logger "openslx resolution utility: XAUTHFILE found." + XAUTHORITY="${XAUTHDATEI}" +else + logger "openslx resolution utility: XAUTHFILE not found. Exiting." + exit 1 +fi + +# Zu verbessern: Der Name des Verbundenen ist nicht immer bekannt. Daher nehmen wir das +# erste 'connected' in der Ausgabe xrandrs: +AUSGABE=$(xrandr -q|grep -m 1 " connected "|awk '{print $1}') +if [ -z "$AUSGABE" ]; then + logger "openslx resolution utility: Could not detect output device. Exiting." + exit 1 +fi + +# Pruefen, ob xrand eine passende modeline ausgibt: +if [ "$(xrandr | grep -c ${RESOLUTION})" -eq 0 ]; then + logger "openslx resolution utility: xrandr yields no fitting modeline; adding one." + MODELINE=$(cvt ${RESOLUTION//x/ } | grep -v "^#" | sed "s/Modeline //g" | sed 's/"//g') + xrandr --newmode $(echo ${MODELINE}) + xrandr --addmode ${AUSGABE} $(echo ${MODELINE} | cut -d " " -f 1) + # Dann einzusteuernde Auflösung natürlich auf die neue ändern: + RESOLUTION=$(echo ${MODELINE} | cut -d " " -f 1) + logger "openslx resolution utility: (xrandr) modeline ${MODELINE} added (${RESOLUTION})." +fi + +# Auflösung per xrandr setzen: +xrandr --output ${AUSGABE} --mode ${RESOLUTION} +ERR=$? +if [ $ERR -ne 0 ]; then + logger "openslx resolution utility: xrandr error code ${ERR}." +else + logger "openslx resolution utility: xrandr ok. Mode $RESOLUTION set." +fi + +# Das hier ist ein wenig problematisch, da nach X-Start die gewünschte Desktopumgebung +# diese gern zuvor gespeicherte Auflösungen wieder einspielt. Daher warten wir einfach, +# und prüfen in gewissen Zeitabständen, ob sich die Auflösung geändert hat, und spielen +# im Änderungsfalle die Grundsystemauflösung wieder ein. Schön ist das nicht. + +# Now, let's wait some seconds to see if another process re-changes res.: +sleep 5 +check_resolution + +# Now, let's wait some more seconds to see if another process re-changes res.: +sleep 15 +check_resolution + +# Now, let's wait even more seconds to see if another process re-changes res.: +sleep 20 +check_resolution + +# Now, let's wait even more seconds to see if another process re-changes res.: +sleep 40 +check_resolution + +exit 0 diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/resolution_standalone b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/resolution_standalone new file mode 100755 index 00000000..0dbbcf98 --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/resolution_standalone @@ -0,0 +1,138 @@ +#!/bin/bash + +# Dieses Skript liest die Auflösungsdate HOSTRES.TXT aus der Diskette 2 (/dev/fd1). In dieser +# Datei wird die native (maximale) Auflösung des Grundsystems bereitgestellt. fd1 wird vom +# openslx-Grundsystem geliefert. Es loggt nach SYSLOG (syslog oder messages). +# +# Zu erledigen: Jede Menge, zB anständiges Trapping, Fehler abfangen usw... +# set -x + +MOUNTDIR=/tmp/diskmount +RESFILE="${MOUNTDIR}"/openslx.ini +DISPLAY=:0.0 + +function ausgang() { + umount "${MOUNTDIR}" + rmdir "${MOUNTDIR}" +} + +check_resolution() { + # Now, let's wait even more seconds to see if another process re-changes res.: + ACTRES=$(xrandr|grep '*'|tr -s " "|cut -f 2 -d " ") + if [ "$ACTRES" != "$RESOLUTION" ]; then + logger "openslx resolution utility: resolution changed; re-changing." + xrandr --output ${AUSGABE} --mode ${RESOLUTION} + else + logger "openslx resolution utility: resolution unchanged." + fi +} + +trap ausgang EXIT SIGHUP SIGINT SIGTERM + +logger "openslx resolution utility started." + +# Mountpunkt erzeugen: +if [ ! -d "$MOUNTDIR" ]; then + mkdir "$MOUNTDIR" + logger "openslx resolution utility: mkdir'ed diskmount dir $MOUNTDIR." +fi + +# Virtuelles Floppylaufwerk mounten +mount /dev/fd1 "$MOUNTDIR" 2>/dev/null +ERR=$? +if [ $ERR -ne 0 ]; then + logger "openslx resolution utility: error code $ERR trying to mount /dev/fd1 to ${MOUNTDIR}. Remounting..." + mount -o remount /dev/fd1 "$MOUNTDIR" 2>/dev/null + ERR=$? + if [ $ERR -ne 0 ]; then + logger "openslx resolution utility: error code $ERR trying to remount /dev/fd1 to ${MOUNTDIR}. Giving up." + exit 1 + else + logger "openslx resolution utility: remount of /dev/fd1 mounted to diskmount dir ${MOUNTDIR} succeeded." + fi +else + logger "openslx resolution utility: /dev/fd1 mounted to diskmount dir ${MOUNTDIR}." +fi + +# Das Mounten mit shortname=WIN95 erwies sich als nicht zuverlässig. Daher hier Kontrolle: +RESFILE=$(find "${MOUNTDIR}" -iwholename "${RESFILE}") + +if [ -f "$RESFILE" ]; then + RESOLUTION=$(grep 'resolution=' "$RESFILE" | cut -d '=' -f 2) + if [ -z $RESOLUTION ]; then + logger "openslx resolution utility: resolution file $RESFILE seems to be empty!" + exit 1 + fi + logger "openslx resolution utility: resolution file $RESFILE yields ${RESOLUTION}." +else + logger "openslx resolution utility: resolution file $RESFILE not found!" + exit 1 +fi + +umount /dev/fd1 +ERR=$? +if [ "$ERR" -ne 0 ]; then + logger "openslx resolution utility: error code $ERR trying to to unmount /dev/fd1. Please unmount by hand." +fi + +# Das ist mitunter ein Problem, da die richtige zu finden - Distroabhängig! +# Hier ein Beispiel für openSuse 13.2, sollte auch für Debian, Ubuntu, Redhat usw. laufen:. +XAUTHDATEI=$(ps aux | grep -o "X.* \-auth *[^ ]*" | awk '{print $NF}') +if [ -n "$XAUTHDATEI" ]; then + logger "openslx resolution utility: XAUTHFILE found." + XAUTHORITY="${XAUTHDATEI}" +else + logger "openslx resolution utility: XAUTHFILE not found. Exiting." + exit 1 +fi + +# Zu verbessern: Der Name des Verbundenen ist nicht immer bekannt. Daher nehmen wir das +# erste 'connected' in der Ausgabe xrandrs: +AUSGABE=$(xrandr -q|grep -m 1 " connected "|awk '{print $1}') +if [ -z "$AUSGABE" ]; then + logger "openslx resolution utility: Could not detect output device." + exit 1 +fi + +# Pruefen, ob xrand eine passende modeline ausgibt: +if [ "$(xrandr | grep -c ${RESOLUTION})" -eq 0 ]; then + logger "openslx resolution utility: xrandr yields no fitting modeline; adding one." + MODELINE=$(cvt ${RESOLUTION//x/ } | grep -v "^#" | sed "s/Modeline //g" | sed 's/"//g') + xrandr --newmode $(echo ${MODELINE}) + xrandr --addmode ${AUSGABE} $(echo ${MODELINE} | cut -d " " -f 1) + # Dann einzusteuernde Auflösung natürlich auf die neue ändern: + RESOLUTION=$(echo ${MODELINE} | cut -d " " -f 1) + logger "openslx resolution utility: (xrandr) modeline ${MODELINE} added (${RESOLUTION})." +fi + +# Auflösung per xrandr setzen: +xrandr --output ${AUSGABE} --mode ${RESOLUTION} +ERR=$? +if [ $ERR -ne 0 ]; then + logger "openslx resolution utility: xrandr error code ${ERR}." +else + logger "openslx resolution utility: xrandr ok. Mode $RESOLUTION set." +fi + +# Das hier ist ein wenig problematisch, da nach X-Start die gewünschte Desktopumgebung +# diese gern zuvor gespeicherte Auflösungen wieder einspielt. Daher warten wir einfach, +# und prüfen in gewissen Zeitabständen, ob sich die Auflösung geändert hat, und spielen +# im Änderungsfalle die Grundsystemauflösung wieder ein. Schön ist das nicht. + +# Now, let's wait some seconds to see if another process re-changes res.: +sleep 5 +check_resolution + +# Now, let's wait some more seconds to see if another process re-changes res.: +sleep 15 +check_resolution + +# Now, let's wait even more seconds to see if another process re-changes res.: +sleep 20 +check_resolution + +# Now, let's wait even more seconds to see if another process re-changes res.: +sleep 40 +check_resolution + +exit 0 diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/umnt_shares b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/umnt_shares new file mode 100755 index 00000000..b028cf76 --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/umnt_shares @@ -0,0 +1,40 @@ +#!/bin/bash + +# set -x + +logger "openslx sharemapper: umounter started." + +OWNDIR=$(dirname $0) +for i in "$(dirname $0)"/includes/*.inc; do + source "$i" +done + +# REMAPMODE (remapMode): 0: None, 1 Native, 2 Native Fallback, 3 vmware +# CREATEMISSING (createMissingRemap): 0: Nichts tun, 1 xdg-Verzeichnisse + +case "$REMAPMODE" in + 0) logger "openslx sharemapper: umounter: remapMode 0 (do nothing) detected." + exit 0 + ;; + 1) logger "openslx sharemapper: umounter: remapMode 1 (native mode) detected." + umount_shares + umount_home + postliminaries_native + ;; + 2) logger "openslx sharemapper: umounter: remapMode 2 (fallback mode) detected." + umount_shares + umount_home + postliminaries_native + exit 0 + ;; + 3) logger "openslx sharemapper: umounter: remapMode 3 (vmware mode) detected." + postliminaries_vmware + exit 0 + ;; + *) logger "openslx sharemapper: umounter: unknown remapMode in $SLXCONFIGFILE; doing nothing end exiting with error." + exit 1. + ;; +esac + +exit 0 + diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/vm_installer b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/vm_installer new file mode 100755 index 00000000..978cee20 --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/vm_installer @@ -0,0 +1,87 @@ +#!/bin/bash + +PS=$(which ps) +GREP=$(which grep) +LOGGER=$(which logger) +MKDIR=$(which mkdir) +MOUNT=$(which mount) +MOUNTDIR=/mnt/diskmount/ +SERVICEFILE=openslx-scriptinstall.service + +# systemd erkennen +case $(${PS} --pid 1 -o comm h) in + systemd) echo "openslx praeinstaller: systemd detected." + if [ -d /etc/systemd/system ]; then + SERVICEDIR=/etc/systemd/system/ + else + SERVICEDIR=/usr/lib/systemd/system/ + fi + echo "openslx praeinstaller: installing systemd service file to ${SERVICEDIR}/${SERVICEFILE}." + + cat <<-HEREDOC > "$SERVICEDIR"/"$SERVICEFILE" + [Unit] + Description=openSLX script installer + Before=display-manager.service graphical.target + + [Service] + Type=oneshot + ExecStartPre=${MKDIR} -p /mnt/diskmount + ExecStartPre=-${MOUNT} /dev/fd1 /mnt/diskmount + ExecStart=${LOGGER} "openslx service file: started." + ExecStart=${MOUNTDIR}/linux/vm_runtime + ExecStart=${LOGGER} "openslx service file: done." + RemainAfterExit=no + HEREDOC + + [ ! -d /etc/systemd/system/graphical.target.wants ] && mkdir /etc/systemd/system/graphical.target.wants 2>/dev/null + [ ! -d /etc/systemd/system/basic.target.wants ] && mkdir /etc/systemd/system/basic.target.wants 2>/dev/null + + ln -s "${SERVICEDIR}"/"${SERVICEFILE}" /etc/systemd/system/graphical.target.wants/"${SERVICEFILE}" + ln -s "${SERVICEDIR}"/"${SERVICEFILE}" /etc/systemd/system/basic.target.wants/"${SERVICEFILE}" + + echo "openslx praeinstaller: doing systemd reload." + systemctl daemon-reload + ;; + init) echo "openslx praeinstaller: init(V) detected." + SERVICEDIR=/etc/init.d/ + [ ! -d "${SERVICEDIR}" ] && { echo "openslx praeinstaller: No init directory $SERVICEDIR found, exiting."; exit 1; } + SERVICEFILE=openslx-scriptinstall + echo "openslx praeinstaller: installing init service file to ${SERVICEDIR}/${SERVICEFILE}." + cat <<-THEREDOC > "${SERVICEDIR}"/"${SERVICEFILE}" + #!/bin/sh -e + ### BEGIN INIT INFO + # Provides: openSLX_scriptinstaller + # Required-Start: 2 3 4 5 + # Required-Stop: 0 1 6 + # Default-Start: 2 3 4 5 + # Default-Stop: 0 1 6 + # X-Interactive: false + # Short-Description: openSLX script package starter. + ### END INIT INFO + # + # Starts via Xsetup patching the openSLX script package + # to correct screen resolution and mount network shares + # when Xsetup is started. + + case "\$1" in + start) ${LOGGER} "openslx init file: started." + ${MKDIR} -p /mnt/diskmount + ${MOUNT} | ${GREP} -q /mnt/diskmount || ${MOUNT} /dev/fd1 /mnt/diskmount + ${MOUNTDIR}/linux/vm_runtime + ${LOGGER} "openslx init file: done." + ;; + stop|restart|force-reload) exit 0 ;; + *) echo "Usage: $0 {start}" >&2; exit 1 ;; + esac + THEREDOC + chmod +x "${SERVICEDIR}"/"${SERVICEFILE}" + echo "openslx praeinstaller: enabling ${SERVICEFILE}." + update-rc.d "${SERVICEFILE}" defaults + ;; + *) echo "openslx praeinstaller: Could not determine mother-of-all-processes (not systemd, not init)." + echo "openslx praeinstaller: Giving up, exiting." + exit 1 +esac + +exit 0 + diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/vm_runtime b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/vm_runtime new file mode 100755 index 00000000..0a81c38d --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/data/linux/vm_runtime @@ -0,0 +1,97 @@ +#!/bin/bash + +# set -x + +# logger "openslx vm_runtime: Started." + +DISTRIBUTION=$(lsb_release -is | tr '[A-Z]' '[a-z]' | sed -r 's/[^a-z0-9]//g;s/project$//g;s/scientificsl$/scientific/g') +VERSION=$(lsb_release -rs) +SCRIPTDIR=/mnt/diskmount/linux/ +RESOLUTIONSCRIPT="${SCRIPTDIR}"/resolution +SHARESCRIPT="${SCRIPTDIR}"/mnt_shares +UMOUNTSCRIPT="${SCRIPTDIR}"/umnt_shares +XSETUP=$(find /etc/X* -name "Xsetup" 2>/dev/null) +XRESET=$(find /etc/X* -name "Xreset" 2>/dev/null) + +[ ! -f "${RESOLUTIONSCRIPT}" -o ! -f "${SHARESCRIPT}" ] && \ + { logger "openslx vm_runtime: could not find needed scripts on disk. Exiting." ; exit 1 ; } + +write_xsetup() { + logger "openslx vm_runtime: writing ${XSETUP}..." + cat<<-BISHIER > "${XSETUP}" + #!/bin/sh + ${SHARESCRIPT} & + ${RESOLUTIONSCRIPT} & + BISHIER +} + +patch_xsetup_shebang() { + # Patching after first line, to be exact. + logger "openslx vm_runtime: patching ${XSETUP} after shebang line, if necessary." + grep -q "${RESOLUTIONSCRIPT}" "${XSETUP}" || sed "1 a ${RESOLUTIONSCRIPT} \&" -i "${XSETUP}" + grep -q "${SHARESCRIPT}" "${XSETUP}" || sed "1 a ${SHARESCRIPT} \&" -i "${XSETUP}" +} + +write_xreset() { + logger "openslx vm_runtime: writing ${XRESET}..." + cat<<-BISHIER > "${XRESET}" + #!/bin/sh + ${UMOUNTSCRIPT} & + BISHIER +} + +patch_xreset_shebang() { + # Patching after first line, to be exact. + logger "openslx vm_runtime: patching ${XRESET} after shebang line, if necessary." + grep -q "${UMOUNTSCRIPT}" "${XRESET}" || sed "1 a ${UMOUNTSCRIPT} \&" -i "${XRESET}" +} + +case "${DISTRIBUTION}" in + opensuse) logger "openslx vm_runtime: openSuse detected." + if [ ! -e "${XSETUP}" ]; then + XSETUP=/etc/X11/xdm/Xsetup + write_xsetup + else + logger "openslx vm_runtime: patching ${XSETUP}." + if ! [ grep -c /etc/sysconfig/displaymanager "$XSETUP" 2>/dev/null 1>&2 ]; then + logger "openslx vm_runtime: Xsetup: openSuse marker found, patching." + sed "s#/etc/sysconfig/displaymanager#/etc/sysconfig/displaymanager\n"${SHARESCRIPT}" \&\n${RESOLUTIONSCRIPT} \&#g" \ + -i "${XSETUP}" + else + logger "openslx vm_runtime: Xsetup: openSuse marker not found, patching after shebang line." + xsetup_patch_shebang + fi + logger "openslx vm_runtime: patching ${RESET}." + patch_xreset_shebang + fi + ;; + ubuntu) logger "openslx vm_runtime: Ubuntu detected, version ${VERSION}." + # Ubuntu has at least in version 14.04 LTS a bug leading to missing Xsetup/Xreset entries: + grep -q "session-setup-script=/etc/X11/Xsetup" /etc/lightdm/lightdm.conf.d/50-myconfig.conf \ + || { sed "$ a session-setup-script=/etc/X11/Xsetup" -i /etc/lightdm/lightdm.conf.d/50-myconfig.conf + logger "openslx vm_runtime: session-setup-script patched into /etc/lightdm/lightdm.conf.d/50-myconfig.conf."; } + grep -q "session-reset-script=/etc/X11/Xreset" /etc/lightdm/lightdm.conf.d/50-myconfig.conf \ + || { sed "$ a session-reset-script=/etc/X11/Xreset" -i /etc/lightdm/lightdm.conf.d/50-myconfig.conf + logger "openslx vm_runtime: session-reset-script patched into /etc/lightdm/lightdm.conf.d/50-myconfig.conf."; } + if [ ! -e "${XSETUP}" ]; then + XSETUP=/etc/X11/Xsetup + write_xsetup + else + patch_xsetup_shebang + fi + if [ ! -e "${XRESET}" ]; then + XRESETP=/etc/X11/Xreset + write_xreset + else + patch_xreset_shebang + fi + ;; + debian) logger "openslx vm_runtime: Debian detected." + ;; + *) logger "openslx vm_runtime: Unknown distribution, exiting." + exit 1 + ;; +esac + +exit 0 + diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/data/openslx.exe b/core/modules/run-virt/data/opt/openslx/vmchooser/data/openslx.exe Binary files differindex 114a0dc0..c0904c92 100755 --- a/core/modules/run-virt/data/opt/openslx/vmchooser/data/openslx.exe +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/data/openslx.exe diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/download_vm_metadata.inc b/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/download_vm_metadata.inc new file mode 100644 index 00000000..974a5626 --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/download_vm_metadata.inc @@ -0,0 +1,32 @@ +######################################################## +# Include: Detect, whether runvirt runs in legacy mode # +######################################################## + +# Legacy mode: As runvirt has been before. +# New mode: uuid in xml _and_ vmx given via http. + +writelog "Detecting current/legacy mode ..." + +declare -rg TMPCONFIG="$TMPDIR/vmconfig.tmp" + +# Assume legacy mode by default, only trigger "current" mode if everything else below worked +LEGACY=yes +if [ -z "$IMGUUID" ]; then # Keine uuid: Abbruch, Legacy + writelog "Could not extract a uuid param from ${xmlfile}. Triggering legacy mode." +else + # Now getting template file: + if ! wget -T 6 -O "$TMPCONFIG" "${SLX_VMCHOOSER_BASE_URL}/lecture/${IMGUUID}" 2>/dev/null >&2; then + writelog "wget ${SLX_VMCHOOSER_BASE_URL}/lecture/${IMGUUID}. Triggering legacy mode." + else + writelog "wget ${SLX_VMCHOOSER_BASE_URL}/lecture/${IMGUUID} successful." + if [ ! -s "$TMPCONFIG" ]; then + writelog "Server sent zero byte virtual machine description file. Triggering legacy mode." + else + writelog "Triggering current (non-legacy) mode." + LEGACY= # everything worked - clear legacy mode variable (so we use the "current" mode) + fi + fi +fi + +readonly LEGACY + diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/get_xml_file_variables.inc b/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/get_xml_file_variables.inc new file mode 100644 index 00000000..fdacc69c --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/get_xml_file_variables.inc @@ -0,0 +1,63 @@ +############################################ +# Include: Get needed values from XML file # +############################################ + +writelog "Parsing XML..." + +declare -rg VMSTORE_PATH=/mnt/vmstore + +get_xml () { + xmlextract "//settings/eintrag/${1}/@param" "${xmlfile}" +} + +IMGUUID=$(get_xml "uuid") + +# # Name of the virt image +SRC_IMG_ABSOLUTE=$(get_xml "image_path") +SRC_IMG_RELATIVE=$(get_xml "image_name") + +if [ -z "${SRC_IMG_ABSOLUTE}${SRC_IMG_RELATIVE}" ]; then + writelog "Neither relative nor absolute path for image found in xml" + cleanexit 1 +fi + +if [ -n "$SRC_IMG_ABSOLUTE" ] && [ "${SRC_IMG_ABSOLUTE:0:1}" != "/" ]; then + writelog "Error parsing XML for absolute image path: given value doesn't start with '/': '$SRC_IMG_ABSOLUTE'" + cleanexit 1 +fi + +if [ -z "$SRC_IMG_ABSOLUTE" ]; then + SRC_IMG_ABSOLUTE="${VMSTORE_PATH}/${SRC_IMG_RELATIVE}" +fi + +IMG_BASENAME=$(basename "$SRC_IMG_ABSOLUTE") +writelog "Virtual image file name: $IMG_BASENAME" + +VM_DISPLAYNAME=$(get_xml "short_description") +[ -z "$VM_DISPLAYNAME" ] && VM_DISPLAYNAME="${IMG_BASENAME}" + +# Define VM_NAME_CLEAN since VM_DISPLAYNAME can be long and contain weird characters +VM_NAME_CLEAN=$(echo "${VM_DISPLAYNAME:0:32}" | sed -r 's/[^0-9a-zA-Z_-\.]+/_/g') + +# image is for the following virtual machine +PLUGIN_ID=$(grep -o 'virtualmachine param=.*"' "${xmlfile}" \ + | sed -e "s/&.*;/; /g" | awk -F '"' '{print $2}') + +# Extracting OS type (VM_OS_TYPE) from xml file. We don't care here whether VM_OS_TYPE is empty, as then +# it will yield the default entries later on. +VM_OS_TYPE=$(get_xml "os") + +readonly IMGUUID +readonly SRC_IMG_ABSOLUTE SRC_IMG_RELATIVE +readonly IMG_BASENAME +readonly VM_DISPLAYNAME VM_NAME_CLEAN +readonly PLUGIN_ID +readonly VM_OS_TYPE + +writelog "VM UUID: $IMGUUID" +writelog "Virtualization plugin: $PLUGIN_ID" +writelog "VM name: $VM_DISPLAYNAME" +writelog "VM short name: $VM_NAME_CLEAN" +writelog "VM OS: $VM_OS_TYPE" +writelog "Done parsing XML." + diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/set_runvirt_hardware_variables.inc b/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/set_runvirt_hardware_variables.inc new file mode 100644 index 00000000..508c9efe --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/set_runvirt_hardware_variables.inc @@ -0,0 +1,113 @@ +########################################### +# Include: Set hardware related variables # +########################################### + +get_vm_id() { + local script=${BASH_SOURCE[-1]} + [ -z "$script" ] && script="$0" + if [ -n "$script" ]; then + script=$(readlink -f "$script") + if [ -n "$script" ] && [ -s "$script" ]; then + #bingo + VM_ID=$(ps ax | grep -F "$script" | grep -v 'grep' | grep -o -- "${script}.*\$" | sort -u | wc -l) + if [ "$VM_ID" -gt 0 ]; then + [ "${#VM_ID}" -eq 1 ] && VM_ID="0${VM_ID}" + [ "${#VM_ID}" -gt 2 ] && VM_ID="${VM_ID:0:2}" + [ "${#VM_ID}" -eq 2 ] && return + fi + fi + fi + # fallback: take last two digits of current pid... + VM_ID=$(expr substr $$ $(expr ${#$} - 1) 2) + [ "${#VM_ID}" -eq 1 ] && VM_ID="0${VM_ID}" +} + +get_vm_id + +# Make sure cpu_cores is not empty +cpu_cores=${cpu_cores:-"1"} + +# Amount of memory for the VM. Be generous if diff is written to HDD +if mount | grep -q '^/dev/sd.*on.*/tmp'; then + reserve=20 + min=768 + max=1800 +else + reserve=65 + min=768 + max=8192 +fi + +# Calculate absulute amount of RAM that should stay available to the host +reserve="$(( ( totalmem * reserve ) / 100 ))" +# Respect some upper and lower bounds for the host amount +[ "$reserve" -lt "$min" ] && reserve="$min" +[ "$reserve" -gt "$max" ] && reserve="$max" + +# Get a result which can be divided by 4 +mem="$(( ( ( totalmem - reserve ) / 4 ) * 4 ))" +if [ -n "$mainvirtmem" ]; then + forcemem="$(( mainvirtmem / 4 * 4 ))" + mem="$forcemem" +fi +hostmem="$(( totalmem - mem ))" + +# Fill in VMID +macaddrsuffix=$(echo "$macaddrsuffix" | sed "s/%VMID%/${VM_ID}/") + +if ! echo "$macaddrprefix" | grep -q -E '^[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}$'; then + slxlog "virt-mac" "Could not properly generate mac address prefix (got $macaddrprefix)" +fi +if ! echo "$macaddrsuffix" | grep -q -E '^[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}$'; then + slxlog "virt-mac" "Could not properly generate mac address suffix (got $macaddrsuffix)" +fi +macaddr="$macaddrprefix:$macaddrsuffix" + +# Virtual fd/cd/dvd and drive devices, floppy b: for configuration +# if $floppy_0 from virtualization.conf set then fdtest="TRUE" +fdtest=${floppy_0:+"TRUE"} +# if $fdtest not set floppy0="FALSE", else "TRUE" +floppy0=${fdtest:-"FALSE"} +# if $cdrom_0 from virtualization.conf set then cdtest="TRUE" +cdtest=${cdrom_0:+"TRUE"} +# if $cdtest not set cdrom0="FALSE", else "TRUE" +cdrom0=${cdtest:-"FALSE"} +# if $cdrom_1 from virtualization.conf set then cdtest="TRUE" +cdtest=${cdrom_1:+"TRUE"} +# if $cdtest not set cdrom1="FALSE", else "TRUE" +cdrom1=${cdtest:-"FALSE"} + +# Dynamically detect serial ports here instead of at boot time +# (virtualization.conf), since USB serial ports get quite common +# and might not be plugged in at boot time yet +serial_0= +#for port in $(awk '{ if ($1 ~ /^[0-9]+:/ && $2 != "uart:unknown") print "/dev/ttyS" sub(/:\$/, "", $1) }' /proc/tty/driver/serial); do +for port in $serial_ports /dev/ttyUSB*; do + [ -c "$port" ] || continue + serial_0="$port" + break +done + +parallel_0= +for port in /dev/parport*; do + [ -c "$port" ] || continue + parallel_0="$port" + break +done + +# RDP/VNC port (59001 - 59099) +remotedesktopport="590${VM_ID}" + +# Add rw share for home dir +homesharepath="${HOME}/PERSISTENT" +homesharename="home" + +# Add common share +commonsharepath="${HOME}/SHARE" +commonsharename="share" + +# Set hostname: using original hostname and adding string +hostname="virt-$(hostname)" + +writelog "\tVM Hostname:\t\t$hostname" + diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/set_runvirt_variables.inc b/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/set_runvirt_variables.inc new file mode 100644 index 00000000..cd9a573a --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/set_runvirt_variables.inc @@ -0,0 +1,26 @@ +###################################################### +# Include: Declaration of run-virt default variables # +###################################################### + +VMCHOOSER_DIR="/opt/openslx/vmchooser" +VMCHOOSER_CONF_DIR="$VMCHOOSER_DIR/config" +USER="$(whoami)" +LOGFILE="/var/log/openslx/run-virt.${USER}.$$.log" +TMPDIR="/tmp/virt/${USER}/$$" + +readonly VMCHOOSER_DIR VMCHOOSER_CONF_DIR LOGFILE TMPDIR USER + +# include general configuration from vmchooser +[ -f "$VMCHOOSER_CONF_DIR/vmchooser.conf" ] && . "$VMCHOOSER_CONF_DIR/vmchooser.conf" +# load general virtualization information +[ -f "$VMCHOOSER_CONF_DIR/virtualization.conf" ] && . "$VMCHOOSER_CONF_DIR/virtualization.conf" +# Load general openslx config +[ -f "/opt/openslx/config" ] && . "/opt/openslx/config" +# Create temp dir +if ! mkdir -p "$TMPDIR"; then + slxlog "virt-tmpdir" "Could not create temporary directory '$TMPDIR' for session" + error_user "Konnte kein temporäres Arbeitsverzeichnis für die VM-Sitzung anlegen. Ein Computer-Neustart könnte das Problem lösen." + cleanexit 1 + exit 1 +fi + diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/setup_firewall.inc b/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/setup_firewall.inc new file mode 100644 index 00000000..f0820ed7 --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/setup_firewall.inc @@ -0,0 +1,12 @@ + +setup_firewall () { + local LOGF="${TMPDIR}/firewall.log" + local RET + [ "$DISPLAY" = ":0" ] || return 0 # For now, to avoid conflicts, we only do this on display :0 + slxfwtool "$IMGUUID" > "$LOGF" 2>&1 + RET=$? + if [ "$RET" != "0" ]; then + slxlog "runvirt-firewall" "Error setting up firewall rules for lecture $IMGUUID (Exit code $RET)" "$LOGF" + fi + return 0 +} diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/setup_image_access.inc b/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/setup_image_access.inc new file mode 100644 index 00000000..4240d9ca --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/setup_image_access.inc @@ -0,0 +1,98 @@ +########################################################### +# Include: Setup dnbd for image access, nfs/cifs fallback # +########################################################### + +# This will currently make sure that the variable +# VM_DISKFILE_RO is set which will contain the +# absolute path to the disk image to use for the vm +# session. +# When using DNBD3 this will differ from SRC_IMG_ABSOLUTE, +# otherwise it will be identical. +# In the future DNBD3 (or something else) might provide +# a CoW layer so we don't need snapshots etc. anymore. +# This include should set VM_DISKFILE_RW in that case to +# indicate to the virt plugin that it doesn't need to +# handle creating a temporary CoW layer itself. + +writelog "Setting up disk access for virtualizer/emulator ..." + +# Try to use dnbd3 to access the image +unset VM_DISKFILE_RO +unset dnbd3_fuse_mount_point + +setup_dnbd3 () { + # Mount path for images mounted with dnbd3-fuse + dnbd3_fuse_mount_point="$TMPDIR/dnbd3fuse.mnt" + mkdir -p "${dnbd3_fuse_mount_point}" + # start dnbd3-fuse in subshell + local dnbd3_tmplog="$TMPDIR/dnbd3fuse.log" + local dnbd3_exitflag="$TMPDIR/dnbd3exit$RANDOM" + local TIMEOUT vm_revision + rm -f -- "$dnbd3_exitflag" + ( + dnbd3-fuse -f -o allow_other,max_readahead=262144 -h "$SLX_DNBD3_SERVERS" -i "${SRC_IMG_RELATIVE}" "${dnbd3_fuse_mount_point}" > "$dnbd3_tmplog" 2>&1 + RET=$? + touch "$dnbd3_exitflag" + if [ "$RET" != "0" ]; then + writelog "dnbd3-fuse stopped working (Exit code $RET)" + slxlog "virt-dnbd3-fuse" "dnbd3-fuse stopped/refused serving '${SRC_IMG_RELATIVE}' from '${SLX_DNBD3_SERVERS}' with error code: $RET" "${dnbd3_tmplog}" + fi + ) & + # give it a bit of time + usleep 250000 + # check if we have the image + for TIMEOUT in 0.5 1 1 OUT; do + if [ -r "${dnbd3_fuse_mount_point}/img" ]; then + vm_revision="$(grep -m 1 "^Revision:" "${dnbd3_fuse_mount_point}/status" | cut -d" " -f2)" + VM_DISKFILE_RO="${dnbd3_fuse_mount_point}/img" + writelog "DNBD3: $SRC_IMG_RELATIVE on $VM_DISKFILE_RO with rid $vm_revision" + break + fi + [ "$TIMEOUT" = "OUT" -o -e "$dnbd3_exitflag" ] && break + sleep "$TIMEOUT" + done + + if [ -z "$VM_DISKFILE_RO" ]; then + slxlog "virt-dnbd3" "No dnbd3 server for ${SRC_IMG_RELATIVE} found, trying NFS/CIFS..." "$dnbd3_tmplog" + writelog "No working dnbd3 server found :-(" + fi +} + +# See if we should setup dnbd3 image access at all +if ! which dnbd3-fuse; then + writelog "Can't use dnbd3 as dnbd3-fuse binary is not in PATH" +elif [ -z "$SRC_IMG_RELATIVE" ]; then + writelog "Can't use dnbd3 as SRC_IMG_RELATIVE is not set" +elif [ -z "$SLX_DNBD3_SERVERS" ] || [ "x$SLX_VM_DNBD3" != "xyes" ]; then + writelog "Can't use dnbd3 as no servers are given in config, or SLX_VM_DNBD3 is not set to yes" +else + setup_dnbd3 +fi + +# VM_DISKFILE_RO will be empty if dnbd3 is not used or failed to connect. +# Let's try to fall back to NFS/CIFS via file system +if [ -z "$VM_DISKFILE_RO" ]; then + # Maybe we're reading a dnbd3 directory with RIDs encoded into the filename - use latest one + rid_suffix=$(ls -1 "${SRC_IMG_ABSOLUTE}.r"* | grep -E -o '\.r[0-9]+$' | grep -o -E '[0-9]+$' | sort -n | tail -n 1) + if [ -n "$rid_suffix" ]; then + # found + VM_DISKFILE_RO="${SRC_IMG_ABSOLUTE}.r${rid_suffix}" + elif [ -e "$SRC_IMG_ABSOLUTE" ]; then + # try name we got from xml in the first place + VM_DISKFILE_RO="$SRC_IMG_ABSOLUTE" + fi +fi + +# Check if virtual machine container file exists +if [ -z "$VM_DISKFILE_RO" ] || ! [ -e "${VM_DISKFILE_RO}" ]; then + slxlog "virt-image-missing" "VM image $VM_DISKFILE_RO not found!" + writelog "Virtual machine image ${VM_DISKFILE_RO} not found!" + error_user "Das Image für die gewählte Virtuelle Maschine konnte nicht gefunden werden. +Versuchen Sie zunächst, den Computer komplett neu zu starten. Sollte das Problem bestehen bleiben, wenden Sie sich bitte an den Support." + cleanexit 1 +fi + +readonly VM_DISKFILE_RO + +writelog "Disk file to use: $VM_DISKFILE_RO" + diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/setup_printer_lpd.inc b/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/setup_printer_lpd.inc new file mode 100644 index 00000000..2fb9310a --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/setup_printer_lpd.inc @@ -0,0 +1,46 @@ +##################################### +# Include: Setup printer daemon LPD # +##################################### + +QUEUE="STANDARD" # This has to match the queue you configured in your VM +SPOOLDIR= + +### Disabled: 100megs is not enough, some jobs are HUGE, try to use temp which should be on disk +## Try using user's tmpfs home first, as it gets wiped on logout +#if [ -n "${HOME}" ] && [ -w "${HOME}" ]; then +# SPOOLDIR="${HOME}/.spool" +# mkdir -p "${SPOOLDIR}/${QUEUE}" +#fi +# If failed, try to fall back to /tmp + +if [ -z "${SPOOLDIR}" ] || [ ! -w "${SPOOLDIR}/${QUEUE}" ]; then + SPOOLDIR="${TMPDIR}/printergui-${RANDOM}" + rm -rf -- "${SPOOLDIR}" + if ! mkdir -p "${SPOOLDIR}/${QUEUE}"; then + slxlog "virt-spooldir" "Could not create spool directory ($SPOOLDIR) for $USER - printing will not work!" + # TODO: Warn user + fi + chmod 0700 "${SPOOLDIR}/${QUEUE}" +fi + +# Start the lpdaemon listening on the given port +# TODO: externalize with something like runvirt.d (other parts might benefit from that too) +tcpsvd -E 192.168.101.1 5515 \ + lpd "$SPOOLDIR" \ + ash -c "/opt/openslx/scripts/run-virt_print '${USER}' \"${SPOOLDIR}/${QUEUE}/\$DATAFILE\"" & + +# PID to kill the process +PID_LPD="$!" + +{ + sleep 2 + # Check if tcpsvd is running. Do this a little delayed so we do not check + # immediately after trying to spawn it, as this could result in + # success even if it's not really working. + + if ! kill -0 "$PID_LPD"; then + slxlog "virt-lpd" "Could not start tcpsvd/lpd for virtual machine session" + notify_user "Durcksystem" "Das Drucksystem konnte nicht initialisiert werden. Druckfunktion nicht verfügbar." + fi +} & + diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/setup_sound.inc b/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/setup_sound.inc new file mode 100644 index 00000000..c6cece3d --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/setup_sound.inc @@ -0,0 +1,79 @@ +#!/bin/bash +# ^-- Add shebang even though it's sourced so vim highlights bash specific features properly +######################## +# Include: Setup sound # +######################## + +writelog "Starting sound setup ..." + +unset VOL +if [ -r "/run/hwinfo" ] && source "/run/hwinfo"; then + # On startup, the volume of Master, PCM, Speaker, etc. will be set to 100% + # Some hardware with builtin speakers might be a bit too loud then, so you can + # define an exception table here. Array key is "Manufacturer//Model" + declare -A VOLUME_EXCEPTIONS + VOLUME_EXCEPTIONS["Hewlett-Packard//HP Compaq 8200 Elite CMT PC"]="85%" # This is bwPC3 + # Read + VOL=${VOLUME_EXCEPTIONS["${HW_MANUF}//${HW_MODEL}"]} +fi + +# Default to maximum volume +if [ -z "$VOL" ]; then + VOL="100%" +fi + +if true; then + # detecting which card is to be used + writelog --quiet "Detecting which sound card to use ..." + PROC="/proc/asound/cards" + if [ ! -r "$PROC" ]; then + writelog --quiet "'${PROC}' not found or not readable." + SOUND_CARD_INDEX=0 + SOUND_CARD_COUNT=1 + else + # Try to filter HDMI cards first + SOUND_CARD_INDEX=$(grep -v -i 'HDMI' "${PROC}" | grep -E -o '^[[:space:]]{0,2}[0-9]+[[:space:]]+' | head -n 1) + # If empty, try again with all + [ -z "${SOUND_CARD_INDEX}" ] && SOUND_CARD_INDEX=$(cat "${PROC}" | grep -E -o '^[[:space:]]{0,2}[0-9]+[[:space:]]+' | head -n 1) + if [ -z "${SOUND_CARD_INDEX}" ]; then + writelog --quiet "No sound card found." + SOUND_CARD_INDEX=0 + fi + SOUND_CARD_COUNT=$(grep -E '^[[:space:]]{0,2}[0-9]+[[:space:]]+' "${PROC}" | wc -l) + fi + + SOUND_CARD_INDEX="$(grep -E -o '[0-9]+' <<<$SOUND_CARD_INDEX)" + writelog --quiet "Detected sound card index is: $SOUND_CARD_INDEX" + writelog --quiet "Sound card count: $SOUND_CARD_COUNT" + + # Adjust sound volume (playback)... Random mixer names we have encountered during testing + writelog --quiet "Setting up volume..." + ( + amixer -q -c "$SOUND_CARD_INDEX" sset 'Master' "$VOL" unmute + amixer -q -c "$SOUND_CARD_INDEX" sset 'PCM' "100%" unmute + amixer -q -c "$SOUND_CARD_INDEX" sset 'CD' "100%" unmute + amixer -q -c "$SOUND_CARD_INDEX" sset 'Headphone' "100%" unmute + amixer -q -c "$SOUND_CARD_INDEX" sset 'Front' "100%" unmute + amixer -q -c "$SOUND_CARD_INDEX" sset 'Speaker' "100%" unmute + # Recording. It seems that (most) devices need the volume set to 0, so you + # don't hear your own mic input, but should be unmuted. Also on some cards, + # you need to set the cap option on the mic you want to use, while other cards + # will just ignore that option. + # Plus, most cards have a Capture mixer, which needs to be set to cap too, and + # have its volume turned up. (There'll probably be some cards that need yet + # another setup, but this works for now on 4 tested cards) + amixer -q -c "$SOUND_CARD_INDEX" sset 'Rear Mic Boost' "50%" cap unmute + amixer -q -c "$SOUND_CARD_INDEX" sset 'Rear Mic' "0%" cap unmute + amixer -q -c "$SOUND_CARD_INDEX" sset 'Front Mic Boost' "50%" cap unmute + amixer -q -c "$SOUND_CARD_INDEX" sset 'Front Mic' "0%" cap unmute + amixer -q -c "$SOUND_CARD_INDEX" sset 'Mic Boost' "50%" cap unmute + amixer -q -c "$SOUND_CARD_INDEX" sset 'Mic' "0%" cap unmute + amixer -q -c "$SOUND_CARD_INDEX" sset 'Capture' "100%" cap unmute + amixer -q -c "$SOUND_CARD_INDEX" sset 'Input Source' 'Mic' + amixer -q -c "$SOUND_CARD_INDEX" sset 'Input Source' 'Front Mic' # Let's hope nobody uses rear mic... + # fix random static noise when starting vmplayer when module snd_pcsp (not pcspkr) is loaded + amixer -q -c pcsp sset Master "0%" mute 2>/dev/null >&2 + ) 2>&1 | grep -v 'amixer: Unable to find' + writelog --quiet "Done setting up volume." +fi >> "${LOGFILE}" 2>&1 # Don't pipe here since it would spawn a subshell so all variable modifications would be lost + diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/setup_virtual_floppy.inc b/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/setup_virtual_floppy.inc new file mode 100644 index 00000000..d9ae052c --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/setup_virtual_floppy.inc @@ -0,0 +1,101 @@ +#!/bin/bash +############################################## +# Include: Setup virtual floppy for drive b: # +############################################## + +declare -rg FLOPPYIMG="${TMPDIR}/floppy.img" +declare -rg TMPHOME="${HOME}" +declare -rg RUNSCRIPT="${TMPDIR}/runscript.tmp" + +wget -T 6 -O "${RUNSCRIPT}" "${SLX_VMCHOOSER_BASE_URL}/lecture/${IMGUUID}/runscript" > /dev/null & +WGET=$! + +dd "if=/dev/zero" "of=${FLOPPYIMG}" count=1440 bs=1024 +chmod 0600 "${FLOPPYIMG}" +mkfs.fat "${FLOPPYIMG}" || mkfs.vfat "${FLOPPYIMG}" || mkdosfs "${FLOPPYIMG}" + +# Create file with resolution information etc. +. "/opt/openslx/inc/shares" +if [ -z "$SHARE_REMAP_MODE" ]; then + SHARE_REMAP_MODE_INI="0" + SHARE_REMAP_MODE="3" +else + SHARE_REMAP_MODE_INI="$SHARE_REMAP_MODE" +fi +[ -z "$SHARE_CREATE_MISSING_REMAP" ] && SHARE_CREATE_MISSING_REMAP="1" +declare -rg RESOLUTION=$(xrandr | grep -o -E 'connected\s*(primary)?\s*[0-9]+x[0-9]+\+0\+0' \ + | grep -o -E -m1 '[0-9]+x[0-9]+') + +# Legacy: HOSTRES.TXT +cat > "${TMPDIR}/HOSTRES.TXT" <<-HIER +${RESOLUTION} +HIER + +# Create file for network shares to mount +declare -rg SHARES="${TMPDIR}/shares.dat" +touch "${SHARES}" +chmod 0600 "${SHARES}" +if ! pwdaemon --query "${TMPHOME}/.pwsocket" > "${SHARES}"; then + slxlog "virt-pwdaemon" "Could not start pwdaemon" +else + sed -i 's/^/192.168.101.1\t/' "${SHARES}" # TODO: Depending on nettype (in case we have != nat some day) + if [ "${SHARE_REMAP_MODE}" = 1 -o "${SHARE_REMAP_MODE}" = 2 ] && [ -e "${TMPHOME}/.home" ]; then + NETHOME=$(cat "${TMPHOME}/.home") + [ -z "$SHARE_HOME_DRIVE" ] && SHARE_HOME_DRIVE="H:" + # Tab between items, so spaces can be used! + echo "${NETHOME} ${SHARE_HOME_DRIVE} Home-Verzeichnis" >> "${SHARES}" + fi + for VAR in ${!SHARE_LINE_*}; do + echo "${!VAR}" >> "${SHARES}" + done +fi + +wait "$WGET" + +# Check downloaded runscript, handle extension marker +EXT= +if [ -s "$RUNSCRIPT" ]; then + EXT=$(head -n 1 "$RUNSCRIPT" | grep -o -i '^EXT=.*$' | cut -d '=' -f 2-) + [ -n "$EXT" ] && [ "x${EXT:0:1}" != "x." ] && EXT=".$EXT" +fi + +# Write info file +UNAME= +[ -s "${HOME}/.account" ] && UNAME=$(cat "${HOME}/.account") +[ -z "${UNAME}" ] && UNAME=$(whoami) +cat > "${TMPDIR}/openslx.ini" <<-HIER +[openslx] +username=${UNAME} +resolution=${RESOLUTION} +createMissingRemap=${SHARE_CREATE_MISSING_REMAP} +remapMode=${SHARE_REMAP_MODE_INI} +homeDrive=${SHARE_HOME_DRIVE} +scriptExt=${EXT} + +[remap] +documents=${SHARE_DOCUMENTS} +downloads=${SHARE_DOWNLOADS} +desktop=${SHARE_DESKTOP} +media=${SHARE_MEDIA} +other=${SHARE_OTHER} +HIER + +# Copy all them there filez into floppy image +mcopy -i "${FLOPPYIMG}" "${TMPDIR}/openslx.ini" "${TMPDIR}/HOSTRES.TXT" "${SHARES}" "::/" +mcopy -i "${FLOPPYIMG}" "$VMCHOOSER_DIR/data/openslx.exe" "::/" +# Copy guest configuration (with added information) config.xml to be accessed +# via virtual floppy +mcopy -i "${FLOPPYIMG}" "$xmlfile" "::/config.xml" + +# Copying linux directory: +mcopy -s -i "${FLOPPYIMG}" "$VMCHOOSER_DIR/data/linux" "::/" + +# User supplied runscript +if [ -n "$EXT" ]; then + sed -i '1d' "${RUNSCRIPT}" + mcopy -i "${FLOPPYIMG}" "${RUNSCRIPT}" "::/runscript${EXT}" +fi + +rm -f -- "${SHARES}" "${TMPDIR}/openslx.ini" "${TMPDIR}/HOSTRES.TXT" +unset SHARES VAR NETHOME UNAME + diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/setup_vm_hypervisor.inc b/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/setup_vm_hypervisor.inc new file mode 100644 index 00000000..7709a85d --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/setup_vm_hypervisor.inc @@ -0,0 +1,34 @@ +########################################################################## +# Include: Setup virtual machine hypervisor via vm-specific include file # +########################################################################## + +# Get all virtual machine specific stuff from the respective include file +if [ ! -e "$VMCHOOSER_DIR/$PLUGIN_ID/run-virt.include" ] ; then + slxlog "virt-plugin-missing" "Could not find run-virt.include for $PLUGIN_ID ($VMCHOOSER_DIR/$PLUGIN_ID/run-virt.include)" + writelog "Failed because of missing ${PLUGIN_ID} plugin." + error_user "Konnte den Virtualisierer '$PLUGIN_ID' nicht finden. Starten der Virtuellen Maschine fehlgeschlagen. + Starten Sie den Computer neu und wenden Sie sich an den Support, wenn das Problem weiterhin besteht." + cleanexit 1 +fi + +self="${PLUGIN_ID}" + +if ! bash -n "$VMCHOOSER_DIR/$PLUGIN_ID/run-virt.include"; then + slxlog "virt-plugin-syntax" "run-virt.include for $PLUGIN_ID contains syntax errors (bash -n run-virt.include failed)" + writelog "Erroneous run-virt.include for $PLUGIN_ID (syntax check)" + error_user "Das Start-Script für den Virtualisierer '$PLUGIN_ID' ist fehlerhaft. + Starten Sie den Computer neu und wenden Sie sich an den Support, wenn das Problem weiterhin besteht." + cleanexit 1 +fi + +setup_vm_commandline () { + # Now including the hypervisor specific include file: + if ! source "$VMCHOOSER_DIR/$PLUGIN_ID/run-virt.include"; then + slxlog "virt-plugin-error" "run-virt.include for $PLUGIN_ID could not be sourced properly." + writelog "Erroneous run-virt.include for ${PLUGIN_ID}? Source returned != 0" + error_user "Das Start-Script für den Virtualisierer '$PLUGIN_ID' hat einen fehlercode zurückgegeben. + Starten Sie den Computer neu, falls es beim Ausführen der VM zu Problemen kommt + und wenden Sie sich an den Support, wenn das Problem weiterhin besteht." + fi +} + diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/start_windowmanager.inc b/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/start_windowmanager.inc new file mode 100644 index 00000000..da43f341 --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/start_windowmanager.inc @@ -0,0 +1,27 @@ +#################################################### +# Include: Start windowmanager for easier handling # +#################################################### + +# Some problems may arise with windows opening in background when +# using eg. vmware without a window manager. + +FOUND_WM= +for dm in openbox kwin xfwm4 metacity blackbox twm fvwm2 ; do + if which $dm >/dev/null 2>&1 ; then + if [ "$dm" = "fvwm2" ] ; then + echo "EdgeScroll 0 0" > ${redodir}/fvwm + writelog "Starting fvwm2." + fvwm2 -f ${redodir}/fvwm >/dev/null 2>&1 & + else + writelog "Starting ${dm}." + $dm >/dev/null 2>&1 & + fi + FOUND_WM=1 + break + fi +done + +if [ -z "$FOUND_WM" ]; then + slxlog "virt-windowmanager" "Could not find any window manager to use!" + notify_user "Konnte keinen Window Manager finden. (Das ist schlecht!)" +fi diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/usb_detector.inc b/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/usb_detector.inc new file mode 100644 index 00000000..a2d442e4 --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/usb_detector.inc @@ -0,0 +1,77 @@ +# Helper function that will scan /dev/bus/usb for devices matching specific classes +# and then output the corresponding device ids. This can be used for selective +# handover of devices to a virtual machine + +declare -rg PASSTHROUGH_USB_DEVICES="2 0:5 0:6 0:7 0:14 0:16 0:17 239" + +# $1: expression to fill with device information. +# valid placeholders are: +# %VENDOR% - device vendor id +# %PRODUCT% - device product id +# $2-n: device classes to include in output +get_usb_devices_int() { + [ -z "$TMPDIR" ] && TMPDIR="/tmp" + local EXP=$1 + shift + if [ -z "$EXP" ]; then + writelog --quiet "No ouput expression template passed to get_usb_devices" + cleanexit 1 + fi + if [ $# -eq 0 ]; then + writelog --quiet "No device classes given to get_usb_devices" + cleanexit 1 + fi + local MATCH=';' + while [ $# -gt 0 ]; do + MATCH+="$1;" + [[ "$1" != *:* ]] && MATCH+="0:$1;" + shift + done + local dev= + local key value trailing + trailing= + local tmp="${TMPDIR}/lsusb.$$.$RANDOM" + for dev in /dev/bus/usb/*/*; do + if ! lsusb -D "$dev" > "$tmp" 2>/dev/null; then + writelog --quiet "Cannot lsusb $dev" + continue + fi + local DC= + local OK= + local VENDOR= + local PRODUCT= + while read -r key value trailing || [ -n "$key" ]; do + if [[ "$key" == "idVendor" ]]; then + [[ "$value" == 0x* ]] && VENDOR="${value:2}" + elif [[ "$key" == "idProduct" ]]; then + [[ "$value" == 0x* ]] && PRODUCT="${value:2}" + elif [ -z "$DC" ]; then + # No bDeviceClass seen yet + if [[ "$key" == "bDeviceClass" ]]; then + DC="$value" + [[ "$MATCH" == *";${DC};"* ]] && OK=yo + fi + else + # #DeviceClass is generic, look at sub class + if [[ "$key" == "bInterfaceClass" ]]; then + [[ "$MATCH" == *";${DC}:${value};"* ]] && OK=yo + fi + fi + if [ -n "$OK" -a -n "$VENDOR" -a -n "$PRODUCT" ]; then + echo "$EXP" | sed "s/%VENDOR%/${VENDOR}/g;s/%PRODUCT%/${PRODUCT}/g" + break + fi + done < "$tmp" + done + rm -f -- "$tmp" +} + +get_usb_devices() { + if which lsusb 2>/dev/null >&2 && lsusb --help 2>&1 | grep -q -- '-D' 2>/dev/null; then + [ $# -eq 1 ] && set -- "$1" $PASSTHROUGH_USB_DEVICES # no quotes here! + get_usb_devices_int "$@" | sort -u + else + writelog --quiet "Cannot scan usb bus: lsusb not found or doesn't support -D" + fi +} + diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/vmchooser_runvirt_functions.inc b/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/vmchooser_runvirt_functions.inc new file mode 100644 index 00000000..ca475da0 --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/run-virt-includes/vmchooser_runvirt_functions.inc @@ -0,0 +1,107 @@ +####################################################### +# Include: Set functions needed by vmchooser-run_virt # +####################################################### + +# function to write to stdout and logfile +writelog() { + local DATE=$(date +%Y-%m-%d-%H-%M-%S) + # write to stdout? + if [ "x$1" = "x--quiet" ]; then + shift + else + echo -e "$DATE: $@" + fi + # log into file + echo -e "$DATE: $@" >> "${LOGFILE}" +} + +notify_user() { + local TOPIC="$1" + shift + notify-send -u normal "$TOPIC" "$@" + writelog "Notify: **${TOPIC}**: $*" +} + +error_user() { + local TOPIC="$1" + shift + local MSG TITLE BODY + if [ $# -gt 0 ]; then + MSG=" $TOPIC +$*" + TITLE="$TOPIC" + BODY="$*" + else + MSG="$TOPIC" + TITLE="ERROR" + BODY="$TOPIC" + fi + # Zenity should yield the nicest result + zenity --error --title "$TITLE" --text "$BODY" && return + # QnD abuse printergui for error message as it's blocking + /opt/openslx/cups/printergui --error "$MSG" && return + # printergui might not exist, try fallback here + # unfortunately, i can only think of notify+sleep right now + notify-send -u critical "$TITLE" "$BODY" + sleep 10 +} + +# Clean exit will be called at the end of vmchooser-run_virt +cleanexit() { + sleep 1 + # Ummount dnbd3-fuse + if [ -n "$dnbd3_fuse_mount_point" ] && [ -e "$dnbd3_fuse_mount_point/img" ]; then + for timeout in 1 1 1 FAIL; do + fusermount -u "$dnbd3_fuse_mount_point" && break + writelog "dnbd3 still busy...." + [ "$timeout" = "FAIL" ] && break + sleep "$timeout" + done + fi + # Kill LPD + [ -n "${PID_LPD}" ] && kill "${PID_LPD}" + + # If we're not in debug mode, remove all temporary files + if [ -n "${TMPDIR}" -a -z "$SLX_DEBUG" ]; then + rm -rf -- "${TMPDIR}" + fi + + [ $# -gt 0 ] && exit "$1" + exit 129 # No exit code was given :/ +} + +rv_clean_string() { + if [ "$#" -ge 1 ]; then + echo "$@" | tr '[A-Z]' '[a-z]' | tr -d -c '[a-z0-9\-]' + else + tr '[A-Z]' '[a-z]' | tr -d -c '[a-z0-9\-]' + fi +} + +# Check if the given variables are set (empty or not) +isset() { + while [ $# -gt 0 ]; do + [ -z "${!1+x}" ] && return 1 + shift + done + return 0 +} + +# Check if the given variables are not empty +notempty() { + while [ $# -gt 0 ]; do + [ -z "${!1}" ] && return 1 + shift + done + return 0 +} + +## +# Extract given xpath from given xml file +# e.g.: xmlextract '//node/nestednode/@attribute' "$file" +# @param +# @return Plain text, UTF-8 +xmlextract() { + xmlstarlet sel -T -E utf-8 -t -v "$1" "$2" +} + diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/scripts/set-firewall b/core/modules/run-virt/data/opt/openslx/vmchooser/scripts/set-firewall new file mode 100644 index 00000000..2773150c --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/scripts/set-firewall @@ -0,0 +1,130 @@ +#!/bin/bash + +# Do not rename/move this script, or change fwtool.c accordingly + +[ "$UID" = "0" ] || exit 1 + +declare -rg RULES=$(mktemp) + +[ -n "$RULES" ] || exit 2 + +[ -n "$1" ] || exit 3 + +[ "${#1}" -ge 10 ] || exit 4 +[ "${#1}" -lt 40 ] || exit 5 + +. /opt/openslx/config + +for TOOL in iptables ip6tables; do + $TOOL -w -F runvirt-INPUT || $TOOL -w -N runvirt-INPUT + $TOOL -w -F runvirt-OUTPUT || $TOOL -w -N runvirt-OUTPUT + + if ! $TOOL -w -C INPUT -i br0 -j runvirt-INPUT; then + $TOOL -w -A INPUT -i br0 -j runvirt-INPUT + fi + if ! $TOOL -w -C OUTPUT -o br0 -j runvirt-OUTPUT; then + $TOOL -w -A OUTPUT -o br0 -j runvirt-OUTPUT + fi + if ! $TOOL -w -C FORWARD -i br0 -j runvirt-INPUT; then + $TOOL -w -A FORWARD -i br0 -j runvirt-INPUT + fi + if ! $TOOL -w -C FORWARD -o br0 -j runvirt-OUTPUT; then + $TOOL -w -A FORWARD -o br0 -j runvirt-OUTPUT + fi + $TOOL -A runvirt-INPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT + $TOOL -A runvirt-OUTPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT +done + +declare -rg AUTORULES=$(mktemp) + +add_ips () { + # add_ips "IN/OUT" "IP1 IP2 IPn" "PORT" "ACCEPT/REJECT" + local IP + [ -z "$1" -o -z "$2" -o -z "$3" -o -z "$4" ] && return 1 + for IP in $2; do + echo "$1 $IP $3 $4" >> "${AUTORULES}" + done +} + +add_ips "IN" "127.0.0.0/8" 0 "ACCEPT" +add_ips "OUT" "127.0.0.0/8" 0 "ACCEPT" +add_ips "OUT" "$SLX_DNS" 53 "ACCEPT" +add_ips "OUT" "$SLX_DNBD3_SERVERS" 5003 "ACCEPT" +add_ips "OUT" "$SLX_KCL_SERVERS $SLX_SERVER_IP" 0 "ACCEPT" + +if [ -n "$SLX_VM_NFS" ]; then + IP= + if [ "${SLX_VM_NFS:0:2}" = '//' ]; then + IP=${SLX_VM_NFS:2} + IP=${IP%%/*} + else + IP=${SLX_VM_NFS%%:*} + fi + [ -n "$IP" ] && add_ips "OUT" "$IP" 0 "ACCEPT" +fi + +sort -u "${AUTORULES}" > "${RULES}" + +wget -T 6 -O - "${SLX_VMCHOOSER_BASE_URL}/lecture/$1/netrules" >> "${RULES}" 2> "${AUTORULES}" +RET=$? + +if [ "$RET" != "0" ]; then + echo "wget exit code: $RET :-(" + grep -q "ERROR 404" "${AUTORULES}" && exit 0 + exit 6 +fi + +declare -rg V4='^[0-9]+(\.[0-9]+)*(/[0-9]+)?$' +declare -rg V6='^([0-9a-fA-F]+|:)(:+[0-9a-fA-F]*)*(/[0-9]+)?$' + +while read -r DIR DEST PORT ACTION GARBAGE || [ -n "$DIR" ]; do + if [ -z "$DEST" -o -z "$PORT" -o -z "$ACTION" ]; then + echo "Invalid rule: '$DIR $DEST $PORT $ACTION'" + continue + fi + IPLINE1=" -w" + IPLINE2= + if [ "$DIR" = "IN" ]; then + IPLINE1+=" -A runvirt-INPUT" + elif [ "$DIR" = "OUT" ]; then + IPLINE1+=" -A runvirt-OUTPUT" + else + continue + fi + if ! [[ $PORT =~ ^[0-9]+$ ]] || [ "$PORT" -gt 65535 ]; then + echo "Invalid port: '$PORT'" + continue + fi + if [ "$DEST" != "*" ]; then + if [ "$DIR" = "OUT" ]; then + IPLINE1+=" -d $DEST" + else + IPLINE1+=" -s $DEST" + fi + fi + if [ "$PORT" != 0 ]; then + IPLINE2+=" --dport $PORT" + fi + IPLINE2+=" -j $ACTION" + # IPv6? + if ! [[ $DEST =~ $V4 ]]; then + if [ "$PORT" = 0 ]; then + ip6tables $IPLINE1 $IPLINE2 + else + ip6tables $IPLINE1 -p tcp $IPLINE2 + ip6tables $IPLINE1 -p udp $IPLINE2 + fi + fi + # IPv4 + if ! [[ $DEST =~ $V6 ]]; then + if [ "$PORT" = 0 ]; then + iptables $IPLINE1 $IPLINE2 + else + iptables $IPLINE1 -p tcp $IPLINE2 + iptables $IPLINE1 -p udp $IPLINE2 + fi + fi +done < "$RULES" + +exit 0 + diff --git a/core/modules/run-virt/data/opt/openslx/vmchooser/vmchooser-run_virt b/core/modules/run-virt/data/opt/openslx/vmchooser/vmchooser-run_virt new file mode 100755 index 00000000..b945cca2 --- /dev/null +++ b/core/modules/run-virt/data/opt/openslx/vmchooser/vmchooser-run_virt @@ -0,0 +1,119 @@ +#!/bin/bash +# Full bash required +# ----------------------------------------------------------------------------- +# Copyright (c) 2007..2010 - RZ Uni FR +# Copyright (c) 2007..2013 - OpenSLX GmbH +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# run-virt.sh +# - This is the generic wrapper for the several virtualization solutions. +# The idea is to setup a set of variables used by at least two different +# tools and then include the specific plugin which configures the speci- +# fied virtualization tool. +################################################################################ + +declare -rg RUNVIRTINCLUDEDIR=/opt/openslx/vmchooser/run-virt-includes +declare -rg xmlfile="$1" + +# Functions needed by vmchooser-run_virt (writelog(), cleanexit(), rv_clean_string()) +if ! source "${RUNVIRTINCLUDEDIR}/vmchooser_runvirt_functions.inc"; then + slxlog "run-virt" "Could not source ${RUNVIRTINCLUDEDIR}/vmchooser_runvirt_functions.inc" + exit 1 +fi + +trap 'trap "" SIGINT SIGTERM; cleanexit' SIGINT SIGTERM + +# Define default dirs / get configs +source "${RUNVIRTINCLUDEDIR}/set_runvirt_variables.inc" + +# Read needed variables from XML file +source "${RUNVIRTINCLUDEDIR}/get_xml_file_variables.inc" + +# Download meta data from server (e.g. vmx for vmware) +source "${RUNVIRTINCLUDEDIR}/download_vm_metadata.inc" + +if ! isset IMGUUID TMPCONFIG TMPDIR USER; then + slxlog "run-virt" "Internal sanity check failed: One of IMGUUID TMPCONFIG TMPDIR USER is not set." + cleanexit 1 +fi + +if [ "$LEGACY" ]; then + + # No longer supported - yay + + error_user "Legacy Mode" " +Die gewählte VM ist eine 'Legacy VM', für die unvollständige +Metadaten auf dem bwLehrpool-Server hinterlegt sind. Diese +werden nicht mehr unterstützt. Um diese VM weiterhin nutzen +zu können, muss sie mittels der bwLehrpool-Suite heruntergeladen, +einmal gebootet, und wieder hochgeladen werden. +(Bei der Gelegenheit könnten z.B. auch gleich anfallende Updates +eingespielt werden.) +" + cleanexit 1 + + # End legacy warning +fi + +# Proper meta data received - proceed normally + +# Helper that looks for virtualizer-specific include, show error to user if not found +source "${RUNVIRTINCLUDEDIR}/setup_vm_hypervisor.inc" + +# For scanning for certain usb classes +source "${RUNVIRTINCLUDEDIR}/usb_detector.inc" + +# Firewall +source "${RUNVIRTINCLUDEDIR}/setup_firewall.inc" || writelog "Could not source setup_firewall" +setup_firewall || writelog "Could not run setup_firewall" + +# Sound setup +source "${RUNVIRTINCLUDEDIR}/setup_sound.inc" + +# Declaration of hardware relatedt variables +source "${RUNVIRTINCLUDEDIR}/set_runvirt_hardware_variables.inc" + +# Start printer daemon +source "${RUNVIRTINCLUDEDIR}/setup_printer_lpd.inc" + +# Setup virtual floppy b: for windows guests with config.xml, openslx.exe etc. +source "${RUNVIRTINCLUDEDIR}/setup_virtual_floppy.inc" + +# Try to use dnbd3 to access the image, nfs/cifs fallback +source "${RUNVIRTINCLUDEDIR}/setup_image_access.inc" + +# Window manager required for handling of popups etc. +source "${RUNVIRTINCLUDEDIR}/start_windowmanager.inc" + +# Source run-virt.include of virtualizer +setup_vm_commandline + +# It should have set this variable if all went well +if [ -z "${VIRTCMD}" ]; then + error_user "Fehler beim Starten der VM-Sitzung" " +Das Start-Script für den Virtualisierer $PLUGIN_ID hat kein Kommando +zum Starten der Sitzung definiert. Kann Sitzung nicht initialisieren." + slxlog "virt-plugin-error" "run-virt.include for $PLUGIN_ID did not set VIRTCMD" + cleanexit 1 +fi + +writelog "VM command: eval ${VIRTCMD} ${VIRTCMDOPTS}" +# This will start the VM +eval ${VIRTCMD} ${VIRTCMDOPTS} + +writelog "Virtualizer exited. Bye." + +# Postrun for commands after virtualization finishes +if [ -n "${POSTRUN}" ]; then + eval ${POSTRUN} >/dev/null 2>&1 +fi + +cleanexit 0 + diff --git a/core/modules/run-virt/fwtool/main.c b/core/modules/run-virt/fwtool/main.c new file mode 100644 index 00000000..9e272384 --- /dev/null +++ b/core/modules/run-virt/fwtool/main.c @@ -0,0 +1,32 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> + +int main(int argc, char **argv) +{ + if (argc < 2) { + puts("Nee\n"); + return 1; + } + char * const nargv[] = { + "bash", + "/opt/openslx/vmchooser/scripts/set-firewall", + argv[1], + 0 + }; + char * const nenv[] = { + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/openslx/sbin:/opt/openslx/bin", + "HOME=/root", + "LC_ALL=C", + "LANG=C", + 0 + }; + + setresuid(0, 0, 0); + setregid(0, 0); + + execve("/bin/bash", nargv, nenv); +} + diff --git a/core/modules/run-virt/module.build b/core/modules/run-virt/module.build index 26762bca..56afd839 100644 --- a/core/modules/run-virt/module.build +++ b/core/modules/run-virt/module.build @@ -8,9 +8,13 @@ build () { rm -f -- "$COPYLIST" list_packet_files > "$COPYLIST" tarcopy "$(cat "$COPYLIST" | sort -u)" "${MODULE_BUILD_DIR}" + # Compile pwdaemon + mkdir -p "${MODULE_BUILD_DIR}/opt/openslx/bin" + gcc -std=gnu99 -o "${MODULE_BUILD_DIR}/opt/openslx/bin/pwdaemon" -Os "${MODULE_DIR}/pw_daemon.c" || perror "Could not compile the pwdaemon" + gcc -std=gnu99 -o "${MODULE_BUILD_DIR}/opt/openslx/bin/slxfwtool" -Os "${MODULE_DIR}/fwtool/main.c" || perror "Could not compile slxfwtool" } post_copy() { - : + chmod +s "${TARGET_BUILD_DIR}/opt/openslx/bin/slxfwtool" || perror "Could not set suid bit on slxfwtool" } diff --git a/core/modules/run-virt/module.conf b/core/modules/run-virt/module.conf index bd24ba58..cc1b34a6 100644 --- a/core/modules/run-virt/module.conf +++ b/core/modules/run-virt/module.conf @@ -1,4 +1,8 @@ REQUIRED_BINARIES=" lsusb + mcopy + pwdaemon + slxfwtool + xmlstarlet " diff --git a/core/modules/run-virt/module.conf.ubuntu b/core/modules/run-virt/module.conf.ubuntu index b285210b..b6008fa0 100644 --- a/core/modules/run-virt/module.conf.ubuntu +++ b/core/modules/run-virt/module.conf.ubuntu @@ -1,7 +1,11 @@ REQUIRED_INSTALLED_PACKAGES=" usbutils + mtools + xmlstarlet " REQUIRED_CONTENT_PACKAGES=" usbutils + mtools + xmlstarlet " diff --git a/core/modules/run-virt/pw_daemon.c b/core/modules/run-virt/pw_daemon.c new file mode 100644 index 00000000..768a5b00 --- /dev/null +++ b/core/modules/run-virt/pw_daemon.c @@ -0,0 +1,315 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <linux/random.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> +#include <fcntl.h> +#include <arpa/inet.h> +#include <sys/socket.h> +#include <sys/prctl.h> +#include <sys/un.h> + +static const ssize_t KEYLEN = 16; + +static pid_t udpPid = -1; +static char *username = NULL; +static uint8_t *passwordEnc = NULL; +static size_t passwordLen = 0; +static uint8_t *key1 = NULL, *key2 = NULL; +static char *key1s = NULL, *key2s = NULL; + +static int mode_daemon(); +static int mode_query(const char *socketPath); +static void sig_handler(int sig); +static int setup_vars(const char *envuser, const char *envpass); +static uint8_t* keygen(); +static char* bin2hex(uint8_t* bin, size_t len); +static uint8_t* xorString(const char* inputText, const uint8_t* key); +static int init_udp(); + +int main(int argc, char **argv) +{ + if (argc > 1 && strcmp(argv[1], "--daemon") == 0) { + return mode_daemon(); + } else if (argc > 2 && strcmp(argv[1], "--query") == 0) { + return mode_query(argv[2]); + } + fprintf(stderr, "Invalid call. Use --daemon or --query\n"); + return 1; +} + +static int mode_query(const char *socketPath) +{ + int fd; + struct sockaddr_un remote; + struct timeval tv; + char buffer[200]; + ssize_t ret; + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd == -1) { + perror("Cannot create unix socket for connecting"); + return 1; + } + memset(&remote, 0, sizeof(remote)); + remote.sun_family = AF_UNIX; + strncpy(remote.sun_path, socketPath, sizeof(remote.sun_path)-1); + tv.tv_sec = 2; + tv.tv_usec = 0; + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + if (connect(fd, (struct sockaddr*)&remote, sizeof(remote)) == -1) { + perror("Cannot connect to pw daemon"); + return 1; + } + if (write(fd, "GET", 3) == -1) { + perror("Writing to pw daemon failed"); + return 1; + } + ret = read(fd, buffer, sizeof(buffer)-1); + if (ret == -1) { + perror("Reading from pw daemon failed"); + return 1; + } + if (ret < 1 || (size_t)ret > sizeof(buffer)-1) { + fprintf(stderr, "Reply from pw daemon has invalid length\n"); + return 1; + } + if (buffer[ret-1] != '\n') { + fprintf(stderr, "Corrupted reply received from pw daemon\n"); + return 1; + } + buffer[ret] = '\0'; + printf("%s", buffer); + return 0; +} + +static int mode_daemon() +{ + int listenFd, udpPort = -1; + struct sockaddr_un addr; + struct sigaction sig; + const char *envuser = getenv("USERNAME"); + const char *envpass = getenv("PASSWORD"); + const char *pwsocket = getenv("PWSOCKET"); + memset(&addr, 0, sizeof(addr)); + memset(&sig, 0, sizeof(sig)); + if (envuser == NULL) { + fprintf(stderr, "USERNAME not set\n"); + return 1; + } + if (envpass == NULL) { + fprintf(stderr, "PASSWORD not set\n"); + return 1; + } + if (pwsocket == NULL) { + fprintf(stderr, "PWSOCKET not set\n"); + return 1; + } + if (setup_vars(envuser, envpass) == -1) { + fprintf(stderr, "Error setting up variables\n"); + return 1; + } + listenFd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (listenFd == -1) { + perror("Could not create unix socket"); + return 1; + } + // Change permissions before bind, so it will be created with + // the right ones right away + if (fchmod(listenFd, S_IRUSR | S_IWUSR) == -1) { + perror("Cannot set permissions on socket fd prior to binding"); + return 1; + } + remove(pwsocket); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, pwsocket, sizeof(addr.sun_path)-1); + if (bind(listenFd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { + perror("Could not bind unix socket"); + return 1; + } + if (listen(listenFd, 10) == -1) { + perror("Cannot listen on unix socket"); + return 1; + } + // Mainloop + sig.sa_handler = &sig_handler; + sigaction(SIGCHLD, &sig, NULL); + for (;;) { + struct sockaddr_un remote; + socklen_t len = sizeof(remote); + int fd = accept(listenFd, (struct sockaddr*)&remote, &len); + if (fd != -1) { + if (udpPort == -1) { + udpPort = init_udp(); + } + // Success, handle client + pid_t child = fork(); + if (child == 0) { + // This is the child + ssize_t ret; + char buffer[200]; + ret = read(fd, buffer, sizeof(buffer)); + if (ret >= 3 && strncmp(buffer, "GET", 3) == 0) { + snprintf(buffer, sizeof(buffer), "%d\t%s\t%s\t%s\n", udpPort, key1s, key2s, username); + ret = write(fd, buffer, strlen(buffer)); + } + close(fd); + return 0; + } else { + // Parent, close child fd + close(fd); + } + } else { + // Error? + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR || errno == ECONNABORTED) + continue; + perror("Fatal accept error, bailing out"); + return 1; + } + } + return 0; +} + +static void sig_handler(int sig) +{ + pid_t p; + int status = sig; // Mute unused warning + while ((p = waitpid(-1, &status, WNOHANG)) > 0) { + if (p == udpPid) { + fprintf(stderr, "UDP listener died!\n"); + exit(1); + } + } +} + +static int setup_vars(const char *envuser, const char *envpass) +{ + srand((unsigned int)getpid() ^ (unsigned int)time(NULL)); + key1 = keygen(); + key2 = keygen(); + key1s = bin2hex(key1, (size_t)KEYLEN); + key2s = bin2hex(key2, (size_t)KEYLEN); + username = strdup(envuser); + passwordEnc = xorString(envpass, key2); + passwordLen = strlen(envpass) + 2; // +2 for 2byte length prefix + if (key1s == NULL || key2s == NULL || username == NULL || passwordEnc == NULL) { + return -1; + } + return 0; +} + +static uint8_t* keygen() +{ + ssize_t done = 0, ret; + uint8_t *key = malloc(KEYLEN); + int entropy; + int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); + if (fd != -1) { + if (ioctl(fd, RNDGETENTCNT, &entropy) == 0 && entropy > 0) { //Make sure we opened a random device + while (done < KEYLEN) { + ret = read(fd, key + done, (size_t)(KEYLEN - done)); + if (ret == -1) { + if (errno == EINTR) + continue; + break; + } + if (ret == 0) + break; + done += ret; + } + } + close(fd); + fprintf(stderr, "Got %d bytes from urandom\n", (int)done); + } + while (done < KEYLEN) { + key[done++] = (char)(rand() & 0xff); + } + return key; +} + +static uint8_t* xorString(const char* inputText, const uint8_t* key) +{ + uint8_t *text = (uint8_t*)inputText; + size_t len = strlen(inputText); + size_t i; + uint8_t *retval = malloc(len + 2); + uint8_t *ptr = retval + 2; + retval[0] = (uint8_t)(len & 0xff00) >> 8; + retval[1] = (uint8_t)(len & 0xff); + for (i = 0; i < len; ++i) { + ptr[i] = text[i] ^ key[i % KEYLEN]; + } + return retval; +} + +static char* bin2hex(uint8_t* bin, size_t len) +{ + static const char hexconvtab[] = "0123456789abcdef"; + char *retval = malloc(len * 2 + 1); + size_t i; + for (i = 0; i < len; ++i) { + retval[i*2] = hexconvtab[bin[i] >> 4]; + retval[i*2+1] = hexconvtab[bin[i] & 0xf]; + } + retval[i*2] = '\0'; + return retval; +} + +static int init_udp() +{ + uint16_t port = 0; + int fd; + int tries = 0; + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) { + perror("Cannot create udp socket"); + return -1; + } + for (;;) { + port = (uint16_t)(40000 + rand() % 20000); + struct sockaddr_in local; + local.sin_family = AF_INET; + local.sin_port = htons((uint16_t)port); + local.sin_addr.s_addr = INADDR_ANY; + if (bind(fd, (struct sockaddr*)&local, sizeof(local)) == -1) { + if (++tries > 100) { + perror("Cannot bind udp socket"); + close(fd); + return -1; + } + continue; + } + break; + } + udpPid = fork(); + if (udpPid == -1) { + perror("Forking udp listener failed"); + close(fd); + return -1; + } + if (udpPid != 0) { + close(fd); + return port; + } + // Child + prctl(PR_SET_PDEATHSIG, SIGTERM); + for (;;) { + struct sockaddr_in remote; + socklen_t remoteLen = sizeof(remote); + uint8_t buffer[KEYLEN]; + ssize_t ret = recvfrom(fd, buffer, KEYLEN, 0, (struct sockaddr*)&remote, &remoteLen); + if (ret == KEYLEN && memcmp(key1, buffer, KEYLEN) == 0) { + if (sendto(fd, passwordEnc, passwordLen, 0, (struct sockaddr*)&remote, sizeof(remote)) == -1) { + perror("Could not send password to remote peer"); + } + } + } +} + diff --git a/core/modules/run-virt/winres/compile b/core/modules/run-virt/winres/compile new file mode 100755 index 00000000..da2f59eb --- /dev/null +++ b/core/modules/run-virt/winres/compile @@ -0,0 +1,13 @@ +#!/bin/sh + +rm -- winres.exe +i686-w64-mingw32-windres -i winres.rc -o resource.res -O coff +i686-w64-mingw32-gcc -Wall -Wextra -pedantic -Wno-unused-parameter -flto -std=c99 -Os -Wl,--subsystem,windows -o winres.exe winres.c resource.res -lole32 -luuid -lgdi32 -lws2_32 -lshell32 -lmpr -lshlwapi +rm -- resource.res +if strip winres.exe; then + echo "Successfully created winres.exe" + echo "It has NOT been moved to data/.../openslx.exe" +else + echo "FAIL FAIL FAIL no EXE generated!" +fi + diff --git a/core/modules/run-virt/winres/winres.c b/core/modules/run-virt/winres/winres.c new file mode 100644 index 00000000..44654161 --- /dev/null +++ b/core/modules/run-virt/winres/winres.c @@ -0,0 +1,1166 @@ +#define NTDDI_VERSION NTDDI_VISTA +#define WINVER 0x0602 +#define _WIN32_WINNT 0x0602 +#define WIN32_LEAN_AND_MEAN +#define _UNICODE +#define UNICODE +#define NO_SHLWAPI_STRFCNS +#include <windows.h> +#include <winsock2.h> +#include <winnetwk.h> +#include <mmdeviceapi.h> +#include <endpointvolume.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <initguid.h> +#include <stdint.h> +#include <stdarg.h> +#include <wchar.h> +#include <time.h> +#include <shlobj.h> +#include <shlguid.h> +#include <strsafe.h> +#include <tlhelp32.h> +#include <shlwapi.h> +#include <shellapi.h> + +DEFINE_GUID(ID_IAudioEndpointVolume, 0x5CDF2C82, 0x841E, 0x4546, 0x97, 0x22, 0x0C, 0xF7, 0x40, 0x78, 0x22, 0x9A); +DEFINE_GUID(ID_IMMDeviceEnumerator, 0xa95664d2, 0x9614, 0x4f35, 0xa7,0x46, 0xde,0x8d,0xb6,0x36,0x17,0xe6); +DEFINE_GUID(ID_MMDeviceEnumerator, 0xBCDE0395, 0xE52F, 0x467C, 0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E); + +#define WM_SOCKDATA (WM_APP+1) + +typedef struct { + const char* path; + const char* pathIp; + const char* letter; + const char* shortcut; + const char* user; + const char* pass; + BOOL success; +} netdrive_t; + +static const ssize_t KEYLEN = 16; +#define DRIVEMAX (100) +#define LOGFILELEN (300) +#define SETTINGS_FILE "B:\\OPENSLX.INI" +#define SETTINGS_FILE_W L"B:\\OPENSLX.INI" + +static BOOL _debug = FALSE; + +static HINSTANCE hKernel32, hShell32; +static OSVERSIONINFO winVer; +static BOOL bGetShares = FALSE; +static netdrive_t drives[DRIVEMAX]; +static wchar_t desktopPath[MAX_PATH+1], tempPath[MAX_PATH+1], programsPath[MAX_PATH+1], windowsPath[MAX_PATH+1]; +static wchar_t logFile[LOGFILELEN]; +static DWORD _startTime; +#define FS_UNKNOWN (-1) +#define FS_ERROR (0) +#define FS_OK (1) +static int _folderStatus = FS_UNKNOWN; // -1 = Not handled yet, 0 = patching failed, 1 = remapped ok +#define RM_NONE (0) +#define RM_NATIVE (1) +#define RM_NATIVE_FALLBACK (2) +#define RM_VMWARE (3) +static int _remapMode = RM_NONE; +static const char* _remapHomeDrive = NULL; + +#define SCRIPTFILELEN (50) +char _scriptFile[SCRIPTFILELEN]; + +struct { + BOOL documents; + BOOL downloads; + BOOL desktop; + BOOL media; + BOOL other; +} remap; +static BOOL _createMissingRemap = FALSE; + +static void setPowerState(); +static int setResolution(); +static int muteSound(); +static int setShutdownText(); +static void readShareFile(); +static BOOL mountNetworkShares(); +static int queryPasswordDaemon(); +static BOOL fileExists(wchar_t* szPath); +static BOOL folderExists(wchar_t* szPath); +static void patchUserPaths(wchar_t *letter); +static void remapViaSharedFolder(); + +static HRESULT createFolderShortcut(wchar_t* sTargetfile, wchar_t* sLinkfile, wchar_t* comment); + +static void alog(const char *fmt, ...) +{ + FILE *f = _wfopen(logFile, L"a+"); + if (f == NULL) return; + time_t raw = time(NULL); + struct tm *tinf; + char buffer[80]; + tinf = localtime(&raw); + strftime(buffer, 80, "%I:%M:%S ", tinf); + fputs(buffer, f); + va_list args; + va_start(args, fmt); + vfprintf(f, fmt, args); + va_end(args); + fputc('\n', f); + fclose(f); +} + +static void wlog(const wchar_t *fmt, ...) +{ + wchar_t wbuffer[1000]; + char abuffer[1000]; + va_list args; + + FILE *f = _wfopen(logFile, L"a+"); + if (f == NULL) return; + time_t raw = time(NULL); + struct tm *tinf; + tinf = localtime(&raw); + strftime(abuffer, 1000, "%I:%M:%S ", tinf); + fputs(abuffer, f); + + va_start(args, fmt); + StringCchVPrintfW(wbuffer, 1000, fmt, args); + va_end(args); + if (WideCharToMultiByte(CP_UTF8, 0, wbuffer, -1, abuffer, 1000, NULL, NULL) == 0) { + snprintf(abuffer, 1000, "Cannot wlog: widechar to utf8 failed."); + } + fputs(abuffer, f); + fputc('\n', f); + fclose(f); +} + +static void CALLBACK resetShutdown(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) +{ + static BOOL bInProc = FALSE; + if (!bInProc) { + bInProc = TRUE; + setShutdownText(); + bInProc = FALSE; + } +} + +static void CALLBACK tmrResolution(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) +{ + static BOOL bInProc = FALSE; + if (!bInProc) { + bInProc = TRUE; + if (setResolution() == 0) { + KillTimer(hWnd, idEvent); + } + bInProc = FALSE; + } +} + +static void CALLBACK setupNetworkDrives(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) +{ + static BOOL bInProc = FALSE; + static int fails = 0; + if (bInProc) + return; + bInProc = TRUE; + if (!bGetShares && (_remapMode == RM_NATIVE_FALLBACK || _remapMode == RM_VMWARE)) { + remapViaSharedFolder(); + } else { + int ret = queryPasswordDaemon(); + if (ret != 0) { + if (++fails < 10) + goto exit_func; + alog("queryPasswordDaemon returned %d", ret); + } else { + if (!mountNetworkShares()) { + if (GetTickCount() - _startTime < 30000 && ++fails < 15) + goto exit_func; + } + } + // Finished successfully or failed completely + if (_folderStatus != FS_OK && (_remapMode == RM_NATIVE_FALLBACK || _remapMode == RM_VMWARE)) { + remapViaSharedFolder(); + } + } + KillTimer(hWnd, idEvent); + if (_remapMode != RM_NONE) { + if (_folderStatus == FS_ERROR) { + MessageBoxA(NULL, "Fehler beim Einbinden des Home-Verzeichnisses. Bitte nichts Wichtiges in der VM speichern, sondern z.B. einen USB-Stick verwenden.", "Warnung", MB_ICONERROR); + } else if (_folderStatus == FS_UNKNOWN) { + MessageBoxA(NULL, "Kein Home-Verzeichnis gefunden. Bitte nichts Wichtiges in der VM speichern, sondern z.B. einen USB-Stick verwenden.", "Warnung", MB_ICONERROR); + } + } + return; +exit_func: + bInProc = FALSE; +} + +typedef HRESULT (*GFPTYPE)(HWND, int, HANDLE, DWORD, wchar_t*); +typedef HRESULT (*GSFTYPE)(HWND, int, ITEMIDLIST**); +typedef BOOL (*ID2PTYPE)(const ITEMIDLIST*, wchar_t*); + +/** + * Load given path (CSIDL). Store in default (must be allocated to hold at least MAX_PATH+1 chars). + * If it could not be retrieved by CSIDL and envName is not NULL, it will be read from the + * environment if possible. + * fallback will be used if everything else fails. + * fallback can be NULL, in which case the fallback is empty. + */ +static void loadPath(wchar_t *dest, int csidl, wchar_t *envName, wchar_t *fallback) +{ + if (hShell32 != NULL) { + GFPTYPE aGetFolderPath = (GFPTYPE)GetProcAddress(hShell32, "SHGetFolderPathW"); + if (aGetFolderPath != NULL) { + if ((aGetFolderPath)(HWND_DESKTOP, csidl, NULL, SHGFP_TYPE_CURRENT, dest) == S_OK) + return; + } + // Fallback + GSFTYPE aGetSpecialFolder = (GSFTYPE)GetProcAddress(hShell32, "SHGetSpecialFolderLocation"); + ID2PTYPE aPathToId = (ID2PTYPE)GetProcAddress(hShell32, "SHGetPathFromIDListW"); + if (aGetSpecialFolder != NULL && aPathToId != NULL) { + ITEMIDLIST *list = NULL; + HRESULT ret1 = (aGetSpecialFolder)(HWND_DESKTOP, csidl, &list); + BOOL ret2 = FALSE; + if (ret1 == 0) { + ret2 = (aPathToId)(list, dest); + } + if (list != NULL) { + CoTaskMemFree(list); + } + if (ret2) + return; + } + } + if (envName != NULL) { + // Fallback + DWORD ret = GetEnvironmentVariableW(envName, dest, MAX_PATH+1); + if (ret > 0 && ret <= MAX_PATH) + return; + } + if (fallback != NULL) { + StringCchPrintfW(programsPath, MAX_PATH+1, fallback); + return; + } + *dest = '\0'; +} + +static void loadPaths() +{ + // Determine a couple of default directories + // dest, id, env, fallback + loadPath(programsPath, CSIDL_PROGRAM_FILES, L"ProgramFiles", L"C:\\Program Files"); + loadPath(windowsPath, CSIDL_WINDOWS, L"windir", L"C:\\WINDOWS"); + loadPath(desktopPath, CSIDL_DESKTOPDIRECTORY, NULL, NULL); + if (GetTempPathW(MAX_PATH+1, tempPath) == 0) { + DWORD ret = GetEnvironmentVariableW(L"TEMP", tempPath, MAX_PATH+1); + if (ret == 0 || ret > MAX_PATH) { + tempPath[0] = 0; + } + } + //wlog(L"Programs: %s, Windows: %s, Desktop: %s, Temp: %s", programsPath, windowsPath, desktopPath, tempPath); + StringCchPrintfW(logFile, LOGFILELEN, L"%s\\%s", desktopPath, L"openslx.log"); + FILE *tfh = _wfopen(logFile, L"a+"); + if (tfh == NULL) { + StringCchPrintfW(logFile, LOGFILELEN, L"%s\\%s", tempPath, L"openslx.log"); + tfh = _wfopen(logFile, L"a+"); + } + if (tfh != NULL) { + fseek(tfh, 0, SEEK_END); + long pos = ftell(tfh); + fclose(tfh); + if (pos < 3) { + _wremove(logFile); + } + } + // Read settings from ini file + remap.documents = GetPrivateProfileIntA("remap", "documents", 1, SETTINGS_FILE) != 0; + remap.downloads = GetPrivateProfileIntA("remap", "downloads", 1, SETTINGS_FILE) != 0; + remap.desktop = GetPrivateProfileIntA("remap", "desktop", 0, SETTINGS_FILE) != 0; + remap.media = GetPrivateProfileIntA("remap", "media", 1, SETTINGS_FILE) != 0; + remap.other = GetPrivateProfileIntA("remap", "other", 0, SETTINGS_FILE) != 0; + _createMissingRemap = GetPrivateProfileIntA("openslx", "createMissingRemap", 1, SETTINGS_FILE) != 0; + _remapMode = GetPrivateProfileIntA("openslx", "remapMode", RM_NATIVE_FALLBACK, SETTINGS_FILE); + if (_remapMode == RM_NONE) { + _folderStatus = FS_OK; + } + char buffer[10]; + GetPrivateProfileStringA("openslx", "homeDrive", "H:", buffer, sizeof(buffer), SETTINGS_FILE); + buffer[0] = toupper(buffer[0]); + buffer[1] = ':'; + buffer[2] = '\0'; + _remapHomeDrive = strdup(buffer); + // Get extension for autorun script + GetPrivateProfileStringA("openslx", "scriptExt", "", buffer, sizeof(buffer), SETTINGS_FILE); + StringCchPrintfA(_scriptFile, SCRIPTFILELEN, "B:\\runscript%s", buffer); +} + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) +{ + hKernel32 = LoadLibraryW(L"kernel32.dll"); + if (hKernel32 == NULL) { + alog("Cannot load kernel32.dll"); + } + hShell32 = LoadLibraryW(L"shell32.dll"); + if (hShell32 == NULL) { + alog("Cannot load shell32.dll"); + } + winVer.dwOSVersionInfoSize = sizeof(winVer); + BOOL retVer = GetVersionEx(&winVer); + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + _startTime = GetTickCount(); + loadPaths(); + if (lpCmdLine != NULL && strstr(lpCmdLine, "debug") != NULL) { + _debug = TRUE; + alog("Windows Version %d.%d", (int)winVer.dwMajorVersion, (int)winVer.dwMinorVersion); + } + // Mute sound by default + if (retVer && winVer.dwMajorVersion >= 6) + muteSound(); + // Disable screen saver as it might give the false impression that the session is securely locked + SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, FALSE, NULL, 0); + // Same with standby + setPowerState(); + // Any network shares to mount? + readShareFile(); + if (bGetShares || _remapMode != RM_NONE) { + UINT_PTR tRet = SetTimer(NULL, 0, 1550, (TIMERPROC)&setupNetworkDrives); + if (tRet == 0) { + alog("Could not create timer for mounting network shares: %d", (int)GetLastError()); + } + } + // Shutdown button label + if (retVer && winVer.dwMajorVersion == 6 && winVer.dwMinorVersion == 1) { + // Only on Windows 7 + // Repeatedly set caption + UINT_PTR tRet = SetTimer(NULL, 0, 5230, (TIMERPROC)&resetShutdown); + if (tRet == 0) { + alog("Could not create timer for shutdown button: %d", (int)GetLastError()); + } + } + // Resolution + if (fileExists(SETTINGS_FILE_W) && setResolution() != 0) { + UINT_PTR tRet = SetTimer(NULL, 0, 3111, (TIMERPROC)&tmrResolution); + if (tRet == 0) { + alog("Could not create timer for resolution setting: %d", (int)GetLastError()); + } + } + // Runscript (if any) + if (PathFileExistsA(_scriptFile)) { + ShellExecuteA(NULL, "open", _scriptFile, NULL, "B:\\", SW_SHOWNORMAL); + } + // Message pump + MSG Msg; + while(GetMessage(&Msg, NULL, 0, 0) > 0) { + TranslateMessage(&Msg); + DispatchMessage(&Msg); + } + FreeLibrary(hKernel32); + FreeLibrary(hShell32); + return 0; +} + +static BOOL fileExists(wchar_t* szPath) +{ + DWORD dwAttrib = GetFileAttributesW(szPath); + return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == 0); +} + +static BOOL folderExists(wchar_t* szPath) +{ + DWORD dwAttrib = GetFileAttributesW(szPath); + return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) != 0); +} + +static int execute(wchar_t *path, wchar_t *arguments) +{ + STARTUPINFOW si; + PROCESS_INFORMATION pi; + ZeroMemory(&si, sizeof(si)); + ZeroMemory(&pi, sizeof(pi)); + si.cb = sizeof(si); + if (!CreateProcessW(path, arguments, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { + return -1; + } + while (MsgWaitForMultipleObjects(1, &pi.hProcess, FALSE, INFINITE, QS_SENDMESSAGE) == WAIT_OBJECT_0+1) { + MSG Msg; + while(PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE | PM_QS_SENDMESSAGE)) { + TranslateMessage(&Msg); + DispatchMessage(&Msg); + } + } + DWORD exitCode; + BOOL ret = GetExitCodeProcess(pi.hProcess, &exitCode); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + if (!ret) { + return -2; + } + return (int)exitCode; +} + +typedef EXECUTION_STATE (WINAPI *TETYPE)(EXECUTION_STATE); + +static void setPowerState() +{ + // Disable standby and idle-mode (this is a VM!) + if (hKernel32 == NULL || winVer.dwMajorVersion < 5 || (winVer.dwMajorVersion == 5 && winVer.dwMinorVersion < 1)) + return; + TETYPE aExecState = (TETYPE)GetProcAddress(hKernel32, "SetThreadExecutionState"); + if (aExecState == NULL) { + alog("Cannot get SetThreadExecutionState from kernel32.dll"); + return; + } + if (winVer.dwMajorVersion >= 6) { // Vista+ + (aExecState)(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED | ES_AWAYMODE_REQUIRED); + } else { // XP/2003 + (aExecState)(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED); + } +} + +static int setResolution() +{ + int ret; + static int width = 0, height = 0; + if (width == 0 && height == 0) { + // use config file in floppy + char data[200] = ""; + GetPrivateProfileStringA("openslx", "resolution", "", data, sizeof(data), SETTINGS_FILE); + char *x = strchr(data, 'x'); + if (x == NULL) { + alog("Malformed resolution in " SETTINGS_FILE ": '%s'", data); + return 0; + } + *x++ = '\0'; + width = atoi(data); + height = atoi(x); + if (width < 320 || height < 240) { + alog("Invalid resolution in " SETTINGS_FILE ": '%s' (parsed width=%d, height=%d)", data, width, height); + return 0; + } + } + // Try vmware tools + static wchar_t path[MAX_PATH] = L""; + if (path[0] == 0) { + StringCchPrintfW(path, MAX_PATH, L"%s\\VMware\\VMware Tools\\VMwareResolutionSet.exe", programsPath); + if (!fileExists(path)) { + // Strip (x86) if found and try again + wchar_t *x86 = wcsstr(path, L" (x86)"); + if (x86 != NULL) { + while ((*x86 = *(x86 + 6)) != 0) ++x86; + } + } + if (!fileExists(path)) { + char buffer[300]; + WideCharToMultiByte(CP_UTF8, 0, path, -1, buffer, 300, NULL, NULL); + alog("vmware tools not found, using winapi to set resolution (path: %s)", buffer); + } + } + if (path[0] != 0 && fileExists(path)) { + wchar_t cmdline[MAX_PATH]; + StringCchPrintfW(cmdline, MAX_PATH, L"VMwareResolutionSet.exe 0 1 , 0 0 %d %d", width, height); + int ret = execute(path, cmdline); + if (ret == -1) { + alog("VmwareRes: CreateProcess failed (%d)", (int)GetLastError()); + } else if (ret == -2) { + alog("VmwareRes: GetExitCode failed (%d)", (int)GetLastError()); + } + } + // Use WinAPI as fallback + DEVMODE mode; + int query = 1337; + memset(&mode, 0, sizeof(mode)); + mode.dmSize = sizeof(mode); + // MSDN recommends to fill the struct first by querying.... + query = EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &mode); + // Then set our own desired mode + mode.dmPelsWidth = width; + mode.dmPelsHeight = height; + mode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; + ret = ChangeDisplaySettings(&mode, CDS_GLOBAL | CDS_UPDATEREGISTRY); + if (ret != DISP_CHANGE_SUCCESSFUL) { + ret = ChangeDisplaySettings(&mode, CDS_GLOBAL); + } + if (ret != DISP_CHANGE_SUCCESSFUL) { + ret = ChangeDisplaySettings(&mode, CDS_UPDATEREGISTRY); + } + if (ret != DISP_CHANGE_SUCCESSFUL) { + ret = ChangeDisplaySettings(&mode, 0); + } + if (ret != DISP_CHANGE_SUCCESSFUL) { + static int fails = 0; + if (++fails == 5) { + alog("Fehler beim Setzen der Auflösung: %d (soll: 0) / %d ( soll: !0) - Zielaufloesung: %d * %d", + ret, query, width, height); + } + return 1; + } + return 0; +} + +static int muteSound() +{ + IMMDeviceEnumerator *deviceEnumerator = NULL; + HRESULT hr = CoCreateInstance(&ID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &ID_IMMDeviceEnumerator, (LPVOID *)&deviceEnumerator); + if (hr != S_OK) { + alog("CoCreateInstance failed. Cannot mute."); + return 1; + } + //deviceEnumerator->lpVtbl->AddRef(deviceEnumerator); + IMMDevice *defaultDevice = NULL; + hr = deviceEnumerator->lpVtbl->GetDefaultAudioEndpoint(deviceEnumerator, eRender, eConsole, &defaultDevice); + if (hr != S_OK) { + alog("GetDefaultAudioEndpoint failed. Cannot mute."); + return 2; + } + //defaultDevice->lpVtbl->AddRef(defaultDevice); + //deviceEnumerator->lpVtbl->Release(deviceEnumerator); + IAudioEndpointVolume *endpointVolume = NULL; + hr = defaultDevice->lpVtbl->Activate(defaultDevice, &ID_IAudioEndpointVolume, CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&endpointVolume); + if (hr != S_OK) { + alog("IMMDevice::Activate() failed. Cannot mute."); + return 3; + } + //endpointVolume->lpVtbl->AddRef(endpointVolume); + //defaultDevice->lpVtbl->Release(defaultDevice); + float targetVolume = 1; + endpointVolume->lpVtbl->SetMasterVolumeLevelScalar(endpointVolume, targetVolume, NULL); + endpointVolume->lpVtbl->SetMute(endpointVolume, TRUE, NULL); + //endpointVolume->lpVtbl->Release(endpointVolume); + //CoUninitialize(); + return 0; +} + +static int setShutdownText() +{ + HWND hMenu = FindWindowA("DV2ControlHost", NULL); + if (hMenu == NULL) return 1; // TODO: Enum all of them + HWND hPane = FindWindowExA(hMenu, NULL, "DesktopLogoffPane", NULL); + if (hMenu == NULL) return 2; + HWND hButton = FindWindowExA(hPane, NULL, "Button", NULL); + if (hButton == NULL) return 3; + if (SendMessageA(hButton, WM_SETTEXT, 0, (LPARAM)"Abmelden") != TRUE) return 4; + return 0; +} + +static char *shost = NULL, *sport = NULL, *suser = NULL, *spass = NULL; +static uint8_t *bkey1 = NULL, *bkey2 = NULL; + +static char* xorString(const uint8_t* text, int len, const uint8_t* key); +static int getbin(int x); +static uint8_t* hex2bin(char *szHexString); + +static char* getToken(char **ptr, BOOL doDup) +{ + if (*ptr == NULL || **ptr == '\0') return NULL; + char *dest = *ptr; + while (**ptr != '\0') { + if (**ptr == '\n' || **ptr == '\r' || **ptr == '\t') { + *(*ptr)++ = '\0'; + break; + } + (*ptr)++; + } + if (doDup) { + dest = strdup(dest); + } + return dest; +} + +const char* uncReplaceFqdnByIp(const char* path) +{ + if (path == NULL || path[0] != '\\' || path[1] != '\\') + return NULL; + const char *host = path + 2; + const char *rest = strchr(host, '\\'); + if (rest == NULL || rest - host >= 200) + return NULL; + char name[201]; + strncpy(name, host, rest - host); + name[rest-host] = '\0'; + if (_debug) { + alog("Trying to resolve '%s'...", name); + } + struct hostent *remote = gethostbyname(name); + if (remote == NULL || remote->h_addrtype != AF_INET || remote->h_addr_list[0] == 0) + return NULL; + struct in_addr addr; + addr.s_addr = *(u_long*)remote->h_addr_list[0]; + char *ip = inet_ntoa(addr); + if (ip == NULL) + return NULL; + size_t len = 2 /* \\ */ + strlen(ip) /* 1.2.3.4 */ + strlen(rest) /* \path\to\share */ + 1 /* nullchar */; + char *retval = malloc(len); + snprintf(retval, len, "\\\\%s%s", ip, rest); + if (_debug) { + alog("Turned '%s' into '%s'", path, retval); + } + return retval; +} + +#define FREENULL(x) do { free((void*)(x)); (x) = NULL; } while (0) + +static void readShareFile() +{ + if (bGetShares) + return; + memset(drives, 0, sizeof(drives)); + FILE *h = fopen("B:\\shares.dat", "r"); + if (h == NULL) return; + char creds[300] = "", buffer[500] = ""; + char *skey1 = NULL, *skey2 = NULL; + if (fgets(creds, sizeof(creds), h) != NULL) { + char *ptr = creds; + shost = getToken(&ptr, TRUE); + sport = getToken(&ptr, TRUE); + skey1 = getToken(&ptr, FALSE); + skey2 = getToken(&ptr, FALSE); + suser = getToken(&ptr, TRUE); + } + int idx = 0; + while (fgets(buffer, sizeof(buffer), h) != NULL && idx < DRIVEMAX) { + char *ptr = buffer; + netdrive_t *d = &drives[idx]; + d->path = getToken(&ptr, TRUE); + d->letter = getToken(&ptr, TRUE); + d->shortcut = getToken(&ptr, TRUE); + d->user = getToken(&ptr, TRUE); + d->pass = getToken(&ptr, TRUE); + if (d->path == NULL || d->path[0] == '\0') + goto drive_fail; + d->success = FALSE; + // Hack: For unknown reasons, with some servers mounting fails using a fqdn, but using the ip address instead succeeds. + d->pathIp = uncReplaceFqdnByIp(d->path); + // + idx++; + continue; +drive_fail: + FREENULL(d->path); + FREENULL(d->letter); + FREENULL(d->shortcut); + FREENULL(d->user); + FREENULL(d->pass); + } + fclose(h); + if (idx == 0) // No drives to map + return; + if (shost == NULL || sport == NULL || skey1 == NULL || skey2 == NULL || suser == NULL) // Credential stuff missing + return; + if (strlen(skey1) != KEYLEN*2 || strlen(skey2) != KEYLEN*2) // Messed up keys + return; + if (atoi(sport) < 1000 || atoi(sport) > 65535) // Invalid port + return; + bkey1 = hex2bin(skey1); + bkey2 = hex2bin(skey2); + if (bkey1 == NULL || bkey2 == NULL) + return; + bGetShares = TRUE; +} + +static void udpReceived(SOCKET sock); + +LRESULT CALLBACK slxWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (uMsg != WM_SOCKDATA) { + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } + // Socket event + int event = LOWORD(lParam); + int errorCode = HIWORD(lParam); + if (errorCode == 0 && event == FD_READ) { + udpReceived((SOCKET)wParam); + } + return 0; +} + +static int queryPasswordDaemon() +{ + // See if preconditions are met + if (!bGetShares || spass != NULL) + return 0; + static int wsaInit = 1337; + static SOCKET sock = INVALID_SOCKET; + static HWND sockWnd = NULL; + // Init socket stuff + if (wsaInit == 1337) { + WSADATA wsa; + wsaInit = WSAStartup(MAKEWORD(2, 2), &wsa); + } + if (wsaInit != 0) + return 2; + // Create window for socket events + if (sockWnd == NULL) { + sockWnd = CreateWindowA("STATIC", "OpenSLX mystery window", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL); + if (sockWnd == NULL) + return GetLastError(); + SetWindowLong(sockWnd, GWL_WNDPROC, (LONG)&slxWindowProc); + } + // Create socket + if (sock == INVALID_SOCKET) { + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock == INVALID_SOCKET) + return 3; + if (WSAAsyncSelect(sock, sockWnd, WM_SOCKDATA, FD_READ) != 0) { + alog("WSAAsyncSelect returned %d", (int)WSAGetLastError()); + } + } + SOCKADDR_IN remote; + remote.sin_family = AF_INET; + remote.sin_port = htons((u_short)atoi(sport)); + remote.sin_addr.s_addr = inet_addr(shost); + // Send out request for password + if (sendto(sock, (const char*)bkey1, KEYLEN, 0, (struct sockaddr*)&remote, sizeof(remote)) != KEYLEN) + return 4; + if (spass == NULL) + return -1; + return 0; +} + +static void udpReceived(SOCKET sock) +{ + int ret; + uint8_t buffer[200]; + ret = recv(sock, (char*)buffer, sizeof(buffer), 0); + // See if reply is valid + if (ret < 2) return; + uint16_t len = (uint16_t)(((uint16_t)buffer[0] << 8) | buffer[1]); + if (ret - 2 != len) return; + // Success + spass = xorString(buffer + 2, len, bkey2); + closesocket(sock); + mountNetworkShares(); +} + +#define BUFLEN (200) + +static DWORD mount(LPNETRESOURCEW share, LPWSTR pass, LPWSTR user) +{ + DWORD retval; + // Now try to mount + if ((pass && *pass) || (user && *user)) { + retval = WNetAddConnection2W(share, pass, user, CONNECT_TEMPORARY | CONNECT_CURRENT_MEDIA); + if (retval == NO_ERROR) { + return retval; + } + if (retval != ERROR_INVALID_PASSWORD && retval != ERROR_LOGON_FAILURE + && retval != ERROR_BAD_USERNAME && retval != ERROR_ACCESS_DENIED + && retval != ERROR_SESSION_CREDENTIAL_CONFLICT && retval != ERROR_BAD_NET_NAME) { + return retval; + } + } + static wchar_t nuser[BUFLEN] = L"\0", npass[BUFLEN] = L"\0"; + if (nuser[0] == 0 && npass[0] == 0) { + BOOL ok = TRUE; + if (suser != NULL) { + ok = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)suser, -1, (LPWSTR)nuser, BUFLEN) > 0 && ok; + } + if (spass != NULL) { + ok = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)spass, -1, (LPWSTR)npass, BUFLEN) > 0 && ok; + } + if (!ok) + return ERROR_INVALID_PARAMETER; + } + retval = WNetAddConnection2W(share, npass, nuser, CONNECT_TEMPORARY | CONNECT_CURRENT_MEDIA); + return retval; +} + +static void postSuccessfulMount(const netdrive_t *d, wchar_t *letter) +{ + if (d->shortcut != NULL && strlen(d->shortcut) != 0) { + wchar_t tmp[MAX_PATH], wShortcut[MAX_PATH], wTarget[MAX_PATH]; + MultiByteToWideChar(CP_UTF8, 0, d->shortcut, -1, tmp, MAX_PATH); + StringCchPrintfW(wShortcut, MAX_PATH, L"%s\\%s.lnk", desktopPath, tmp); + MultiByteToWideChar(CP_UTF8, 0, d->path, -1, tmp, MAX_PATH); + StringCchPrintfW(wTarget, MAX_PATH, L"\"%s\"", tmp); + DeleteFileW(wShortcut); + createFolderShortcut(wTarget, wShortcut, letter); + // Fix paths and kill explorer if it's the home directory + if (_folderStatus != FS_OK && strncmp(d->shortcut, "Home-", 5) == 0) { + BOOL isVmware = strcmp(d->path, "\\\\vmware-host\\Shared Folders\\home") == 0; + if (_remapMode == RM_NATIVE_FALLBACK + || (isVmware && _remapMode == RM_VMWARE) + || (!isVmware && _remapMode == RM_NATIVE)) { + patchUserPaths(letter); + } + } + } +} + +static BOOL mountNetworkShare(const netdrive_t *d, BOOL useIp) +{ + wchar_t path[BUFLEN] = L"", user[BUFLEN] = L"", pass[BUFLEN] = L"", letter[10] = L"", shortcut[BUFLEN] = L""; + int ok = -1; + if (useIp && d->pathIp[0] == '\0') + return FALSE; + const char *uncPath = useIp ? d->pathIp : d->path; + ok &= MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)uncPath, -1, (LPWSTR)path, BUFLEN) > 0; + if (d->letter != NULL) { + ok &= MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)d->letter, -1, (LPWSTR)letter, 10) > 0; + } + if (d->user != NULL) { + ok &= MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)d->user, -1, (LPWSTR)user, BUFLEN) > 0; + } + if (d->pass != NULL) { + ok &= MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)d->pass, -1, (LPWSTR)pass, BUFLEN) > 0; + } + if (d->shortcut != NULL) { + ok &= MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)d->shortcut, -1, (LPWSTR)shortcut, BUFLEN) > 0; + } + if (!ok || path[0] == 0) { // Convert failed/no path - return true anyways since retrying wouldn't change anything + alog("mountNetworkShare: utf8 to utf16 failed, or path empty (src: '%s')", uncPath); + return TRUE; + } + DWORD retval; + NETRESOURCEW share = { 0 }; + share.dwType = RESOURCETYPE_DISK; + share.lpLocalName = letter; + share.lpRemoteName = path; + share.lpProvider = NULL; + letter[1] = ':'; + letter[2] = 0; + letter[3] = 0; + if (letter[0] != 0) { + // Try with specific letter + // Connect defined share + retval = mount(&share, pass, user); + if (retval == NO_ERROR) { + postSuccessfulMount(d, letter); + return TRUE; + } + if (retval != ERROR_ALREADY_ASSIGNED && retval != ERROR_DEVICE_ALREADY_REMEMBERED + && retval != ERROR_CONNECTION_UNAVAIL) { + alog("mountNetworkShare: with letter failed: %d", (int)retval); + return FALSE; + } + } + // Try to find free drive letter + for (letter[0] = 'Z'; letter[0] > 'C'; --letter[0]) { + retval = mount(&share, pass, user); + if (retval == ERROR_ALREADY_ASSIGNED || retval == ERROR_DEVICE_ALREADY_REMEMBERED + || retval == ERROR_CONNECTION_UNAVAIL) + continue; + if (retval == NO_ERROR) { + postSuccessfulMount(d, letter); + return TRUE; + } + alog("mountNetworkShare: without letter failed: %d", (int)retval); + if (retval == ERROR_INVALID_PASSWORD || retval == ERROR_LOGON_FAILURE + || retval == ERROR_BAD_USERNAME || retval == ERROR_ACCESS_DENIED + || retval == ERROR_SESSION_CREDENTIAL_CONFLICT) { + return TRUE; + } + return FALSE; + } + return FALSE; +} + +static BOOL mountNetworkShares() +{ + if (!bGetShares) + return TRUE; + if (spass == NULL) + return FALSE; + int failCount = 0; + for (int i = 0; i < DRIVEMAX; ++i) { + if (drives[i].path == NULL) + break; + if (drives[i].success) + continue; + if (mountNetworkShare(&drives[i], FALSE)) { + drives[i].success = TRUE; + } else if (mountNetworkShare(&drives[i], TRUE)) { + drives[i].success = TRUE; + } else { + failCount++; + } + } + if (failCount > 0) + return FALSE; + SecureZeroMemory(spass, strlen(spass)); + return TRUE; +} + +static void remapViaSharedFolder() +{ + static const char* homeDirA = "\\\\vmware-host\\Shared Folders\\home"; // thiscase! + static const wchar_t* homeDirW = L"\\\\vmware-host\\shared folders\\home"; // lowercase! + static BOOL once = FALSE; + if (once) return; + once = TRUE; + netdrive_t d; + d.path = homeDirA; + d.letter = _remapHomeDrive; + d.shortcut = "Home-Verzeichnis"; + d.user = ""; + d.pass = ""; + d.success = FALSE; + // See if it's already mapped + wchar_t letter[5] = L"C:\\"; + char buffer[600]; + UNIVERSAL_NAME_INFOW *uni = (UNIVERSAL_NAME_INFOW*)buffer; + for (letter[0] = 'D'; letter[0] <= 'Z'; ++letter[0]) { + //wlog(L"Checking %s", letter); + DWORD len = (DWORD)sizeof(buffer); + if (NO_ERROR == WNetGetUniversalNameW(letter, UNIVERSAL_NAME_INFO_LEVEL, uni, &len)) { + _wcslwr(uni->lpUniversalName); + //wlog(L"Is %s", uni->lpUniversalName); + if (wcscmp(uni->lpUniversalName, homeDirW) == 0) { + letter[2] = '\0'; + postSuccessfulMount(&d, letter); + return; + } + } + } + // Map vmware shared folder + mountNetworkShare(&d, FALSE); +} + +static char* xorString(const uint8_t* text, int len, const uint8_t* key) +{ + int i; + uint8_t *retval = malloc(len + 1); + uint8_t *ptr = retval; + for (i = 0; i < len; ++i) { + ptr[i] = text[i] ^ key[i % KEYLEN]; + } + ptr[len] = '\0'; + return (char*)retval; +} + +static int getbin(int x) +{ + if (x >= '0' && x <= '9') + return x - '0'; + if (x >= 'A' && x <= 'F') + return x - 'A' + 10; + return x - 'a' + 10; +} + +static uint8_t* hex2bin(char *szHexString) +{ + int size = strlen(szHexString) / 2, i; + char *p = szHexString; + uint8_t *pBinary = malloc(size); + + for(i = 0; i < size; i++, p += 2) { + pBinary[i] = (uint8_t)((getbin(p[0]) << 4) | getbin(p[1])); + } + return pBinary; +} + +// Stuff for creating a simple shortcut (.lnk) + +static HRESULT createFolderShortcut(wchar_t* targetDir, wchar_t* linkFile, wchar_t* comment) +{ + HRESULT hRes; /* Returned COM result code */ + IShellLink* pShellLink; /* IShellLink object pointer */ + IPersistFile* pPersistFile; /* IPersistFile object pointer */ + + hRes = E_INVALIDARG; + if ( + (targetDir != NULL) && (wcslen(targetDir) > 0) && + (linkFile != NULL) && (wcslen(linkFile) > 0) + ) { + hRes = CoCreateInstance( + &CLSID_ShellLink, /* pre-defined CLSID of the IShellLink object */ + NULL, /* pointer to parent interface if part of aggregate */ + CLSCTX_INPROC_SERVER, /* caller and called code are in same process */ + &IID_IShellLink, /* pre-defined interface of the IShellLink object */ + (void**)&pShellLink); /* Returns a pointer to the IShellLink object */ + if (SUCCEEDED(hRes)) { + wchar_t explorer[MAX_PATH]; + StringCchPrintfW(explorer, MAX_PATH, L"\"%s\\explorer.exe\"", windowsPath); + // Set the fields in the IShellLink object + hRes = pShellLink->lpVtbl->SetPath(pShellLink, explorer); + hRes = pShellLink->lpVtbl->SetArguments(pShellLink, targetDir); + if (comment != NULL) { + hRes = pShellLink->lpVtbl->SetDescription(pShellLink, comment); + } + StringCchPrintfW(explorer, MAX_PATH, L"%s\\system32\\imageres.dll", windowsPath); + hRes = pShellLink->lpVtbl->SetIconLocation(pShellLink, explorer, 137); + + /* Use the IPersistFile object to save the shell link */ + hRes = pShellLink->lpVtbl->QueryInterface( + pShellLink, /* existing IShellLink object */ + &IID_IPersistFile, /* pre-defined interface of the IPersistFile object */ + (void**)&pPersistFile); /* returns a pointer to the IPersistFile object */ + if (SUCCEEDED(hRes)) { + hRes = pPersistFile->lpVtbl->Save(pPersistFile, linkFile, TRUE); + pPersistFile->lpVtbl->Release(pPersistFile); + } + pShellLink->lpVtbl->Release(pShellLink); + } + + } + return (hRes); +} + +// Patch user directories + +static BOOL patchRegPath(BOOL *patchOk, BOOL *anyMapped, HKEY hKey, wchar_t *letter, wchar_t *value, ...) +{ + wchar_t *folder = NULL; + wchar_t first[MAX_PATH] = {0}; + wchar_t path[MAX_PATH]; + wchar_t oldvalue[MAX_PATH]; + va_list args; + LONG ret; + DWORD type; + DWORD len; + // Let's check the path in the registry first + len = (DWORD)(sizeof(oldvalue) - sizeof(wchar_t)); + ret = RegQueryValueExW(hKey, value, NULL, &type, (BYTE*)oldvalue, &len); + if (ret == ERROR_SUCCESS && (type == REG_EXPAND_SZ || type == REG_SZ)) { + len /= 2; + oldvalue[len] = '\0'; + if (towlower(oldvalue[0]) == towlower(letter[0]) && folderExists(oldvalue)) // Same drive, folder exists, yay + return TRUE; + } + // Old registry value doesn't fit - figure out new value + va_start(args, value); + while ((folder = va_arg(args, wchar_t*)) != NULL) { + StringCchPrintfW(path, MAX_PATH, L"%s\\%s", letter, folder); + if (folderExists(path)) + break; + if (*first == 0) { + wcsncpy(first, path, MAX_PATH); + } + } + va_end(args); + if (folder != NULL) { + // Found something existing + folder = path; + } else if (!_createMissingRemap) { + // Nothing found, must not create + wlog(L"Cannot remap %s to %s: target not found!", value, first); + return FALSE; + } else { + // Nothing found, use first element of list and create it + folder = first; + CreateDirectoryW(folder, NULL); + } + _wcslwr(folder); + _wcslwr(oldvalue); + if (wcscmp(folder, oldvalue) == 0) { + // Path already in registry, don't update + return TRUE; + } + ret = RegSetValueExW(hKey, value, 0, REG_SZ, (BYTE*)folder, (wcslen(folder) + 1) * sizeof(wchar_t)); + if (ret == ERROR_SUCCESS) { + *anyMapped = TRUE; + return TRUE; + } + wlog(L"Setting reg key %s to %s failed (return value %ld)", value, folder, (long)ret); + *patchOk = FALSE; + return FALSE; +} + +typedef HANDLE (WINAPI *SNTYPE)(DWORD, DWORD); +typedef BOOL (WINAPI *P32TYPE)(HANDLE, PROCESSENTRY32W*); + +static void patchUserPaths(wchar_t *letter) +{ + LONG ret; + HKEY hKey; + BOOL patchOk = TRUE; + BOOL killOk = FALSE; + BOOL anyMapped = FALSE; + _folderStatus = FS_ERROR; + ret = RegOpenKeyExW(HKEY_CURRENT_USER, + L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders", + 0, KEY_WOW64_64KEY | KEY_READ | KEY_WRITE, &hKey); + if (ret != ERROR_SUCCESS) { + alog("Opening registry for patching of pathes failed with return code %ld", (long)ret); + return; + } + // Ha! + const BOOL win10 = winVer.dwMajorVersion >= 10; + if (remap.other) { + patchRegPath(&patchOk, &anyMapped, hKey, letter, L"{56784854-C6CB-462B-8169-88E350ACB882}", L"Contacts", L"Profile\\Contacts", L"Kontakte", NULL); + patchRegPath(&patchOk, &anyMapped, hKey, letter, L"Favorites", L"Favorites", L"Profile\\Favorites", L"Favoriten", NULL); + patchRegPath(&patchOk, &anyMapped, hKey, letter, L"{7D1D3A04-DEBB-4115-95CF-2F29DA2920DA}", L"Searches", L"Profile\\Searches", NULL); + patchRegPath(&patchOk, &anyMapped, hKey, letter, L"{BFB9D5E0-C6A9-404C-B2B2-AE6DB6AF4968}", L"Links", L"Profile\\Links", NULL); + patchRegPath(&patchOk, &anyMapped, hKey, letter, L"{4C5C32FF-BB9D-43B0-B5B4-2D72E54EAAA4}", L"Saved Games", L"SavedGames", L"Profile\\SavedGames", NULL); + } + if (remap.media) { + patchRegPath(&patchOk, &anyMapped, hKey, letter, L"My Video", L"Videos", L"My Videos", L"Eigene Videos", NULL); + patchRegPath(&patchOk, &anyMapped, hKey, letter, L"My Pictures", L"Pictures", L"My Pictures", L"Eigene Bilder", L"Bilder", NULL); + patchRegPath(&patchOk, &anyMapped, hKey, letter, L"My Music", L"Music", L"My Music", L"Eigene Musik", L"Musik", NULL); + if (win10) { + patchRegPath(&patchOk, &anyMapped, hKey, letter, L"{35286a68-3c57-41a1-bbb1-0eae73d76c95}", L"Videos", L"My Videos", L"Eigene Videos", NULL); + patchRegPath(&patchOk, &anyMapped, hKey, letter, L"{0ddd015d-b06c-45d5-8c4c-f59713854639}", L"Pictures", L"My Pictures", L"Eigene Bilder", L"Bilder", NULL); + patchRegPath(&patchOk, &anyMapped, hKey, letter, L"{a0c69a99-21c8-4671-8703-7934162fcf1d}", L"Music", L"My Music", L"Eigene Musik", L"Musik", NULL); + } + } + if (remap.downloads) { + patchRegPath(&patchOk, &anyMapped, hKey, letter, L"{374DE290-123F-4565-9164-39C4925E467B}", L"Downloads", L"Profile\\Downloads", NULL); + if (win10) { + patchRegPath(&patchOk, &anyMapped, hKey, letter, L"{7d83ee9b-2244-4e70-b1f5-5393042af1e4}", L"Downloads", L"Profile\\Downloads", NULL); + } + } + if (remap.documents) { + patchRegPath(&patchOk, &anyMapped, hKey, letter, L"Personal", L"Documents", L"Dokumente", L"My Documents", L"Eigene Dateien", NULL); + if (win10) { + patchRegPath(&patchOk, &anyMapped, hKey, letter, L"{f42ee2d3-909f-4907-8871-4c22fc0bf756}", L"Documents", L"Dokumente", L"My Documents", L"Eigene Dateien", NULL); + } + } + if (remap.desktop) { + patchRegPath(&patchOk, &anyMapped, hKey, letter, L"Desktop", L"Windows Desktop", L"Desktop", L"Arbeitsfl\u00E4che", NULL); + if (win10) { + patchRegPath(&patchOk, &anyMapped, hKey, letter, L"{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}", L"Windows Desktop", L"Desktop", L"Arbeitsfl\u00E4che", NULL); + } + } + RegCloseKey(hKey); + if (!anyMapped) { + _folderStatus = FS_OK; + return; + } + // Kill explorer + // Late binding + if (hKernel32 == NULL || winVer.dwMajorVersion < 5 || (winVer.dwMajorVersion == 5 && winVer.dwMinorVersion < 1)) { + return; + } + SNTYPE aCreateSnapshot; + P32TYPE aProcessFirst, aProcessNext; + aCreateSnapshot = (SNTYPE)GetProcAddress(hKernel32, "CreateToolhelp32Snapshot"); + if (aCreateSnapshot == NULL) { + alog("No CreateToolhelp32Snapshot in kernel.dll"); + return; + } + aProcessFirst = (P32TYPE)GetProcAddress(hKernel32, "Process32FirstW"); + if (aProcessFirst == NULL) { + alog("No Process32FirstW in kernel.dll"); + return; + } + aProcessNext = (P32TYPE)GetProcAddress(hKernel32, "Process32NextW"); + if (aProcessNext == NULL) { + alog("No Process32NextW in kernel.dll"); + return; + } + PROCESSENTRY32W entry; + entry.dwSize = sizeof(PROCESSENTRY32W); + HANDLE snapshot = (aCreateSnapshot)(TH32CS_SNAPPROCESS, 0); + if (snapshot != INVALID_HANDLE_VALUE && (aProcessFirst)(snapshot, &entry)) { + do { + if (_wcsicmp(entry.szExeFile, L"explorer.exe") == 0) { + HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID); + if (hProcess == NULL) { + alog("Cannot OpenProcess explorer.exe: Remapped paths will not work properly (GetLastError=%d)", (int)GetLastError()); + } else { + if (TerminateProcess(hProcess, 23)) { + killOk = TRUE; + } + CloseHandle(hProcess); + } + } + } while ((aProcessNext)(snapshot, &entry)); + } else { + alog("Could not get process list"); + } + CloseHandle(snapshot); + if (patchOk && killOk) { + _folderStatus = FS_OK; + } +} + diff --git a/core/modules/run-virt/winres/winres.manifest b/core/modules/run-virt/winres/winres.manifest new file mode 100644 index 00000000..a4ffc98e --- /dev/null +++ b/core/modules/run-virt/winres/winres.manifest @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1"> + <assemblyIdentity type="win32" + name="org.openslx.windows.winres" + version="4.0.0.0" + processorArchitecture="x86" + publicKeyToken="0000000000000000" + /> + <description>Sausageface</description> + <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> + <security> + <requestedPrivileges> + <requestedExecutionLevel + level="asInvoker" + uiAccess="false" + /> + </requestedPrivileges> + </security> + </trustInfo> + <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> + <application> + <!-- Windows 10 --> + <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> + <!-- Windows 8.1 --> + <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> + <!-- Windows Vista --> + <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> + <!-- Windows 7 --> + <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> + <!-- Windows 8 --> + <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> + </application> + </compatibility> +</assembly> diff --git a/core/modules/run-virt/winres/winres.rc b/core/modules/run-virt/winres/winres.rc new file mode 100644 index 00000000..a9faf9a0 --- /dev/null +++ b/core/modules/run-virt/winres/winres.rc @@ -0,0 +1,4 @@ +#include <windows.h> + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "winres.manifest" + |