Instructions for Setting Up a CentOS 7 System with Bridged Networking for Virtual Machines

CentOS 7 bridging network card for virtual machines

My goal is to set up virtualization where the guests can access the entire LAN as well as the host over the network. The host should also be able to reach all the guests via the network.

This task was so simple, but somehow it eluded me for over a year. I use this document to establish a new kvm host in my network pool.

Install virtualization tools

sudo yum -y install libvirt qemu-kvm virt-install
sudo systemctl enable libvirtd.service ; sudo systemctl start libvirtd.service
sudo setsebool -P virt_use_nfs 1

Adjust the ethernet configuration

sudo su -
this_nic="$( nmcli device show | awk '/^GENERAL.DEVICE:/ && $2 ~ /e.*/ {print $2}' )"
this_nic_count="$( printf "%s\n" "${this_nic}" | sed '/^\s*$/d' | wc -l )"
if test ${this_nic_count} -ne 1 ;
   echo "Other than 1 nic detected. Please deal with manually. Aborted."
   # prepare values for bridge definition
   this_mac="$( ip -o link | grep "${this_nic}" | grep -oE 'ether [a-fA-F0-9:]{17}' | awk '{print $2}' | tr '[[:lower:]]' '[[:upper:]]' )"
   this_ipaddr="$( ip -o address show "${this_nic}" | grep -oE 'inet [0-9\.]{7,15}' | awk '{print $2}' )"
   # define bridge interface
      echo "DEVICE=${this_bridge}"
      echo "TYPE=Bridge"
      echo "ONBOOT=yes"
      echo "DELAY=0"
      grep -h -E 'DNS1|DNS2|DOMAIN|IPADDR|PREFIX|BOOTPROTO|GATEWAY|DEFROUTE' "${indir}/ifcfg-${this_nic}"
   } > "${indir}/ifcfg-${this_bridge}"
   # define ethernet card
      echo "DEVICE=${this_nic}"
      echo "HWADDR=${this_mac}"
      echo "ONBOOT=yes"
      echo "BRIDGE=${this_bridge}"
      grep -h -E 'UUID' "${old_nic_file}"
   } > "${temp_nic_file}"
   chmod --reference "${old_nic_file}" "${temp_nic_file}"
   /bin/mv -f "${temp_nic_file}" "${old_nic_file}"

systemctl restart network.service NetworkManager.service

Using the virtual host

With the setup complete, the environment is ready to serve virtual machines!

Install a virtual machine

vm=c7-03a ; time sudo virt-install -n "${vm}" --memory 2048 --vcpus=1 --os-variant=centos7.0 --accelerate -v --disk path=/var/lib/libvirt/images/"${vm}".qcow2,size=20 -l /mnt/public/Support/SetupsBig/Linux/CentOS-7-x86_64-Minimal-1804.iso --initrd-inject=/mnt/public/Support/Platforms/CentOS7/centos7-ks.cfg --extra-args "ks=file:/centos7-ks.cfg SERVERNAME=${vm}" --debug --network type=bridge,source=br0 --noautoconsole

Delete a virtual machine

vm=c7-03a; sudo virsh destroy "${vm}"; sudo virsh undefine --remove-all-storage "${vm}";




Internal files

  1. file:///mnt/public/Support/Platforms/CentOS7/centos7-ks.cfg

Setup Yum Repository with Security Metadata

Define repository

Prepare the repo file on the server, so clients can download it.

cd /var/www/html/yum
cat <<'EOF' > hosting.repo
name=Hosting Delivery

Make or update repository

Use createrepo tool to make the repository. A wrapper script for creating or updating the existing repository is shown here.

cat <<'EOF' > "${tf}"
# reference:

# Prepare directory and files
test -z "${UR_REPODIR}" && UR_REPODIR=/var/www/html/yum/hosting
test -z "${UR_BASEURL}" && UR_BASEURL=
test -z "${UR_OWNERSHIP}" && UR_OWNERSHIP="root.root"
test -z "${UR_FILETYPES}" && UR_FILETYPES="rpm"

find "${UR_REPODIR}" -exec chown "${UR_OWNERSHIP}" {} + 1>/dev/null 2>&1
find "${UR_REPODIR}" -type f -exec chmod "0664" {} + 1>/dev/null 2>&1
find "${UR_REPODIR}" -type d -exec chmod "0775" {} + 1>/dev/null 2>&1
chmod 0754 "$0"
restorecon -RF "${UR_REPODIR}"

