VMWare Horizon Client - Dedicated Thin Client
With the cost of zero clients being as much as a standard computer, it made more sense to purchase commodity hardware and build from scratch.
This is my endeavor into building a poor man's zero client.
Requirements
- Audio, Video, and user input must work.
- USB Redirection must work for webcam usage.
- The only window the user's see is the Horizon Client.
- If the client is closed, it must auto-open again.
Install Script
CentOS6 32bit
WIP[1]
yum -y install epel-release yum -y update yum install -y chrony wget yum-cron fail2ban bind-utils htop bzip2 nano openbox xorg-x11-* gnome-terminal alsa-utils libXScrnSaver useradd publicuser sed -i 's|exec\ /sbin/mingetty\ \$TTY|exec\ /sbin/mingetty\ --autologin\ publicuser\ \$TTY|' /etc/init/tty.conf
CentOS7
WIP : Designed to run over SSH.
- Updated with latest display script, amixer fixes
setenforce 0 sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config yum -y install epel-release yum clean all yum makecache yum -y update yum -y install alsa-plugins-speex alsa-plugins-pulseaudio wget chrony wget yum-cron firewalld fail2ban oddjob oddjob-mkhomedir sssd samba-common realmd bind-utils htop bzip2 nano libao libv4l speex setroubleshoot setools openbox xorg-x11-* gnome-terminal alsa-utils pcsc-lite-libs-1.8.8-6.el7 glibmm24 gstreamer-plugins-base-0.10.36-10.el7 libpng12 libXScrnSaver useradd publicuser cp /usr/lib/systemd/system/getty@.service ./ sed -i -e "s/\/sbin\/agetty/\0 --autologin publicuser/" /etc/systemd/system/getty.target.wants/getty@tty1.service cat << EOF >>/home/publicuser/.bash_profile if [[ -z \$DISPLAY ]] && [[ \$(tty) = /dev/tty1 ]]; then # Check if we have a display and we're on TTY1 startx else echo "Please contact the help desk and provide the following" echo "IP : " echo "Hostname : $hostname" fi EOF cat << EOF > /home/publicuser/.xinitrc /home/publicuser/display.sh exec openbox EOF cat << EOF > /home/publicuser/display.sh #!/bin/bash displaycheck () { if grep -qi \$1 <<<\$line; then if grep -qi \$1\1 <<<\$line; then if [ -z "\$d1" ]; then d1=\$line elif [ -z "\$d2" ]; then d2=\$line fi fi # if grep -qi \$1-2 <<<\$line; then # if [ -z "\$d1" ]; then # d1=\$line # elif [ -z "\$d2" ]; then # d2=\$line # fi # fi fi } xrandr > /tmp/xout sed '/disconnected/ d; /LVDS/ d' /tmp/xout | grep connected | awk '{print \$1}' > /tmp/xout2 while read line do displaycheck VGA displaycheck DVI displaycheck HDMI displaycheck DP done < /tmp/xout2 if [ -z $d1 ] && [ -z $d2 ]; then exit 0 fi xrandr --output $d1 --left-of $d2 --output LVDS-1 --off EOF chmod +x /home/publicuser/display.sh cat << EOF > /home/publicuser/vdi_keep_alive.sh #!/bin/bash pidview=\$(/usr/sbin/pidof vmware-view) if [ "\$pidview" = "" ]; then killall vmware-view killall vmware-usbarbitrator killall vmware-view-usbd /usr/bin/amixer set Master 100% /usr/bin/amixer set Capture 60% DISPLAY=\$(who | grep -E "publicuser pts\\/[0-9]" | grep -Eo "\\(:[0-9][.]?[0-9]?\\)" | sed 's/(//; s/)//') vmware-view -s <connection server> -d csp --allmonitors else echo "keep running" >> /dev/null fi EOF chmod 755 /home/publicuser/vdi_keep_alive.sh echo -e "00 01 * * * rm -rf /tmp/*\n01 01 * * * fstrim -a\n05 01 * * * reboot -h now" | crontab su publicuser -c 'echo "* * * * * /root/vdi_keep_alive.sh" | crontab' ln -s /usr/lib64/libudev.so.1.6.2 /usr/lib64/libudev.so.0 ln -s /usr/lib64/libffi.so.6 /usr/lib64/libffi.so.5 wget https://download3.vmware.com/software/view/viewclients/CART19FQ2/VMware-Horizon-Client-4.8.0-8518891.x64.bundle chmod +x VMware-Horizon-Client-4.8.0-8518891.x64.bundle ./VMware-Horizon-Client-4.8.0-8518891.x64.bundle --eulas-agreed --required mkdir -p /home/publicuser/.config/openbox/ cat << EOF > /home/publicuser/.config/openbox/autostart /usr/bin/vmware-view -s <connection server> -d csp --allmonitors & EOF sed -i 's/set\ timeout=5/set\ timeout=0/g' /etc/grub2.cfg sed -i 's/set\ timeout=5/set\ timeout=0/g' /boot/grub2/grub.cfg
Notes
From the most awesome technical resource in these projects, Tanner said
Okay, I just thought of a much more minimal way to handle this. Let's systemctl disable gdm and directly modify the getty service to autologin your user at the TTY level. You'll need to install the Xorg xinit package. Then in the user's .bash_profile we run startx upon login. This way you can use xrandr to configure the display in the user's ~/.xinitrc. Let me break it down: First we override the getty systemd service with /etc/systemd/system/getty@tty1.service.d/override.conf: [Service] ExecStart= ExecStart=-/usr/bin/agetty --autologin user_name --noclear %I $TERM Then we add these lines to the end of /home/user_name/.bash_profile: if [[ -z $DISPLAY ]] && [[ $(tty) = /dev/tty1 ]]; then # Check if we have a display and we're on TTY1 startx fi And finally, make /home/user_name/.xinitrc look like this: xrandr --output HDMI2 --whatever_other_options_you_need --right-of HDMI1 exec openbox # or whatever DE/WM you use This method will use less resources and IMO XrandR is *much* better at configuring displays than xorg.conf. Also, I would uninstall the xorg-x11-drv-intel, the modesetting drivers work with intel display thingies just fine
Display Script
This is used for checking the currently detected displays and sorting them left to right based on the connection used. VGA -> DVI -> HDMI -> DP (the script is currently looking for dual monitors)
#!/bin/bash ##Updated to work with systems with greater than three of each type of display. ##Still only outputs dual displays and will only work with the first two it detects. displaycheck () { if grep -qi \$1 <<<\$line; then if grep -qi \$1\1 <<<\$line; then if [ -z "\$d1" ]; then d1=\$line elif [ -z "\$d2" ]; then d2=\$line fi fi # if grep -qi \$1-2 <<<\$line; then # if [ -z "\$d1" ]; then # d1=\$line # elif [ -z "\$d2" ]; then # d2=\$line # fi # fi fi } xrandr > /tmp/xout sed '/disconnected/ d; /LVDS/ d' /tmp/xout | grep connected | awk '{print \$1}' > /tmp/xout2 while read line do displaycheck VGA displaycheck DVI displaycheck HDMI displaycheck DP done < /tmp/xout2 if [ -z $d1 ] && [ -z $d2 ]; then exit 0 fi xrandr --output $d1 --left-of $d2 --output LVDS-1 --off