Rooting a $20 tablet: Azpen A727

I just picked up a little 7″ tablet (really more like 6″) for $20 on a special. It normally sells for $50, which is still really damn cheap. It is a totally workable Android 4.2 tablet, definitely worth $50 if you want a tablet, or a little extra thing to kick around.

But being me, I of course must have root on every device I own. Turns out it was dead simple, really, it came rooted. Here’s how I installed Superuser on it so I can use apps that use root.

  1. first download this:
  2. unzip
  3. adb root (to run adb as root user on the emulator)
  4. adb remount
  5. adb push Superuser.apk /system/app/
  6. adb push armeabi/su /system/xbin/su
  7. adb shell chmod 6755 /system/xbin/su
  8. adb shell ln -s /system/xbin/su /system/bin/su
  9. wget
  10. adb push init.superuser.rc /

And just for good measure, I installed F-Droid as a system app, so I can have free software in a smooth app store experience:

Then go into the FDroid preferences, and check off “Install using system permissions”.

installing koush’s Superuser on an Android emulator

I want to install Superuser on an Android emulator so that I can have automated testing of our apps that use root access. So far, it has been a bit tricky, but I finally got it working.

Here’s how:

  1. first download this:
  2. unzip
  3. adb root (to run adb as root user on the emulator)
  4. adb remount
  5. adb push Superuser.apk /system/app/
  6. adb push armeabi/su /system/xbin/su
  7. adb shell chmod 6755 /system/xbin/su
  8. adb shell ln -s /system/xbin/su /system/bin/su
  9. adb shell "su --daemon &"
  10. adb shell rm /system/app/SdkSetup.apk

LilDebi seems to be running the install fine and created /data/debian, so it is working. I don’t know if this setup will survive a reboot of the emulator though.

simple automated backup to a USB disk on Debian/Ubuntu/Mint

I used to use Mac OS X as my main machine, indeed for 18 years since I started when it was called NeXTSTEP.  Now Apple has turned it into a paradise for mindless consumption, driving creators away.  Now I use Linux Mint as my daily system, but one thing I was missing was a really simple, automatic backup to an external USB disk.  Apple did a good job with Time Machine.  Its simple to setup, happens automatically and in the background, provides a directory structure that you can browse like any standard file system and see the complete snapshots of your computer at the time of a given backup.

So I looked at a lot of apps and tools for doing this on Mint.  I looked at Back In Time, simplebackup, pybackpack, obnam, BoxBackup, rsnapshot, and more.  Back In Time seemed promising, and I probably should have considered it more. Its totally GUI focused, and oriented towards backing up the home folder as a regular user. I want a full disk backup as root. Also, the UI hit me with a ton of options, and I didn’t want to think that much.  simplebackup just seemed like a simple way to make a single backup, with no snapshots. pybackpack is no longer developed.  obnam sounds very promising as a command line tool, but it does not represent the backups as transparent files on a filesystem, so that nixed it for me.  BoxBackup has a client/server architecture, so far too complicated.  I think its oriented towards backing up a fleet of servers to a single backup host.

rsnapshot ended up being quite simple to set up for a command line person like me, and also easy to automate.  And most importantly, it backs up easily to an external disk and writes out each snapshot as a full image of the system at the time of the snapshot.  Sold!  So I just needed to tweak it a little bit.

I had to make a couple edits to /etc/rsnapshot.conf.  One annoying detail to note in this config file is that you have to use tabs between the config items in a line, spaces won’t work. So first I pointed it to a folder on my external USB backup disk:

snapshot_root /media/ext4backup/palatschinken/

Then I told it not to create the root folder on the backup disk. If the backup disk is not present, it should not try to backup at all:

no_create_root 1

Then I enabled one fs mode (i.e. -x in rsync speak), which means that rsnapshot won’t cross the boundaries of a disk. So if you tell it to backup /, and you have /home on a separate partition or disk, it won’t backup /home. You could then add /home specifically, then it would be backed up. Here’s the option:

one_fs 1

Then I specified what I want backed up. These are each separate partitions on my system:

backup / root/
backup /media/share share/