# Prepare repo for rpm
cd "${UR_REPODIR}"
createrepo -v -u "${UR_BASEURL}" --basedir "${UR_REPODIR}" --simple-md-filenames --no-database --update --pretty .

Run this script.


Manually make the security metadata

The security metadata that yum interprets is stored in updateinfo.xml.gz. To make this file and include it in repomd.xml, you need to prepare it and learn some information about it.

This is a trim example of updateinfo.xml. Please see the epel metadata for a full example. I do not have an automatic process for generating this file yet.

cat <<'EOF' > "${tf}"
<?xml version="1.0" encoding="UTF-8"?>
  <update status="final" type="security" version="1" from="">
    <title>bgscripts-core update</title>
    <release>Enterprise Linux 7</release>
    <issued date="2018-04-02"/>
    <rights>CC-BY-SA 4.0</rights>
- latest version from upstream
    <solution>This update is internal to the company.</solution>
      <reference href="" type="self" title="bgscripts-core" />
      <collection short="bgscripts">
        <name>bgscripts suite</name>
        <package name="bgscripts-core" version="1.3-8" release="" epoch="0" arch="noarch">
          <sum type="md5">eaa20075720bf12d6e837a4f546241ab</sum>

Update the repo metadata to include updateinfo.xml

A yum repository includes metadata of the package metadata, and stores this meta-metadata in repomd.xml. Insert the metadata for this new file, updateinfo.xml in the repomd file.
This script is an update version of, which was listed earlier in this document.

        cat <<'EOF' > "${tf}"
# reference:

# Prepare directory and files
test -z "${UR_REPODIR}" && UR_REPODIR=/var/www/html/yum/hosting
test -z "${UR_BASEURL}" && UR_BASEURL=
test -z "${UR_OWNERSHIP}" && UR_OWNERSHIP="root.root"
test -z "${UR_FILETYPES}" && UR_FILETYPES="rpm"
test -z "${UR_UPDATEINFO_INPUT}" && UR_UPDATEINFO_INPUT=/var/www/html/yum/build-hosting-repo/updateinfo.xml

find "${UR_REPODIR}" -exec chown "${UR_OWNERSHIP}" {} + 1>/dev/null 2>&1
find "${UR_REPODIR}" -type f -exec chmod "0664" {} + 1>/dev/null 2>&1
find "${UR_REPODIR}" -type d -exec chmod "0775" {} + 1>/dev/null 2>&1
chmod 0754 "$0"
restorecon -RF "${UR_REPODIR}"

# Prepare basic repo
cd "${UR_REPODIR}"
createrepo -v -u "${UR_BASEURL}" --basedir "${UR_REPODIR}" --simple-md-filenames --no-database --update --pretty .

# Inject custom updateinfo
# this task assumes the repomd file does not include node <data type="updateinfo"> yet.

if ! test -e "${UR_UPDATEINFO_INPUT}" ;
   # file is absent, so decide how to fail.
   # file exists, so continue with custom injection

   # learn open-size and open-checksum
   UR_updateinfo_opensize="$( /usr/bin/stat -c "%s" "${UR_UPDATEINFO_INPUT}" )"
   UR_updateinfo_openchecksum="$( /usr/bin/sha256sum "${UR_UPDATEINFO_INPUT}" | awk '{print $1}' )"

   # compress file and learn size and checksum
   /usr/bin/gzip < "${UR_UPDATEINFO_INPUT}" > "${UR_updateinfo_gz}"
   UR_updateinfo_size="$( /usr/bin/stat -c "%s" "${UR_updateinfo_gz}" )"
   UR_updateinfo_checksum="$( /usr/bin/sha256sum "${UR_updateinfo_gz}" | awk '{print $1}' )"
   UR_updateinfo_timestamp="$( /usr/bin/stat -c "%Y" "${UR_updateinfo_gz}" )"

   # insert information into repomd
   this_string="<data type=\"updateinfo\">
  <checksum type=\"sha256\">${UR_updateinfo_checksum}</checksum>
  <open-checksum type=\"sha256\">${UR_updateinfo_openchecksum}</open-checksum>
  <location xml:base=\"${UR_BASEURL}\" href=\"${UR_updateinfo_gz_short}\"/>

      sed -r -e '/<\/repomd>/d' "${UR_repomd}"
      printf "%s\n%s\n" "${this_string}" "</repomd>"
   } > "${UR_repomd}.$$"
   /bin/touch --reference "${UR_repomd}" "${UR_repomd}.$$"
   /bin/mv -f "${UR_repomd}.$$" "${UR_repomd}"


