Installing Parallels Tools on Centos 8

A little while back I tweeted Parallels via their @ParallelsCares twitter account to find out if they could provide support for Parallels Tools with CentOS 8.

RHEL 8 was released 7th May 2019, with Parallels 15 released 13th August 2019, so since CentOS is a free version of RHEL with the commercial, and copyright protected parts removed, I made the presumption that Parallels 15 would support it. However with CentOS 8 taking until 24th September 2019 to be released, all they did was respond to state that they didn’t support it, with a link to their KB article.

I went about looking for a fix, and it turns out it’s fairly easy. It needs two things:

  • Pretending to Parallels that what you’re installing is RHEL 8
  • The EPEL Repo enabled

Here’s the exactly what I did:

  1. Download the CentOS DVD image for the version that you want here
  2. In Parallels, Start the new VM Installation Assistant
  3. Select “Install Windows or another OS from a DVD or image file”
  4. Select “Choose Manually”, then “Select a file…”, and find the DVD image you’ve just downloaded
  5. It will claim “Unable to detect operating system”, click “Continue”
  6. When the Prompt comes up to select your operating system, select “More Linux” > “Red Hat Enterprise Linux”, then proceed through to creating and booting the VM
  7. Proceed with the install
    1. Ensure the network is enabled under “Network & Host Name”
    2. Ensure the drive is checked in “Installation Destination”
    3. To fix the error in “Installation Source”, select “On the network” > “http://”, and enter the following in the text box:
    4. Once it’s downloaded the metadata, under the “Software Selection” select the options you want for your install
    5. Proceed with the install, and make sure you enter a root password
  8. Once rebooted under a terminal, install EPEL using the instructions here. Ensure you run the command to enable the PowerTools repository too.
  9. Start the “Install Parallels Tools” process, ensuring that any existing mounted CD/DVD is unmounted. This is needed to mount the Parallels Tools DVD image.
  10. Run the following commands in a terminal:
    mkdir /media/cdrom
    mount /dev/cdrom /media/cdrom
    cd /media/cdrom
  11. Proceed through the install process for Parallels Tools, and you should be done.

If you want to do this on an existing VM, shutdown the VM, go to Settings > General and change the type (above the VM Name), to Red Hat Enterprise Linux, then boot, and perform steps 9-11 above.

Restoring Google Chrome Last Tabs / Session

I had a slight frustration with this, I generally keep a lot of tabs open, but once due to an issue fiddling with an extension destroying my existing session, and another due to transferring to a new computer at work, I’ve had to try and restore my Last Tabs / Session data, and each time had to google around and cobble together an answer.

Here it is for those of you searching for the same answer.

If you’ve opened chrome since you’re going to need a backup of your old files (I’ve retrieved this from an Apple Time Machine, or simply the old mac profile ~/Library folder)

For the from backup route, navigate to the following folders in your backup:
Mac – /Library/Application Support/Google/Chrome/Default
Windows – \AppData\Local\Google\Chrome\User Data\Default

And copy the Last Session and Last Tabs files to the same folder on your hard drive, so most likely:
Mac – /Users//Library/Application Support/Google/Chrome/Default
Windows – C:\Users\\AppData\Local\Google\Chrome\User Data\Default

After which you simply need to launch it using the correct launch parameter of --restore-last-session, so:

Mac – Open a terminal and launch using: open /Applications/Google\ --args --restore-last-session
Windows – Edit the shortcut and make sure it’s launching with something like "C:\Users\\AppData\Local\Google\Chrome SxS\Application\chrome.exe" --restore-last-session

MariaDB 10.1 and SystemD start issue



I was receiving the following messages:






Possible Solutions:

  • Set the ProtectSystem value to false

I’d recommend the former

MySQL & Pivot functionality


  2. As it supports them, a MySQL Stored Procedure


Here’s an example of the stored procedure I produced:

DROP PROCEDURE IF EXISTS pivot_user_preferences;


CREATE PROCEDURE pivot_user_preferences(IN user_id INT)
    DECLARE definition_label CHAR(255);
    DECLARE label_cursor CURSOR FOR SELECT DISTINCT label FROM example_db.user_preferences;
    SET @user_id = user_id;
    SET @sql_statement = 'SELECT u.* ';

    OPEN label_cursor;

    read_loop: LOOP
        FETCH label_cursor INTO definition_label;
        IF done THEN
            LEAVE read_loop;
        END IF;

    CLOSE label_cursor;

    PREPARE stmt FROM @sql_statement;
    EXECUTE stmt USING @user_id;
END $$


PHP MySQL PDO and the Multiple Queries Bug



I had three choices:



