CentOS 6 / Windows 8 Dual-Boot on Different Drives

Please don’t consider this an authoritative post. To reach this point there were many, many reboots, reinstalls, and recoveries.

I bought a small ASUS tower from Best Buy and a 120GB solid-state drive.

I tried to install CentOS 6.4 over Windows 8, but the UEFI boot sequence prevented me from succeeding. Taking this a sign that there was value in Windows 8, I began to look into dual-booting.

Since I will be using CentOS for development and Windows 8 for testing (IE10), I wanted to put CentOS on the solid-state drive, and not modify the Windows 8 drive, other than that which was required to allow dual-booting.

The first few attempts assumed Windows would boot and allow me to choose CentOS, defaulting to CentOS. No matter what I did, this didn’t work. Right or wrong, I think Windows was unable to dual-boot to a drive other than the one it was on.

In the process, I lost the Windows recovery partitions installed by the manufacturer. This meant I had to reinstall Windows from a licensed MSDN disk. It also meant I lost the UEFI elements. But, in the end Windows 8 was booting and running.

I had to disable the UEFI only boot limitation in the BIOS. This may not be necessary.

The next step was to install CentOS, which I did using a USB stick and a network install. The stick was created with UNetBootin, which is really nice.

When the installer got to the part where it asked which drives should be used, I chose both the Windows and solid-state drives, and placed the boot loader on the Windows drive. This is because the Windows drive is identified in the UEFI BIOS as a boot device, and the solid-state disk isn’t. The solid-state device may be considered bootable now that the installation is complete, but there’s no reason to change what is working.

Once CentOS installed everything was fine. The only adjustment I made was to edit the /boot/grub/grub.conf file to change ‘Other’ to ‘Windows 8.’

This is the grub.conf file:

# grub.conf generated by anaconda
#
# Note that you do not have to rerun grub after making changes to this file
# NOTICE:  You have a /boot partition.  This means that
#          all kernel and initrd paths are relative to /boot/, eg.
#          root (hd1,0)
#          kernel /vmlinuz-version ro root=/dev/mapper/vg_asustowercentos-lv_root
#          initrd /initrd-[generic-]version.img
#boot=/dev/sdc
default=0
timeout=5
splashimage=(hd1,0)/grub/splash.xpm.gz
hiddenmenu
title CentOS (2.6.32-358.6.1.el6.i686)
	root (hd1,0)
	kernel /vmlinuz-2.6.32-358.6.1.el6.i686 ro root=/dev/mapper/vg_asustowercentos-lv_root rd_NO_LUKS LANG=en_US.UTF-8 rd_LVM_LV=vg_asustowercentos/lv_root rd_NO_MD SYSFONT=latarcyrheb-sun16 rd_LVM_LV=vg_asustowercentos/lv_swap crashkernel=128M  KEYBOARDTYPE=pc KEYTABLE=us rd_NO_DM rhgb quiet
	initrd /initramfs-2.6.32-358.6.1.el6.i686.img
title CentOS (2.6.32-358.6.1.el6.i686)
	  root (hd1,0)
	  kernel /vmlinuz-2.6.32-358.6.1.el6.i686 ro root=/dev/mapper/vg_asustowercentos-lv_root rd_NO_LUKS LANG=en_US.UTF-8 rd_LVM_LV=vg_asustowercentos/lv_root rd_NO_MD SYSFONT=latarcyrheb-sun16 rd_LVM_LV=vg_asustowercentos/lv_swap crashkernel=128M  KEYBOARDTYPE=pc KEYTABLE=us rd_NO_DM rhgb quiet
	  initrd /initramfs-2.6.32-358.6.1.el6.i686.img
title Windows 8
	rootnoverify (hd0,0)
	chainloader +1

/dev/hd0 is the Windows drive
/dev/hd1 is the solid-state drive

Lessons I learned:

If you use gparted (http://gparted.sourceforge.net/), you can’t install Windows 8 on the new partitions.

I could create the UEFI partitions, but not the recovery partitions. But it wouldn’t work. I’m sure I missed some vital step. Every time. :)