Using bash to modify xml files is obviously not ideal. However, this xml file is simple enough so this ugly mechanism suffices. For teams that know how to manage custom yum repositories and also want to just use yum update –security, this process should be a good basis or even complete solution!


Appendix A: http proxy

If you use an http proxy for your yum traffic, the proxy might cache old versions of the metadata or package files. A quick and dirty way to clean up a squid proxy of the metadata file follows.

time squidclient -h localhost -r -p 3128 -m PURGE

Squid unfortunately does not allow recursive purging, so you will have to loop over all the metadata files and any package files you want to ensure get cleared.


Local file /var/cache/yum/x86_64/7Server/epel/69b82df00108c0ac8ac82fafbd0f3b89cc98d8dfe4fa350af7a23331a878fea2-updateinfo.xml.bz2

Fix CentOS 7 and motherboard clock UTC problem

If you have a freshly installed CentOS 7 system, and when you reboot the time is a few hours off, you might find this info useful.

A probably reason for this error is the system is fetching the hardware clock which is not set in UTC, and so the offset is basically doubled. Or possible the hardware clock is set in UTC but GNU/Linux doesn’t know that.

Here’s how to fix it (courtesy of Michael Hampton at Stack Overflow— what doesn’t he know?).

Fix the date.

# You might need to stop ntpd first.
#systemctl stop ntpd

Ensure the date is correct



# sync hardware clock to the system time
hwclock -wu

And just remember to start up ntpd if you need it.

Changing screen resolution on Linux terminal server

If you’re using xvnc on Linux (for example, if you’re running Xfce on xrdp on Fedora) and you want to resize the display, here’s a way to do it.

DISPLAY=:10 xrandr --output VNC-0 --mode 1024x768

You could also use an –auto instead of a –mode XxY setting.

Apparently I like to redo my own work.
Check out from my bgscripts package.

for word in $( xrandr --listactivemonitors | awk 'NR != 1 { print $NF; }'; ); do xrandr --output ${word} --auto 1>/dev/null 2>&1; done

Compile FreeFileSync 10.1 on Fedora

How to compile FreeFileSync 10.1 on Fedora

Tested on Fedora 28

1. Install build dependencies.

dnf install -y boost-devel compat-wxGTK3-gtk2-devel gcc-c++ gtk+-devel gtk3-devel wxGTK-devel wxGTK3-devel

2. Fetch and extract source

wget --user-agent 'Firefox 60.0 (GNU/Linux) X11' -L
mkdir 10.1
pushd 10.1; unzip ../; popd

3. Prepare patch required to compile on Fedora.

cat <<'EOF' > "${pfile}"
diff -x '*.orig' -x '*.rej' -Naur 10.1/FreeFileSync/Source/Makefile 10.1-0/FreeFileSync/Source/Makefile
--- 10.1/FreeFileSync/Source/Makefile	2018-06-03 04:27:00.000000000 -0400
+++ 10.1-0/FreeFileSync/Source/Makefile	2018-06-07 22:44:44.919899060 -0400
@@ -5,15 +5,15 @@
-CXXFLAGS  = -std=c++17 -pipe -DWXINTL_NO_GETTEXT_MACRO -I../.. -I../../zenXml -isystem../../boost -include "zen/i18n.h" -include "zen/warn_static.h" \
+CXXFLAGS  = -std=c++17 -pipe -DWXINTL_NO_GETTEXT_MACRO -I../.. -I../../zenXml -isystem/usr/include/boost -include "zen/i18n.h" -include "zen/warn_static.h" \
 -Wall -Wfatal-errors -Wmissing-include-dirs -Wswitch-enum -Wcast-align -Wshadow -Wnon-virtual-dtor \
 -O3 -DNDEBUG `wx-config --cxxflags --debug=no` -pthread