Now the rsnapshot should happily backup. You can test it by running rsnapshot hourly.So the next step is to cron it so it runs automatically in the back ground. I made a script like this for each of the cron folders (/etc/cron.hourly, /etc/cron.daily, /etc/cron.weekly, /etc/cron.monthly):


# See ionice(1)
if [ -x /usr/bin/ionice ] &&
    /usr/bin/ionice -c3 true 2>/dev/null; then
    IONICE="/usr/bin/ionice -c3"

test -d /media/ext4backup/palatschinken && \
	nice $IONICE /usr/bin/rsnapshot hourly

Be sure to change the hourly in each script to match the folder its in. Adding in nice and ionice makes it run at very low CPU and disk priority, so you don’t notice that its running really. Lastly, if you are using this on a machine that is shutodwn or goes to sleep often, then you probably want to install anacron to make sure the daily, weekly, monthy jobs run (apt-get install anacron, it usually configures itself to do the right thing once its installed).

Converting MediaWiki to Redmine

We had a MediaWiki install for our wiki needs, and we used it lots and filled it up with good stuff. Then we got Redmine going. Redmine has its own integrated wiki, but with a different wiki syntax. We had all this lovely scribblings in our mediawiki, but we wanted the tasty redmine integration. So conversion must be done. Thanks to python, it was easy. Here’s the script I wrote:


from BeautifulSoup import BeautifulSoup
import io
import os
import re
import sys
import urllib2

# def download_inline_images(text):
#     m = re.findall()
#     for inline in m:
#         print('Downloading ' + inline)
#         image = urllib2.urlopen('' + inline)
#         with open(inline, 'w') as f:
#             f.write(

def convert_and_save(title, text):
    contents = 'h1. ' + re.sub('_', ' ', title) + '\n\n'
    contents += text

    # headers
    contents = re.sub('===== (.+?) =====', 'h5. \\1\n', contents)
    contents = re.sub('==== (.+?) ====', 'h4. \\1\n', contents)
    contents = re.sub('=== (.+?) ===', 'h3. \\1\n', contents)
    contents = re.sub('== (.+?) ==', 'h2. \\1\n', contents)
    contents = re.sub('= (.+?) =', 'h1. \\1\n', contents)

    # fix bullet lists
    contents = re.sub('\n\*\*([^ ])', '\n** \\1', contents)
    contents = re.sub('\n\*([^ \*])', '\n* \\1', contents)

    # bold - needs to be after 'fix bullet lists'
    contents = re.sub("'''(.+?)'''", '*\\1*', contents)

    # italic
    contents = re.sub("''(.+?)''", '_\\1_', contents)

    # external links with mistaken double brackets
    contents = re.sub('\[\[(http.+?) (.+?)\]\]', '"\\2":\\1', contents)

    # external links
    contents = re.sub('\[(http.+?) (.+?)\]', '"\\2":\\1', contents)

    # inline image links
    contents = re.sub('\[\[File:([^|\]]+).*?\]\]', '!\\1!', contents)

    # make sure there's a trailing newline
    contents += '\n'

    filename = re.sub('/', '_', title) + '.redmine'
    with, 'w', encoding='utf8') as f:

def crawl_url(url):
    page = urllib2.urlopen(url)
    soup = BeautifulSoup(page)
    find = soup.find('textarea')
    if not find or len(find) == 0:
    textarea = find.contents[0]

    m = re.match('.*title=([^&]+)', url)
    title =
    convert_and_save(title, textarea)

    m = re.findall('\[\[([^|\]]+)', textarea)
    for page in m:
        print 'Trying: ' + page
        if not os.path.exists(page + '.redmine') \
                and not page.startswith('File:') \
                and not page.startswith('http'):
            print 'MATCH: ' + page
            crawl_url('' + re.sub(' ', '_', page) + '&action=edit')

def main(argv):
    if len(argv) == 0:
        print "Usage: " + sys.argv[0] + " [URL]"
    if len(argv) == 1:

if __name__ == "__main__":

Cannot run program “/opt/android-sdk/platform-tools/aapt”