The EasyBCD tool (http://neosmart.net/EasyBCD/) is really nice, but I couldn’t get it to do what I wanted. I think if you are going to put both operating systems on the same drive, it would be the way to go.

It costs $130 for the GeekSquad at BestBuy to restore the disk to its manufacturer state. I didn’t bother.

You can’t buy a Windows 8 recovery disk from the manufacturer (ASUS) at the time of this writing.

CentOS installs very fast on a solid-state drive. Thank goodness.

Creating the UEFI partitions for Windows 8 would probably take me a week, and even then, I’m not sure it would work. It’s very interesting.

UEFI = Universal Extensible Firmware Interface

I should have made a recovery disk for Windows before I started.

Windows 8 is nice.

Window-X (holding the Windows key and clicking X) gets you the admin menu, which includes the command line and disk management utilties.

Windows 8 has auto-repair, but it didn’t work in my case.

It often takes less time to reinstall than to identify and correct a problem. That’s a fine strategy if you don’t need to know too much. In my case, the goal was dual-boot - not a thorough understanding of the boot load process across two operating systems.

Adjusting the screen resolution under CentOS 6

With sincere thanks to the link above, this is how you can adjust the screen resolution under CentOS 6.

Run this command (with the appropriate resolution):

cvt 1920 1080

You’ll get output similar to this:

# 1920x1080 59.96 Hz (CVT 2.07M9) hsync: 67.16 kHz; pclk: 173.00 MHz
Modeline “1920x1080_60.00″ 173.00 1920 2048 2248 2576 1080 1083 1088 1120 -hsync +vsync

Copy the modeline and paste it after –newmode, like so:

xrandr --newmode "1920x1080_60.00" 173.00 1920 2048 2248 2576 1080 1083 1088 1120 -hsync +vsync

Then assign the new mode to the device:

xrandr --addmode VGA-1 "1920x1080_60.00"

This was done on a clean stock CentOS 6.4 installation, no custom drivers.

To get the settings to persist across sessions, I created /etc/gdm/PostLogin/Default
like so:

#!/bin/sh
#
if [ -f "$HOME/.gdm/PostLogin/Default" ];
then
	exec -l "$SHELL" -c "$HOME/.gdm/PostLogin/Default"
fi;

Under the account, I add the Default file:

xrandr --newmode "1920x1080_60.00"  173.00  1920 2048 2248 2576  1080 1083 1088 1120 -hsync +vsync
xrandr --addmode VGA-1 "1920x1080_60.00"
xrandr --output VGA-1 --mode "1920x1080_60.00"

Be sure to grant execute permissions on both /etc/gdm/PostLogin/Default and $HOME/.gdm/PostLogin/Default.

Adding nVidia drivers didn’t help, adding hplip didn’t help. There is a Tripp-Lite KVM switch. This was the answer.

And, it looks great.

PHP Abstract Base Class - Including validation

An example of a base class.

Sample extension and call for instantiation:


<?php
class Course extends Base {

        public function __construct($properties) {
                $this->_properties = array(
                        'id'=>array(
                                'required' => false,
                                'default' => null,
                                'filter' => FILTER_VALIDATE_INT,
                                'options' => array('min_range' => 0, 'max_range' => PHP_INT_MAX)),
                        'name' => array(
                                'required' => true,
                                'default' => null,
                                'filter' => FILTER_VALIDATE_REGEXP,
                                'options' => array('regexp' => '/^[\w\,\.\'\-\(\)\$ ]{3,}$/')),
                        'isCore' => array(
                                'required' => true,
                                'default' => true,
                                'filter' => FILTER_VALIDATE_BOOLEAN));

                return parent::__construct($properties);
        }
}

<?php
Abstract Class Base {

        protected $_properties = null;
        protected $_data = array();
        protected $_valid = false;
        protected $_error_message = array();

        function __construct($properties = null) {
                if (is_array($properties)) {
                        foreach ($this->_properties as $k => $v) {
                                if (isset($properties[$k])) {
                                        $p = $properties[$k];
                                        if (is_string($p)) {
                                                $p = trim($p);
                                                if ($p === "") {
                                                        if (empty($p) && $v['required']) {  // this should never happen
                                                                return null;
                                                        }
                                                }
                                        }
                                } else {
                                        $p = $v['default'];
                                }
                                $this->_data[$k] = $p;
                        }
                        $filter = filter_var_array($this->_data,$this->_properties);
                        if ($filter === false || in_array(false,$filter,true)) {
                                foreach ($filter as $k => $v) {
                                        if (($v === false) &&
                                                ($this->_properties[$k]['filter'] !== FILTER_VALIDATE_BOOLEAN)) {
                                                // This is an important test, a boolean false, 
                                                // validated as such, doesn't mean the filter
                                                // failed.  
                                                trigger_error('Invalid data');
                                        }
                                }
                        }
                }
                return $this->_data;
        }

        function __get($property) {
                return isset($this->_data[$property]) ? $this->_data[$property] : null;
        }

        function __set($property, $value) {
                $this->_data[$property] = $value;
        }

        function toJson() {
                return json_encode($this->_data);
        }

        function isValid($property = null) {
                if ($property === null)
                        return $this->_valid || (count($this->_error_message) == 0);
                else
                        return !isset($this->_error_message[$property]);
        }

        function error($property) {
                if (isset($this->_error_message[$property])) {
                        return $this->_error_message[$property];
                } else {
                        return '';
                }
        }
}

PHP filter_var_array Example

This is a very simple PHP contact form validation script.

It fails silently, based on the expectation the client is validating the data prior to submitting it. In this case, if the server receives invalid inputs, inputs with invalid data, or a data set missing required inputs, it is assumed the data is either not being submitted by the expected client code, or it has been tampered with en route.

The silent fail is a die with no output. This provides no information for potentially malicious visitors.

If the data is valid, it is echoed back to the server JSON encoded.

if (isset($_POST) && !empty($_POST)) {
        $required = array('name', 'email', 'interest', 'relationship', 'message');
        $optional = array('phone', 'subscribe');
        $inputs = array_merge($required, $optional);
        foreach ($_POST as $k => $v) {
                if (!in_array($k, $inputs)) {
                        die;
                }
                $v = trim($v);
                if (!empty($v)) {
                        $data[$k] = $v;
                } else {
                        if (in_array($k, $required)) {
                                die;
                        } else {
                                $data[$k] = '';
                        }
                }
        }

        $filter = filter_var_array($data, array(
                'name' => array('filter' => FILTER_VALIDATE_REGEXP,
                                'options' => array('regexp' => '/^[ \w\,\.\'\-]{5,}$/')),
                'email' => FILTER_VALIDATE_EMAIL,
                'interest' => array('filter' => FILTER_VALIDATE_REGEXP,
                                'options' => array('regexp' => '/^(Sales|Support)$/')),
                'relationship' => array('filter' => FILTER_VALIDATE_REGEXP,
                                'options' => array('regexp' => '/^(Client|Partner|Vendor)$/')),
                'subscribe' => array('filter' => FILTER_VALIDATE_REGEXP,
                                'options' => array('regexp' => '/^(on)?$/')),
                'message' => array('filter' => FILTER_VALIDATE_REGEXP,
                                'options' => array('regexp' => '/^[\w\,\.\'\-\(\)\$ ]{5,}$/')),
                'phone' => array('filter' => FILTER_VALIDATE_REGEXP,
                                'options' => array('regexp' => '/^(1[ \-\.])?(\d{3})?[ \-\.]?\d{3}[ \-\.]?\d{4}$/'))));

        if ($filter === false || in_array(false,$filter)) {
                die;
        }
        echo json_encode($filter);
}

In an environment where security is important (security is important in all environments), this code should be extended to include a unique token validation, where a token is sent to the client on the initial request and the next request must have the identical token or it will be considered invalid.

PHP - ImageMagick command line Pie Chart

This code was converted from a bash version to create pie charts. It is much faster and does not require bc on the server.

PHP is used to create the ImageMagick command line.

This code creates pie chart and legend images which can then be placed on a web page or added in to a document.

To call the code, create an array of values, instantiate the GFX objects, establish the label_color_map as an associative array where the label text is the key and the value is a hex color code, and finally create the chart, sending a name and the data array, like so:


                        $data = array(50, 70, 100, 3, 49);
                        $chart = new GFX();
                        $chart->label_color_map(array('one' => '#1c28a1', 
                          'two' => '#600087',
                          'three' => '#107a3f',
                          'four' => '#bf0000',
                          'five' => '#ff6d00'));
                        $chart->piechart('project',$data);

<?php 
Class GFX extends Base {

        public function __construct($parms = null) {
                $this->_properties = array('centerx','centery','width','height',
'radius');
                if (!is_array($parms)) {
                        $parms['width'] = $parms ['height'] = 330;
                        $parms['centerx'] = $parms['centery'] = 160;
                        $parms['radius'] = 135;
                }
                parent::__construct($parms);
        }

        public function label_color_map ($map = array()) {
                $this->_data['labels'] = array();
                foreach ($map as $k => $v) {
                        $this->_data['labels'][$k] = $v;
                }
        }
        public function piechart($name = null, $data = array()) {
                // Thanks to: http://jbkflex.wordpress.com/2011/07/28/creating-a-svg-pie-chart-html5/
                if ($name === null) return;

                $total = array_sum($data);

                $max=0;$kmax='None';
                foreach ($data as $k => $v) {
                        $value[$k] = (int)(($v/$total) * 100);
                        if ($value[$k] > $max) {
                                $kmax=$k;
                                $max=$value[$k];
                        }
                }
                while (array_sum($value) < 100) {
                        $value[$kmax]++;
                }

                foreach ($data as $k => $v) {
                        $value[$k] = (int)(($v/$total) * 100);
                        if ($value[$k] > $max) {
                                $kmax=$k;
                                $max=$value[$k];
                        }
                }
                while (array_sum($value) < 100) {
                        $value[$kmax]++;
                }
                $centerx = $this->_data['centerx'];
                $centery = $this->_data['centery'];
                $radius= $this->_data['radius'];
                $count=0;
                $startAngle=0;
                $endAngle=0;
                $arc=0;
                $x1=0;
                $x2=0;
                $y1=0;
                $y2=0;
                $pi=pi();
                $cmd='convert -size '.$this->_data['width'].'x'.$this->_data['height'].' xc:white -stroke white -strokewidth 5 ';

                foreach ($value as $k => $v) {
                        $startAngle=$endAngle;
                        $endAngle=$startAngle+(360*$v/100);
                        $theta = $pi*$startAngle/180;
                        $x1=$centerx+$radius*cos($theta);
                        $y1=$centery+$radius*sin($theta);
                        $theta = $pi*$endAngle/180;
                        $x2=$centerx+$radius*cos($theta);
                        $y2=$centery+$radius*sin($theta);
                        $arc = ($v >= 50) ? '1' : '0';
                        $cmd.=' -fill "'.$this->_data['labels'][$k].'" -draw "path \'M '.$centerx.','.$centery.' L '.$x1.','.$y1.
                                ' A '.$radius.','.$radius.' 0 '.$arc.',1 '.$x2.','.$y2.' Z"';
                        $count++;
                }
                $cmd.=' '.escapeshellarg(_GFX_DIR_.'/'.$name.'_chart.jpg');
                `$cmd`;

                if (!is_array($this->_data['labels'])) return;

                $KEY_SIZE=20;
                $MARGIN=5;
                $TEXT_X=$KEY_SIZE+$MARGIN;

                $height =  $TEXT_X * count($value);

                $cmd = 'convert -size 135x'.$height.' xc:white -fill white ';
                $label = " -font 'Nimbus-Sans-Bold' -stroke none -pointsize 12 ";
                $count=0;
                $y1=5;
                foreach ($value as $k => $v) {
                        $y2=$y1+$KEY_SIZE;
                        $y3=$y2-$MARGIN;
                        $label.=' -fill "'.$this->_data['labels'][$k].'" -draw "rectangle 0,'.$y1.' '.$KEY_SIZE.','.$y2.'"'.
                                ' -draw "text '.$TEXT_X.','.$y3.' '.escapeshellarg(/*$data[$k].' '.*/$v.'% '.$k).'"';
                        $count++;
                        $y1=$y1+$KEY_SIZE+$MARGIN;
                }
                $cmd.= $label.' '.escapeshellarg(_GFX_DIR_.'/'.$name.'_legend.jpg');
                `$cmd`;
        }
}

<?php
Abstract Class Base {

        protected $_properties;
        protected $_data;
        protected $_valid = false;
        protected $_error_message = array();

        function __construct($props) {
                $this->_data = array_fill_keys($this->_properties, null);
                $this->_data = array_merge($this->_data, $props);
        }

        function __get($property) {
                return isset($this->_data[$property]) ? $this->_data[$property] : null;
        }

        function __set($property, $value) {
                $this->_data[$property] = $value;
        }

        function isValid($property = null) {
                if ($property === null)
                        return $this->_valid || (count($this->_error_message) == 0);
                else
                        return !isset($this->_error_message[$property]);
        }

        function error($property) {
                if (isset($this->_error_message[$property])) {
                        return $this->_error_message[$property];
                } else {
                        return '';
                }
        }
}

Many thanks to the link above for the chart algorithm.

This post courtesy of Worktrainer.