-LINKFLAGS = -s -no-pie `wx-config --libs std, aui --debug=no` -pthread
+LINKFLAGS = -s -no-pie `wx-config --libs std, aui --debug=no` -lz -pthread
 #Gtk - support recycler/icon loading/no button border/grid scrolling
-CXXFLAGS  += `pkg-config --cflags gtk+-2.0`
-LINKFLAGS += `pkg-config --libs   gtk+-2.0`
+CXXFLAGS  += `pkg-config --cflags gtk+-3.0`
+LINKFLAGS += `pkg-config --libs   gtk+-3.0`
 #support for SELinux (optional)
 SELINUX_EXISTING=$(shell pkg-config --exists libselinux && echo YES)
@@ -125,5 +125,5 @@
 	mkdir -p $(DOCSHAREDIR)
-	cp ../Build/Changelog.txt $(DOCSHAREDIR)/changelog
+	cp ../../Changelog.txt $(DOCSHAREDIR)/changelog
 	gzip $(DOCSHAREDIR)/changelog
diff -x '*.orig' -x '*.rej' -Naur 10.1/FreeFileSync/Source/RealTimeSync/Makefile 10.1-0/FreeFileSync/Source/RealTimeSync/Makefile
--- 10.1/FreeFileSync/Source/RealTimeSync/Makefile	2018-06-03 04:27:00.000000000 -0400
+++ 10.1-0/FreeFileSync/Source/RealTimeSync/Makefile	2018-06-07 22:20:59.043383931 -0400
@@ -6,11 +6,11 @@
 -Wall -Wfatal-errors -Wmissing-include-dirs -Wswitch-enum -Wcast-align -Wshadow -Wnon-virtual-dtor \
 -O3 -DNDEBUG `wx-config --cxxflags --debug=no` -pthread
-LINKFLAGS = -s -no-pie `wx-config --libs std, aui --debug=no` -pthread
+LINKFLAGS = -s -no-pie `wx-config --libs std, aui --debug=no` -lz -pthread
 #Gtk - support "no button border"
-CXXFLAGS  += `pkg-config --cflags gtk+-2.0`
-LINKFLAGS += `pkg-config --libs   gtk+-2.0`
+CXXFLAGS  += `pkg-config --cflags gtk+-3.0`
+LINKFLAGS += `pkg-config --libs   gtk+-3.0`
diff -x '*.orig' -x '*.rej' -Naur 10.1/FreeFileSync/Source/ui/main_dlg.cpp 10.1-0/FreeFileSync/Source/ui/main_dlg.cpp
--- 10.1/FreeFileSync/Source/ui/main_dlg.cpp	2018-06-03 04:27:02.000000000 -0400
+++ 10.1-0/FreeFileSync/Source/ui/main_dlg.cpp	2018-06-07 22:25:32.856972097 -0400
@@ -11,6 +11,7 @@
 #include <zen/thread.h>
 #include <zen/shell_execute.h>
 #include <zen/perf.h>
+/* #include <zen/warn_static.h>  REMOVED FOR TESTING. Add back on any failures. */
 #include <wx/clipbrd.h>
 #include <wx/wupdlock.h>
 #include <wx/sound.h>
@@ -4844,7 +4845,7 @@
         globalCfg_.gui.lastUpdateCheck = 0; //reset to GlobalSettings.xml default value!
+    /*
     if (shouldRunAutomaticUpdateCheck(globalCfg_.gui.lastUpdateCheck))
         flashStatusInformation(_("Searching for program updates..."));
@@ -4852,6 +4853,7 @@
         automaticUpdateCheckEval(this, globalCfg_.gui.lastUpdateCheck, globalCfg_.gui.lastOnlineVersion,
+    */
@@ -4859,7 +4861,7 @@
     //execute just once per startup!
     Disconnect(wxEVT_IDLE, wxIdleEventHandler(MainDialog::OnRegularUpdateCheck), nullptr, this);
