Akom's Tech Ruminations

Various tech outbursts - code and solutions to practical problems

Linux Simple puppet update-alternatives

Posted by Admin • Thursday, June 11. 2015 • Category: DevOps, Linux
This is a quick and dirty interface to update-alternatives on Centos/Redhat/Ubuntu for puppet. Seems to work well and doesn't require any modules.
Usage example: alternatives_update { 'java': versiongrep => '1.8' }

class my_alternatives {

  # Manipulates alternatives using update-alternatives.
  # Supports RHEL, Centos and Suse.
  # Ubuntu not tested (yet).
  # If multiple matches are available, picks the first one.
  # There is rudimentary alternatives support in the java class,
  # but it's rather limited and doesn't support most platforms and java versions.
  define update (
    $item = $title,   # the item to manage, ie "java"
    $versiongrep,     # string to pass to grep to select an alternative, ie '1.8' (1.8.*openjdk would also work)
    $optional = true,  # if false, execution will fail if the version is not found
    $altcmd   = 'update-alternatives' # command to use
  ) {

    case $::osfamily {
      'RedHat','SuSE': {

        if ! $optional {
          # verify that we have exactly 1 matching alternatives, unless it's optional
          exec { "check alternatives for ${item}":
            path    => ['/sbin','/bin','/usr/bin','/usr/sbin'],
            command => "echo Alternative for ${item} version containing ${versiongrep} was not found, or multiple found ; false",
            unless  => "test $(${altcmd} --display ${item} | grep '^/' | grep -- '$versiongrep' | wc -l) -eq 1",
            before  => Exec["update alternatives for ${item} to ${versiongrep}"],

        # Runs the update alternatives command
        #  - unless it reports that it's already set to that version
        #  - unless that version is not found via grep
        exec { "update alternatives for ${item} to ${versiongrep}":
          path    => ['/sbin','/bin','/usr/bin','/usr/sbin'],
          command => "${altcmd} --set ${item} $( ${altcmd} --display ${item} | grep '^/' | grep -- '$versiongrep' | head -n 1 | sed 's/ .*$//' ) ",
          unless  => "test -x \"$(${altcmd} --display ${item} | grep 'currently points' | grep -- '$versiongrep' | awk '{print \$NF}')\"",
          onlyif  => "${altcmd} --display ${item} | grep '^/' | grep -- '$versiongrep' ", # check that there is one (if optional and not found, this won't run)


      # Leave Ubuntu alone, this probably won't work there anyway



Linux Getting a systemd unit to read your .bashrc file for its environment

Posted by Admin • Thursday, June 11. 2015 • Category: Linux
Although it'd be nice to have all of your services not rely on their shell environment, sometimes it is unavoidable. In my case, some of the systems are still on init.d (redhat <=65, centos <=65, etc), but some are on systemd (7.0's, suse 12, etc). The old init scripts rely on the .bashrc file. In order to make the service run consistently on both flavors, I had to teach systemd to read the user's bashrc file. This is what it looks like.

# Process the normal environment files for this user by starting a login shell
# and outputting it all in a temp file.  This makes it compatible
# with the non-systemd init scripts that still rely on .bashrc
ExecStartPre=/bin/bash --login -c 'env > /tmp/.magic-environment-file'
The "-" before the filename seems to make systemd ignore the error if the file is not there (for example, on the first run of the pre command). Now, obviously, you could just set your ~some-user/.bashrc as the EnvironmentFile - but your mileage will vary. systemd will not interpolate variables and it will ignore lines starting with "export"...

Continue reading "Getting a systemd unit to read your .bashrc file for its environment"

Code and Hacks QOS settings for pfSense - optimizing Google Hangouts and SIP

Posted by Admin • Monday, June 8. 2015 • Category: Code and Hacks
pfSense router configuration for Traffic Shaping is relatively convoluted if you're not already familiar with the interface. Assuming that you've used one of the wizards to create the default queues (default, ack, voip, p2p), what you have to do is:
  1. Set the upload bandwidth (Traffic Shaper->By Interface->WAN). This may be optional if using the default PRIQ mode
  2. Create a rule to match Google Hangouts traffic. According to this helpful post, the UDP ports hangouts uses are 19302-19309 (I'm assuming that outbound UDP is open). Therefore we create a new "match" type firewall rule with these ports as destination and assign them to the voip queue. Here is an example of a Floating rule to do that:
    Example Floating Rule
  3. For SIP (if you have SIP devices or softphones), you can make a Layer 7 rule (Traffic Shaper -> Layer 7) that assigns sip traffic to the voip queue
This is, of course, just a note for myself.

Linux Wifi doesn't work after resume from suspend in Ubuntu 15.04 (Dell Latitude E4310)

Posted by Admin • Sunday, May 17. 2015 • Category: Linux
Although toggling the hardware Wi-Fi switch usually fixed this issue, it was decidedly annoying for the non-technical user of this laptop. Googling for a well-known solution turned up several, but none of these worked. I found that either running
nmcli r wifi off
nmcli r wifi on
usually worked, so I tried to stick that into /etc/pm/sleep.d - but that script was never used. I then discovered that in 15.04 Ubuntu switched to systemd, which requires a service file in /etc/systemd/system/ In the end, it turns out that the problem is intermittent, therefore it is not possible to simply toggle networking blindly and hope for the best. Half the time it will not work. The ultimate solution turned out to be a combination of a toggle and a check, and both could be done via network manager, as follows:

Continue reading "Wifi doesn't work after resume from suspend in Ubuntu 15.04 (Dell Latitude E4310)"

Hardware Hacks Increasing capacity of the PetSafe Simply Clean Litter box

Posted by Admin • Tuesday, March 10. 2015 • Category: Hardware Hacks
Although I wrote a somewhat harsh review of the PetSafe Simply Clean Litterbox on Amazon, I still felt that there was some potential to this thing. What I wanted most from it was a very low maintenance experience, and having to empty the waste bin every 3 days simply didn't seem to qualify. When I go on vacation (and please don't start a neglect flame war over this) I leave the cats alone - monitored by 4 cameras, auto feeder, 5 gallon water dispenser and many litter boxes. I don't want to bother relatives with the long drive to come over just to clean cat poo, and my cats do not take well to being moved. Or to strangers. Or to basically anything besides the quiet at-home life that is exactly like yesterday.

Therefore, what I'm looking for is a larger waste bin. The litterbox doesn't handle capacity problems well - it backs up, jamming up against the chute cover, making a mess and eventually just shutting itself off. What I need to do is to create a jam-resistant path for the waste to go, somewhat like this patent here. The idea is good but it seems a bit too complex for my taste, and of course it is intended for a rectangular raking box like the LitterMaid. So, I chose to do the simplest and most reliable thing I could think of.

Continue reading "Increasing capacity of the PetSafe Simply Clean Litter box"

Linux Building RPMs for an older version of CentOS

Posted by Admin • Thursday, November 20. 2014 • Category: Linux
If you build RPMs on Centos 6.X (6.5 in my case) and then try to install them on Centos 5.X (5.10 in my case), bad things happen. Ironically, bad things happen even though my RPM contains a single jar file, and is thus entirely platform independent. Here is what I see:
Running rpm_check_debug
ERROR with rpm_check_debug vs depsolve:
rpmlib(FileDigests) is needed by my-rpm-1.0.x86_64
rpmlib(PayloadIsXz) is needed by my-rpm-1.0.x86_64
If you google for a solution, most people suggest running a virtual with Centos 5 just so you can build the RPMs, but this is apparently not necessary in this simple case, as you can simply specify a few flags. Basically, Centos 5 can't decompress the default archive format, and doesn't support the new digest algorithm.

Since I am using maven-rpm-plugin to build RPMs, my modifications look as follows:


                      <!-- skipping irrelevant items -->

                            <!-- don't strip jar files, it takes forever and is useless -->
                            <defineStatement>__os_install_post %{nil}</defineStatement>
                            <!-- for Centos6 -> Centos5 forwards compatibility -->
                            <defineStatement>_source_filedigest_algorithm md5</defineStatement>
                            <defineStatement>_binary_filedigest_algorithm md5</defineStatement>
                            <defineStatement>_source_payload w9.bzdio</defineStatement>
                            <defineStatement>_binary_payload w9.bzdio</defineStatement>

If you're using fpm or rpmbuild, you can just take these lines and make them %define's in your spec.

Linux Backing up cPanel 11 hosted account with wget and dav/rsync

Posted by Admin • Thursday, August 21. 2014 • Category: Linux
I want to back up my hosting account regularly by retrieving everything onto my box somewhere else (my home server). By regularly, I mean every day. I want this to happen automatically. cPanel makes that hard to do, but there is always a way to script things.

First, let's break down what we want to back up:
  1. Files
  2. Databases and email forwarders
Files are fairly easy. Obviously we don't want to use the full backup functionality of cPanel because we'd be transferring your entire storage space each and every time, even if nothing has changed. In order to do it efficiently, you have a few options:
  1. FTP account using recursive wget (create an FTP account with required access and teach an ftp client of your choice to recursively transfer everything. Hopefully this client skips unchanged files)
  2. WebDAV using rsync (this is what I'm using). Just mount, back up, unmount
Databases and Email stuff is not as easy, as we do have to log into cPanel. The trick to logging in to cPanel is:
  1. Submit your login to the log-in page and save cookies
  2. Parse the resulting file, find the backup link which includes your session name in the URL and hit that
  3. Only accept .gz files (DB backups and email stuff), but avoid hitting /logout, and don't start spidering the entire website
So, how do we do this? Here is the plan

Continue reading "Backing up cPanel 11 hosted account with wget and dav/rsync"

Linux mssh Bash completion

Posted by Admin • Friday, February 28. 2014 • Category: Linux
Clusterssh (cssh) is great, but I was getting a little fed up with unmanageable terminal windows. They either go all over my monitors or get lost, and they are hard to move and resize.

So I switched to mssh, which solves all that because all the terminals are in one window.... but, it doesn't read my /etc/clusters file! In fact, nobody seemed to even know what file it does read.

What to do? First of all, for the record, it reads its aliases (-a) from ~/.mssh_clusters This file is exactly like /etc/clusters, except for a colon, like so:
alias1: host host host
alias2: host host host

Now that we got that straight, let's make a bash completion file

Continue reading "mssh Bash completion "

Low Tech Hacks Importance of Small Engine Preventative Maintenance for seasonal tools

Posted by Admin • Thursday, February 13. 2014 • Category: Low Tech Hacks

Lawmowers, snowblowers, chainsaws, generators, trimmers (even motorcycles) ... I have all of these, but, obviously, they are used seasonally. That means that they spend a fair amount of time sitting around. While most preventative maintenance advice tends to focus on the maintenance of frequently used engines, I find that it is the stored machines that get overlooked. Therefore:

Preventing In-Storage Problems in just 2 steps

  1. Stabilize Your Fuel

    While people will tell you that gasoline doesn't usually break down, that's not true in my experience. After sitting around in your carburetor for a few months, the fuel does and will separate. The issue here is that one part of it will literally varnish the surfaces, either sticking moving parts together, or restricting flow. The result? Popping, backfiring, or an engine that will only run with the choke on (if it runs at all). The only solution is to take the carb apart and clean everything - perhaps even changing the seals and needle seat. In the ideal world you'd have no fuel in the carburetor at all during storage, but since you're unlikely to get it all out, this is the next best thing.

    In other words:
    • Simply add fuel stabilizer of your choice (eg Stabil) to gasoline the day you buy gasoline, and use only treated gas in seasonally used engines.
    • When you shut off the engine before storage (or any time), let it run out of fuel - either shut off the valve, or use up what's in the tank if you have no valve.

  2. End-of-season Oil Change

    Oil absorbs byproducts of combustion, which are acidic. The more the engine runs, the more acidic the oil becomes. This acidic medium, coupled with 6 months in storage means corrosion risk to all the lubricated engine surfaces.

    In other words:
    • Simply change the oil when the season is over - spring for snowblowers, fall for lawnmowers, etc.
      Warm up the engine, let it run out of fuel (see above), then change the oil. Don't start it again before storage.

That's it - you'll have years of reliable service from your engines. It's amazing how many people overlook these two steps that can and do prevent so many issues. I used a trashed lawnmower for 8 more seasons after I found it by following these steps. I've had my snowblower 12 years and it always starts on the first pull. Speaking of snowblowers, see also how to change oil without making a mess.

But wait, what about spark plugs? Air filters?

In my experience, on occasionally used engines, spark plugs never actually wear out. They can get fouled from improper carburetor adjustment or over-priming, but when the system is functioning properly - a good quality plug might outlast the engine. Go ahead and change it if it makes you feel better, but I've run my lawnmower weekly on the same plug for 8 seasons and it still looks and works just fine. As for air filters - that's another story, but their maintenance has nothing to do with seasonal use. Every once in a while it's good to check that the filter is clean enough to let some air through and that it's not torn so it's not letting dirt into the carb. Otherwise, I tend to leave them alone.

Linux Upgrading pfSense fails due to low disk space

Posted by Admin • Tuesday, October 22. 2013 • Category: Linux
I have an embedded installation of pfSense 2.0.2-RELEASE running from a 512MB compact flash card via an IDE adapter. At the time this was what I had on hand, and it seemed like plenty of space - but now that I tried to upgrade to 2.1, the process failed because there wasn't enough room to download the new image...

I tried all sorts of ways including both webconfigurator upgrade and trying to scp the image to the box, but either way there simply wasn't enough space. "OK" I said, "let me install 2.1 on a new (larger) card and restore the config.xml" .... bad idea. The firewall becomes unbootable when you do this - I think that no upgrade processing is performed on the configuration, it is assumed to be a match for your software version. Not to mention that I tried a 4GB card, but the old Pentium III running the firewall can't handle anything over 2GB!

Ultimately, this is the process:
  1. Download and install the same version as what you're running on the larger card
  2. Restore your config.xml backup onto the new instance of pfSense. You can do this through webconfigurator, or if you are (like me) running on a temporary rig without multiple NIC's, you can scp it over manually
  3. Confirm that the system boots, but don't make any changes (to the interfaces, for instance). Put the card into your real system
  4. Now you can use the auto-updater as normal, and your old config.xml will be upconverted

Linux amixer toggle mutes but does not unmute

Posted by Admin • Monday, July 22. 2013 • Category: Linux
It seems that the common approach to mute the audio in Ubuntu 13.04 is amixer -q sset Master toggle ... which works great when muting, but fails to unmute. This command is, for example, the default for openbox's handling of XF86AudioPlay shortcut.

The problem, it would appear, is that muting the Master channel causes other channels (eg Headphone and Front on my machine) to be muted as well. I have no idea why this works this way and why it doesn't undo what it did... and, frankly, I don't really care that much. It's much easier to code around this, see the following workaround script:

Continue reading "amixer toggle mutes but does not unmute"

Switchable Storage Locations in an Android App

Posted by Admin • Saturday, October 13. 2012 • Category: Android
This is also a typical pattern. In my app, I need to offer the user a choice of storage locations without getting too low-level. I am content with offering three options in a ListPreference:
  1. Private to application
  2. Public but on the main storage
  3. Public but on the removable storage (actual SD card)
Here is how I am doing it in a relatively generic and reusable manner (using an enum)

Continue reading "Switchable Storage Locations in an Android App"

Getting location of the removable storage on Android

Posted by Admin • Saturday, October 13. 2012 • Category: Android
Seems like a fairly typical problem, yet there are dozens of implementations and no standard. Here is mine:

        private static File s_removableStoragePath;

          Utility function that attempts to find the removable storage directory
(as opposed to {@link Environment#getExternalStorageDirectory()} which is usually non-removable)
          by running "mount" and parsing the output.   This implementation looks for
the strings "vfat" and "vold", eg:
          /dev/block/vold/179:97 /mnt/extSdCard vfat rw,dirsync,nosuid,nodev,noexec,noatime,nodiratime,uid=1000,gid=1023,fmask=0002,dmask=0002,allow_utime=0020,codepage=cp437,iocharset=iso8859-1,shortname=mixed,utf8,errors=remount-ro 0 0

          The result will be cached indefinitely.

         * @return either a valid file or null.  This may not be accessible, but it does exist

        public static File findRemovableStorage() {
                if (s_removableStoragePath != null && s_removableStoragePath.exists()) {
                        return s_removableStoragePath;
                s_removableStoragePath = null;
                String result = null;
                try {
                        Process process = new ProcessBuilder().command("mount")


                        InputStream is = process.getInputStream();
                        BufferedReader isr = new BufferedReader(new InputStreamReader(is));

                        String line = null;
                        while ((line = isr.readLine()) != null) {
                                if (-1 != line.indexOf("vold") && -1 != line.indexOf("vfat")) {
                                        String[] blocks = line.split("\\s");
                                        if (blocks.length > 2) {
                                                result = blocks[1];
                        if (result != null) {
                                s_removableStoragePath = new File(result);
                                if (!s_removableStoragePath.exists()) {
                                        s_removableStoragePath = null;
                } catch (Throwable t) {
                                        "Unable to find external mount point");
                return s_removableStoragePath;

Linux motion drops MySQL support, but you can get around it with a shell script

Posted by Admin • Monday, September 24. 2012 • Category: Linux
I use motion as a surveillance system for a sizable office (9 cameras). Motion is good. But the current package of motion in Ubuntu 12.04 is no longer compiled with MySQL support (used to have it in 10.04). Compiling motion from source really didn't sound appealing... but I do want to add events to a database for my own in-house motion history browser. What to do?

Motion supports command execution on events, such as on_movie_end and on_picture_save... which makes it really easy to do a one-liner like this:
on_movie_end bash -c "echo insert into security(camera, filename, frame, file_type, time_stamp, event_time_stamp, event_id) values('%t', '%f', '%q', '%n', '%Y-%m-%d %T', '%C', '%v') | mysql -uUSER -pPASS"
if you'd like to do something fancier, you can break it out into a shell script:


logger -t test "$0 executing with $*"
sql="insert into security(camera, filename, frame, file_type, time_stamp, event_time_stamp, event_id) values('$1', '$2', '$3', '$4', '$5', '$6', '$7')"
echo $sql | mysql -u$U -p$P motion
and invoke like this:
on_movie_end U=USER P=PASS bash /usr/local/sbin/motion-insert-event.sh '%t' '%f' '%q' '%n' '%Y-%m-%d %T' '%C' '%v

(same for on_picture_save, if needed). You can also daisy-chain commands with semicolon if you have multiple. Also, note that the query above is for my table, yours may have different columns. The rest are my specific examples of usage:

Continue reading "motion drops MySQL support, but you can get around it with a shell script"

Code and Hacks Converting .NET String.format pattern to Java with a regular expression

Posted by Admin • Monday, September 10. 2012 • Category: Code and Hacks
For this exercise in futility, I want to accomplish the following:

From .NET: "Hello, {0} Today is the {1} day"  
To JAVA: "Hello, {%1$s} Today is the {%2$s} day"

Note that a simple regex would work here if it weren't for the zero-based indexes. We need to increment the number as well do a pattern replace. Since I'm comfortable with PHP, I took a sample from preg_replace_callback description and modified it accordingly:

Continue reading "Converting .NET String.format pattern to Java with a regular expression"