It seems that Google did a big reorg of the Android SDK file tree, and some tools still expect things in the old locations, like our Jenkins build server:

FATAL: Cannot run program "/opt/android-sdk/platform-tools/aapt" (in directory ": error=2, No such file or directory Cannot run program "/opt/android-sdk/platform-tools/aapt" (in directory ": error=2, No such file or directory
	at java.lang.ProcessBuilder.start(
	at hudson.Proc$LocalProc.(
	at hudson.Proc$LocalProc.(
	at hudson.Launcher$LocalLauncher.launch(
	at hudson.Launcher$ProcStarter.start(
	at hudson.Launcher$ProcStarter.join(
	at hudson.plugins.android_emulator.util.Utils.runAndroidTool(
	at hudson.plugins.android_emulator.util.Utils.runAndroidTool(
	at hudson.plugins.android_emulator.builder.AbstractBuilder.getPackageIdForApk(
	at hudson.plugins.android_emulator.builder.AbstractBuilder.uninstallApk(
	at hudson.plugins.android_emulator.InstallBuilder.perform(
	at hudson.tasks.BuildStepMonitor$1.perform(
	at hudson.model.AbstractBuild$AbstractBuildExecution.perform(
	at hudson.model.Build$
	at hudson.model.Build$BuildExecution.doRun(
	at hudson.model.AbstractBuild$
	at hudson.model.Run.execute(
	at hudson.model.ResourceController.execute(
Caused by: error=2, No such file or directory
	at java.lang.UNIXProcess.(
	at java.lang.ProcessImpl.start(
	at java.lang.ProcessBuilder.start(
	... 19 more

Luckily there are symlinks! This fixed it for me:

cd /opt/android-sdk
ln -s 18.0.1 build-tools/current
ln -s ../build-tools/current/aapt platform-tools/aapt
ln -s ../build-tools/current/lib platform-tools/lib

Then whenever the build-tools get updated, just the symlink in /opt/android-sdk/build-tools/current needs to be updated. This is a little improvement on the solution found here.

sign and encrypt while unencrypting using gnupg

I have a file that was encrypted to my OpenPGP key and I want to use gpg to decrypt then encrypt to someone else’s OpenPGP key while signing it with my key. After reading some man pages and searching the internet, I figured this out:

gpg --decrypt secrets.gpg | gpg -o signed-secrets.gpg  --encrypt --sign -r

Then to make this file full paranoid, I’m also going to use symmetric encryption with a long generated passphrase. Here’s how I did that:

pwgen=`pwgen 32 1` && echo $pwgen && echo $pwgen | gpg --batch --passphrase-fd 0 --symmetric signed-secrets.gpg; unset pwgen

Running a Jenkins Build slave in MinGW MSYS bash

I’m a UNIX guy but I have to build some of the software that I work on for Windows. Luckily there are lovely projects like Cygwin and MinGW for people like me. I’m using Jenkins to automate a lot of builds for Debian, Ubuntu, Mac OS X, and Windows, so I wanted the Jenkins slave on Windows to have a UNIX shell like all the rest of the slaves. Normally when you install the Jenkins slave as a service, it is running in a Windows environment by default, just like any other Windows service. The jenkins slave can be manually started in the MinGW MSYS bash shell, then the jenkins slave will be running jobs in the MinGW MSYS environment.

Luckily, there is an .XML config file for the Jenkins slave, and it can be configured to run the jenkins slave to run in MinGW MSYS bash. Here’s how I did it:

  1. follow the instructions for Install Slave as a Windows Service
  2. in cmd.exe run jenkins-slave.exe uninstall to uninstall the service
  3. edit these two lines jenkins-slave.xml to look like this:

    <arguments>-c "java -Xrs -jar /c/jenkins-build-path/slave.jar -jnlpUrl https://my.jenkins.server/computer/my.jenkins.slave/slave-agent.jnlp"</arguments>
  4. in cmd.exe run jenkins-slave.exe install to re-install the service

Then I can start it and ultimately have Jekins running in a nice, UNIXy, MinGW MSYS bash shell!


Get every new post delivered to your Inbox.