+    /*
     if (shouldRunAutomaticUpdateCheck(globalCfg_.gui.lastUpdateCheck))
         flashStatusInformation(_("Searching for program updates..."));
@@ -4873,6 +4875,7 @@
                                      resultAsync.get()); //run on main thread:
+    */
diff -x '*.orig' -x '*.rej' -Naur 10.1/FreeFileSync/Source/ui/small_dlgs.cpp 10.1-0/FreeFileSync/Source/ui/small_dlgs.cpp
--- 10.1/FreeFileSync/Source/ui/small_dlgs.cpp	2018-06-03 04:27:02.000000000 -0400
+++ 10.1-0/FreeFileSync/Source/ui/small_dlgs.cpp	2018-06-07 22:20:59.050384125 -0400
@@ -970,7 +970,8 @@
-    m_textCtrlOfflineActivationKey->ForceUpper();
+    // Fedora 27 does not have wxWidgets 3.1.1 yet.
+    //m_textCtrlOfflineActivationKey->ForceUpper();
     m_textCtrlLastError           ->ChangeValue(lastErrorMsg);
     m_textCtrlManualActivationUrl ->ChangeValue(manualActivationUrl);
diff -x '*.orig' -x '*.rej' -Naur 10.1/FreeFileSync/Source/ui/version_check_impl.h 10.1-0/FreeFileSync/Source/ui/version_check_impl.h
--- 10.1/FreeFileSync/Source/ui/version_check_impl.h	2018-06-03 04:27:02.000000000 -0400
+++ 10.1-0/FreeFileSync/Source/ui/version_check_impl.h	2018-06-07 22:20:59.051384152 -0400
@@ -14,7 +14,7 @@
 namespace fff
 time_t getVersionCheckInactiveId()
     //use current version to calculate a changing number for the inactive state near UTC begin, in order to always check for updates after installing a new version
@@ -38,7 +38,6 @@
 time_t getVersionCheckCurrentTime()
     return std::time(nullptr);
diff -x '*.orig' -x '*.rej' -Naur 10.1/wx+/grid.cpp 10.1-0/wx+/grid.cpp
--- 10.1/wx+/grid.cpp	2018-06-03 04:27:02.000000000 -0400
+++ 10.1-0/wx+/grid.cpp	2018-06-07 22:30:02.202436428 -0400
@@ -1169,7 +1169,9 @@
                 if (overlapPix != 0)
-                    const double scrollSpeed = wnd_.ToDIP(overlapPix) * mouseDragSpeedIncScrollU; //unit: [scroll units / sec]
+                    // Fedora 28 does not have wxGTK 3.1.1 yet. This probably breaks HiDPI usage
+                    //const double scrollSpeed = wnd_.ToDIP(overlapPix) * mouseDragSpeedIncScrollU; //unit: [scroll units / sec]
+                    const double scrollSpeed = overlapPix * mouseDragSpeedIncScrollU; //unit: [scroll units / sec]
                     toScroll += scrollSpeed * deltaSecs;

A summary of what this patch does:

  • Ensure using the system boost library. It appears the source looks for a locally-bundled boost lib, which was not included with the 10.1 code.
  • Link to zlib because previous versions use to, and without I could not compile.
  • Compile against gtk+3 instead of gtk+2 because I could not get it to compile against gtk2.
  • Place the changelog text file where the Makefile expects.
  • Disable the check for updates and lines related to activation, particularly a few based on a newer version of wxWidgets which Fedora 28 does not have yet (wxWidgets 3.1.1).
  • Revert to an older version of the scrollSpeed calculation because wxWidgets 3.1.1 is not in Fedora yet.

4. Apply patch

cd 10.1
patch -p1 < ../FreeFileSync-10.1-1.fc28.patch

5. Compile and install

cd FreeFileSync/Source
make && sudo make install

Next steps

Build an rpm for FreeFileSync (gitlab)



  1. How-to: Build 10.* from Source (Linux Mint) (FreeFileSync forum)
  2. Compiling FreeFileSync 10.0 on Fedora (this blog)
  3. Compiling FreeFileSync on Fedora (this blog)
  4. AUR (en) – freefilesync

Convert m4a to mp3 while preserving audio quality

If you have a set of m4a files and want to automatically convert them to mp3 so you can tag them the right way, use a snippet I wrote.

See the code in its proper formatting at

# reference:

logfile="/mnt/bgstack15/log/m4a-to-mp3.$( date -u "+%FT%H%M%SZ" ).log"