$myConn = new PDO(

$multiSqlStatement = '
INSERT INTO users (username) VALUES (:username);
$bindVariables = array(
	':group_type' => 'this_type',
	':username' => 'my_username',
	':group_name' => 'Other'

foreach ($tempSqlParts as $tempSqlPart) {
    $tempSqlPart = trim($tempSqlPart);
    if (!empty($tempSqlPart)) {
        	: array();

CloudFlare and Dynamic DNS

Recently I changed ISPs.  This generally isn’t a big deal. It didn’t cause any difference in speed or  connection reliability, however my old ISP had, regardless of me not ordering one, given me a static IP address.  I’d taken advantage of this and assigned the IP to a DNS record to give me easy to remember access to my files when I’m away from home.  Anyway when I changed ISPs I discovered my new one changed my IP address every few days, which is an unfortunate thing to discover when you’re not at home.

I took a look at my router and found it only supported DynDns, which I then found out that, apart from to some DLink users, didn’t provide provide a free version of their service to the general public, and I don’t have a DLink router.

The next option was a free alternative to DynDns, there were several, and they all required running some kind of linux demon / cron script, which I would then have to alter my existing A record DNS entry to a CNAME to point to the new dynamic DNS record.

Before setting this up I thought I’d look into my existing DNS provider just incase there was any way of updating DNS records on the fly without having to maintain two separate DNS providers. I found that CloudFlare had an API, which I could update records with. The example provided was a Curl example, which made me wonder, surely I could just write a bash script to update the record with via a cron job. I didn’t want to have to install any major scripting language on my minimal Linux distro, excluding any php, perl etc. solution I’d found that worked with CloudFlare.

I checked CloudFlare’s API rate limiting and found it certainly wouldn’t be limiting for what I was trying to do (it’s approximately 1200 requests every 5 mins if you’re wondering). I then found MyExternalIP that could provide my external IP address with a rate limit that was easy to deal with of roughly 1/s, after which it was just a case of writing the bash script.

I worked a script out using Curl requests to get the information I needed including the specific id of the DNS record I was trying to update and then used sed to get the information I needed out each requests response. I made it compatible with the CloudFlareAPI call I was making that way any parameter passed to the call could be passed by a command line argument to the script itself.

The resultant script I added to my crontab to run every half hour.  Everything worked perfectly so I thought  I’d create a gist at github so others could take advantage of this:

# CloudFlare Dynamic DNS
# Updates CloudFlare records with the current public IP address
# Takes the same basic arguments as A/CNAME updates in the CloudFlare v4 API
# Use with cron jobs etc.
# e.g.
# manually run:
# -key 404613183ab3971a2118ae5bf03d63e032f9e -email -zone -name extra
# cronjob entry to run every 5 minutes:
# */5 * * * * /path/to/ -key 404613183ab3971a2118ae5bf03d63e032f9e -email -zone -name extra >> /path/to/cloudflare_dyn_dns.log
# will both set the type A DNS record for to the current public IP address for user with the provided API key


    case $1 in
        -key )     shift
        -email )   shift
        -zone )    shift
        -zone_id ) shift
        -type )    shift
        -rec_id )  shift
        -name )    shift
        -content ) shift
        -ttl )     shift
        -proxied ) shift
        * )        echo "unknown parameter $1"
                   exit 1

if [ "$content" = "" ]
    content=`curl -s`
    if [ "$content" = "" ]
        echo "No IP address to set record value with."
        exit 1

if [ "$name" = "" ]
    echo "You must provide the name of the record you wish to change."
    exit 1

if [ "$zone" = "" ]
    echo "You must provide the domain you wish to change."
    exit 1

if [ "$name" = "$zone" ]

command -v host > /dev/null 2>&1
if [ "$?" = "1" ]
    command -v nslookup > /dev/null 2>&1
    if [ "$?" = "1" ]
        echo "Cannot find a way to check existing $type record for $hostname"
        exit 1
    existing_content=`nslookup -type=$type $hostname | awk -F 'Address: ' 'NR==6 { print $2 }'`
    existing_content=`host -t $type $hostname | sed -E 's/.+?\s+([^\s]+)$/\1/'`

if [ "$content" = "$existing_content" ]
    echo "Existing record value $existing_content is the same as provided content $content. Exiting."

if [ "$key" = "" ]
    echo "You must provide your user API token."
    exit 1

if [ "$email" = "" ]
    echo "You must provide your user email."
    exit 1

# Get the zone id for the entry we're trying to change if it's not provided
if [ "$zone_id" = "" ]
    zone_response_json=`curl -X GET "$zone" -H "X-Auth-Email: $email" -H "X-Auth-Key: $key" -H "Content-Type: application/json"`
    zone_id=`echo $zone_response_json | sed -E "s/.+\"result\":\[\{\"id\":\"([a-f0-9]+)\"[^\}]+$zone.+/\1/g"`
    if [ "$zone_id" = "" ]
        echo "Cloudflare DNS Zone id could not be found, please make sure it exists"
        exit 1

# Get the record id for the entry we're trying to change if it's not provided
if [ "$rec_id" = "" ]
    rec_response_json=`curl -X GET "$zone_id/dns_records?name=$hostname" -H "X-Auth-Email: $email" -H "X-Auth-Key: $key" -H "Content-Type: application/json"`
    rec_id=`echo $rec_response_json | sed -E "s/.+\"result\":\[\{\"id\":\"([a-f0-9]+)\"[^\}]+\"type\":\"$type\"[^\}]+$hostname.+/\1/g"`
    if [ "$rec_id" = "" ]
        echo "Cloudflare DNS Record id could not be found, please make sure it exists"
        exit 1

# Update the DNS record
update_response=`curl -X PUT "$zone_id/dns_records/$rec_id" -H "X-Auth-Email: $email" -H "X-Auth-Key: $key" -H "Content-Type: application/json" --data "{\"id\":\"$rec_id\",\"type\":\"$type\",\"name\":\"$hostname\",\"content\":\"$content\",\"ttl\":$ttl,\"proxied\":$proxied}"`

success_val=`echo $update_response | sed -E "s/.+\"success\":(true|false).+/\1/g"`
if [ "$success_val" = "true" ]
    echo "Record Updated."
    echo "Record update failed."
    exit 1

Firewalld, firewall-cmd and Fail2Ban



Fedora 19, GNOME 3 and nVidia graphics





BETT 2013






NginX – Blocking per IP address or range




geo $ban_ip {
default 0; 1; 1;


if ($ban_ip) {
return 404;

or you can tell NginX to simply drop the connection,

if ($ban_ip) {
return 444;


Thanks for this lovely snippet of information go to Alexander Azarov.

Giving you lovely snippets of helpful, strange or otherwise random information