func() {
for word in "$@" ;
   echo "Entering item ${word}";
   outdir="${word}/mp3" ; mkdir "${outdir}" || exit 1 ;
   find "${word}" -type f \( -regex '.*M4A' -o -regex '.*m4a' \) | while IFS='\0' read infile ;
      test -f "${infile}" && echo "Found file: \"${infile}\"" || echo "INVALID! ${infile}"
      outfile="$( echo "${infile}" | sed -r -e "s/\.m4a/\.mp3/i" )"
      echo  ffmpeg -i \"${infile}\" -codec:v copy -codec:a libmp3lame -q:a 2 \"${outfile}\"
      yes | ffmpeg -i "${infile}" -codec:v copy -codec:a libmp3lame -q:a 2 -y "${outfile}" ; test -n "${outdir}" && /bin/mv -f "${outfile}" "${outdir}/" ;
      sleep 2 ;

time func "$@" | tee -a "${logfile}"

TIL #6: Top 10 Commands That You Use On Your Command-line Terminal

When you get bored, generate a histogram of your common commands.

history | awk '{CMD[$2]++;count++;}END { for (a in CMD)print CMD[a] " " CMD[a]/count*100 "% " a;}' | grep -v "./" | column -c3 -s " " -t | sort -nr | nl |  head -n10
     1  212  21.2%  ansible
     2  207  20.7%  ansible-playbook
     3  94   9.4%   cd
     4  69   6.9%   ll
     5  58   5.8%   cat
     6  55   5.5%   vim
     7  52   5.2%   ssh
     8  39   3.9%   vi
     9  35   3.5%   time
    10  33   3.3%   exit

Shekhar Gulati

I found a command that you can use to list down top commands that you use in your terminal

Following is my list

gdw is shortcut for Gradle wrapper.

View original post

A few useful keybindings for Window Manager in Xfce

xfce4-keyboard-shortcuts /xfwm4/custom/Down             tile_down_left_key
xfce4-keyboard-shortcuts /xfwm4/custom/Left             tile_up_left_key
xfce4-keyboard-shortcuts /xfwm4/custom/Right            tile_down_right_key
xfce4-keyboard-shortcuts /xfwm4/custom/Up               tile_up_right_key
xfce4-keyboard-shortcuts /xfwm4/custom/Down                  tile_down_key
xfce4-keyboard-shortcuts /xfwm4/custom/Left                  tile_left_key
xfce4-keyboard-shortcuts /xfwm4/custom/Right                 tile_right_key
xfce4-keyboard-shortcuts /xfwm4/custom/Up                    tile_up_key

I found that by having these values, I can use the keyboard to control the window placement with almost identical functionality as Cinnamon. This was one of the key differences (no pun intended) for me between Xfce and Cinnamon.

To import these settings with that input as is, check out I describe it here on the blog at xfconf-query save and load from file

Migrating GitHub gists to gitlab

Gitlab, as far as I know, does not have a quick-and-dirty area for snippets in the fashion of Gists on GitHub. My choice was to stick all my gists in a single repository. Here is how I got it done.

This script saves to the current directory all your gists, each to its named folder with an additional file “description” with the gist description since I couldn’t find it inside the git repo for a gist.

#!/usr/bin/env python
# Filename:
# Location: gitlab, probably
# Author: Chris Arndt (stackoverflow uid 39275), bgstack15
# Startdate: 2018-06-05 21:49
# Title: Script That Downloads GitHub Gists in a Nice Format
# Purpose: To facilitate my departure from GitHub
# History:
# Usage: ./ bgstack15
# Reference:
#    copied from
# Improve:
# -*- coding: utf-8 -*-
"""Clone all gists of GitHub username given on the command line."""

import subprocess
import sys
import requests

if len(sys.argv) > 1:
   gh_user = sys.argv[1]
   print("Usage: ")

req = requests.get('' % gh_user)

for gist in req.json():

   # get attributes
   name = gist['files'].keys()[0]
   descrip = gist['description']

   # debugging
   print name + ": " + descrip

   # clone the repo
   ret =['git', 'clone', gist['git_pull_url'], name])
   if ret != 0:
      print("ERROR cloning gist %s. Please check output." % gist['id'])

   # save description
   with open(name + "/" + "description", "w") as text_file:

Once I had all my gist directories there, I cleaned up their .git directories, and made a new git repo and uploaded it to gitlab.

rm -rf */.git/
git init
git add . -m 'initial retrieval from github gists'
git remote add origin
git push --set-upstream origin master