Monday, February 24, 2014

Convert Unix man page to PDF

Thanks to this post by Aron at True EDGE, I learned how to convert a Unix man page to a PDF file:

man -t bash | ps2pdf - bash.pdf

Combined with my iPad, and the GoodReader app (in which I can sync a subdirectory from my Dropbox), and I can save man pages for reading later in a more comfortable setting.

Saturday, February 22, 2014

Changing the Lock Screen Wallpaper in Lubuntu

I noticed that after changing my desktop wallpaper in Lubuntu 13.10 "saucy" (right click on the desktop and choose Desktop Preferences, Appearance tab), that the image on my lock screen had not changed. I don't love that wallpaper, so I wanted to change it. After searching the web and not finding a way to do so, I tried a few things and finally ran a locate for "wallpaper" finding, among other things, these files:

dave@tyrant:~$ locate wallpaper
(output has been truncated to display only relevant files)
Listing the contents of that directory gave me a little more insight:
dave@tyrant:~$ cd /usr/share/lubuntu/wallpapers
dave@tyrant:/usr/share/lubuntu/wallpapers$ ls -l
total 5260
-rw-r--r-- 1 root root  890508 Sep 19 11:57 1310-A_Winter_Magic_by_Luciash_D-Be
-rw-r--r-- 1 root root  585583 Sep 19 14:41 1310-lubuntu-default-wallpaper.png
-rw-r--r-- 1 root root  370825 Sep 19 11:57 1310-Moody_by_Robert_Wicek.jpg
-rw-r--r-- 1 root root  461580 Sep 19 11:57 1310-Muelle_by_Manuel_Puentes.jpg
-rw-r--r-- 1 root root  835281 Sep 19 11:57 1310-Smolikas_by_George_Blades_Voul
-rw-r--r-- 1 root root 2175372 Sep 19 11:57 1310-Two_Jack_Lake_by_C_Ayers.jpg
lrwxrwxrwx 1 root root      29 Feb 11 12:49 lubuntu-default-wallpaper.jpg ->
lrwxrwxrwx 1 root root      12 Feb 22 10:54 lubuntu-default-wallpaper.png ->
So, I copied the picture I wanted as my wallpaper into this directory and re-established what seemed to be the operative link:
dave@tyrant:/usr/share/lubuntu/wallpapers$ sudo su - # be careful after this as
we are now root
root@tyrant:~# cd /usr/share/lubuntu/wallpapers
root@tyrant:/usr/share/lubuntu/wallpapers# cp /home/dave/unnecessarily_long_web
_name.jpg ./nifty_colors.jpg
root@tyrant:/usr/share/lubuntu/wallpapers# ln -fs ./nifty_colors.jpg ./lubuntu-
root@tyrant:/usr/share/lubuntu/wallpapers# exit
And it worked! Using this method I have a different wallpaper on my desktop than on my lock screen, which is also cool.

Monday, January 27, 2014

Confusion Between Target Host and Local Host in SSH Tunnel

I was trying to tunnel VNC connections over SSH and I encountered a problem caused by a mistake I always make, setting up the SSH tunnel on the host that's going to VNC-view the target host:

$ ssh -f user@target_host -L 5905:target_host:5901 -N

I try to connect via this tunnel and I see:

channel 2: open failed: connect failed: Connection refused

I should have created the tunnel like this:

$ ssh -f user@target_host -L 5905: -N

Then, VNC-ing to port 5905 on works.

Tuesday, December 10, 2013


An "xword" is part of a system I created to automate some of the routine things in my life. It is somewhat similar in function to a hashtag, in that it categorizes text without being part of the text itself.

To follow my examples, it helps to know that I use the iPhone app Captio (which I recommend) to send myself emails from my phone. It will send every email you create to the same address that you specify in the settings.  This means that to send yourself a simple reminder note, open the app, type just your text (for example: "wash car") and press send. It's handy if you email yourself a lot with reminders and notes. It also allows you to attach a photo.

In my case, the complete message to myself would be:

xdo wash car

"xdo" is an xword I use to tell my email account to forward the item to my Remember the Milk (also recommended and free) account as a To Do item. In Gmail, this can be accomplished by creating a filter.

Now, why an xword and not a normal word or even a word combination like "todo?" Because that might appear in normal text, say, something someone else might send you. If it does, the filter will act upon that email and you'll need to deal with it each time. An xword that you create should not be a real word. This way, the chances of it appearing in text where you did not intend it to be are slim.

Why not a hashtag or symbol of some sort? I want my solution to be "application-agnostic" meaning I don't want it to work in some places and not others. Software is inconsistent at dealing with and parsing words containing symbols, so to keep it simple, I want to use only alphanumerics.

Another thing I like to track is when I have finished a book. That message will look like this:

xred Peopleware: Productive Projects and Teams by Tom DeMarco and Timothy Lister

This triggers a separate filter which tags the email and archives it after forwarding it to my Evernote account.

xwords are also a great way to "tag" something that doesn't allow for tags. The "tags" can be used anywhere. You could rename a series of photos to end with "_xhivn" so that "dave_hiking.jpg" becomes "dave_hiking_xhivn.jpg" so that you can search for files containing "_xhivn" on your computer to find all of those files which relate to your Hawaii vacation (photos, maps, receipts, etc.)

You can also use multiple xwords, just like tags:

xdo xhivn get plane tickets

As a final thought, it's best not to tell people your actual xwords and the email account in which you use them, for obvious reasons. ;)

Sunday, December 18, 2011

Shortcut Shell Script

I have a shell script I use to simplify my life on Unix systems. This is the "Git" version, saved as $HOME/bin/g:


usage() {
      sed -n '/^  *[a-zA-Z0-9][a-zA-Z0-9]*)/ {
s?^\(  *[a-zA-Z0-9][a-zA-Z0-9]*\)) *cmd="\(.*\)" *;;?\1  \2?
}' $PROG >&2


case $lookup in
   a) cmd="git add $*" ;;
   b) cmd="git branch" ;;
   c) cmd="git commit -m \"$*\"" ;;
   d) cmd="git checkout develop $*" ;;
   m) cmd="git checkout master $*" ;;
  pd) cmd="git push origin develop $*" ;;
  pm) cmd="git push origin master $*" ;;
   r) cmd="git checkout -b $*" ;;
   s) cmd="git status -s $*" ;;
  "") usage;;
   *) echo "Not valid." >&2

echo $cmd >&2
eval $cmd
Ideally, any frequently executed commands take only a few keystrokes (not counting any parameters that need to be passed):

~/dev/TuSC (develop)$ g s

git status -s
M  tusc.ahk
?? tusc.ahk.bak

~/dev/TuSC (develop)$ g c fixed multi-lock bug plus usage comments for GoApp

git commit -m "fixed multi-lock bug plus usage comments for GoApp"
[develop 1a39e4d] fixed multi-lock bug plus usage comments for GoApp
 1 files changed, 67 insertions(+), 23 deletions(-)

~/dev/TuSC (develop)$ g pd

git push origin develop
Counting objects: 5, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 1.26 KiB, done.
Total 3 (delta 2), reused 0 (delta 0)
   ede7073..1a39e4d  develop -> develop

~/dev/TuSC (develop)$

Some people use aliases or functions for this kind of thing, but I like my little script because I feel like it's more portable and also has a built-in command list which I can access just by typing the command key by itself (plus Enter, of course):

~/dev/TuSC (develop)$ g
   a  git add $*
   b  git branch
   c  git commit -m \"$*\"
   d  git checkout develop $*
   m  git checkout master $*
  pd  git push origin develop $*
  pm  git push origin master $*
   r  git checkout -b $*
   s  git status -s $*

~/dev/TuSC (develop)$

Sunday, June 19, 2011

Keep a Debug Journal

Whether just starting a new job, or entering a long period of heads-down coding, something I have found helpful from time to time is keeping a "Debug Journal". This is another tool in your arsenal to solve problems. You may have spent an hour or twelve today struggling with an issue. In addition to making notes about what the fix is for that item so that you have it handy in case it reappears elsewhere, you can also look at the big picture and infer lessons which apply to a larger subset of problems. An entry from my own journal follows:
Problem: CGI Form submit, script is executed twice

Answer: HTML Validator plug-in for Firefox was making additional requests. I checked the Changelog and they were aware of the bug and it had been fixed. After I upgraded, everything was fine

Lesson: Involving another pair of eyes and another brain helped me greatly. For one thing, the other person involved wasn't experiencing the error, even when hitting my code. That got us to thinking about the browser. Then he sent me a link regarding the plug-in and voilĂ . Also, again I made a bad assumption - that there was a problem with the server, and not the client. I need to start asking myself what I am assuming after being frustrated by a problem. I also need to involve other people sooner.

Having another person look at the problem may seem like an obvious step, but one thing I want to stress is that this journal can help alert you to your own blind spots. I'm not sure if it was vanity or stubbornness but it took me too long to ask for help in this case. This is something I can work on during future troubleshooting. It is also a good example of drawing a larger lesson from a specific fix. Here is another:
Problem: Does the user have sufficient privileges to see this particular control and perform its function? Code is not always very friendly in looking this sort of thing up.

Answer: In dev, I hard-coded the username to be her if it was initially me:

if($ENV{REMOTE_USER} eq 'david') {
$ENV{REMOTE_USER} = 'betsy';
then I could see the screen from her point of view and saw that she did indeed have access.

Lesson: If the initial problem seems difficult, it might be worth a few minutes trying to figure out if there's an easier way to determine the answer.

In our software, we do have a feature that allows us to take the role of another user. However, the view is artificial and there are some constraints on the way it works, so it was not really useful to me in this case. The point here again is the larger picture: the privilege system is somewhat complex, both in code and in data. Hard coding the login was much faster and easier than the alternatives I was facing. Also, in this case, I ended up keeping this snippet handy and using it quite frequently to diagnose user issues. Another entry:
Problem: device_id's were showing up as 1 or 2 or 8 etc (expecting 5762,12428,etc).

Answer: That's what they freakin' were.

Lesson: Just because something might look a little weird, doesn't mean it isn't so. Check the source of the data first and see if it is correct before considering it a problem.

This one was a funny one. Since there are a significant number of devices being tracked by our system, I was used to seeing DEVICE_IDs in the database of say, "5728" or "14972". My brain did not want to accept a single-digit DEVICE_ID. It just so happened I was working with devices that were entered very early in the lifetime of the system. When I looked them up explicitly, they indeed had single-digit identifiers.

Assumptions are a major blind spot for me (and many other people, I suspect). Assumptions are difficult, since we can't really move forward diagnosing any problem without making some assumptions. We just have to try to keep combing through our minds for the bad or unnecessary ones.

And that's the gist of it. If nothing else, keeping a journal can be reenforcement of your experience, helping you to remember without even needing to look it up. But you can look it up if you need to! Several more entries follow without additional comment:

Problem: I keep saying "verified" is set to 1, so why isn't this code running?

Answer: Because that's not the only requirement to get to that code. "Submit" also needs to be set (case-sensitive), but instead, I have set "submit" (lower-case "s").

Fix: copied & pasted output of Data::Dumper of $qs right beside code, so I could compare what was being sent with how it was being tested

Lesson: Also, I was getting sleepy. That makes for bad debugging. Walk around a bit and come back to it.

Problem: alert() isn't working

Answer: the JavaScript command is there, the problem is that the JavaScript command preceding it causes an error. It was a focus() method called on a control that doesn't exist.

Lesson: If a JavaScript command isn't working, you should check the JavaScript console and also the HTML source. Furthermore, if any component of a program isn't working, check THAT component. No sense in adding debug code to a Perl script when the JavaScript is failing (at least as a first resort).

Problem: Script is plugging SYSDATE into records when I have a fixed date I want in there. At the end of the script, I changed the way the script works so that, at the end of processing, I plug the same date into whatever records' AS_OF_DATE field is NULL.

Answer: The problem is that when I changed the script to do this, I never went back and took out SYSDATE so that the records' AS_OF_DATE fields were NULL.

Lesson: I solved this one pretty quickly, but I'm not sure what I could have done differently. There were two parts of the script that needed to be changed and I only changed one part. I guess when I see that setting (foo to bar if foo is baz) isn't working, I should make sure I have set foo to baz first. That seems to be a common theme in slow debugging - assumptions. When you assume...

Monday, June 13, 2011

Hard Drive Paranoia

Brought my laptop running XP out of hibernation mode today and tried to start Firefox, and got some weird error about it not being able to start due to a missing file.  Tried to start something else and started seeing weirdness in other apps as well and decided to reboot.  Upon reboot, CHKDSK automatically ran and found (and seemingly fixed) a TON of errors.

I went to the Event Viewer (Start->Control Panel->Administrative Tools->Event Viewer) and could only find this error "The system failed to flush data to the transaction log. Corruption may occur." which, after a quick Google check led me to believe it was only related to pulling out my USB drive.

I felt compelled to thoroughly check the drive before I put any more serious work onto this laptop only to risk losing it.

My idea was to copy a lot of data and then run a comparison.  First, I ran JDiskReport.  It's a great tool for identifying what's taking up space on your hard drive.  Using this, I located a local directory taking up about 25G.

I copied this directory to c:\testA

Then, though it might have been overkill, I copied it to c:\testB

Then I used another tool I'm liking: FreeFileSync.  This directory sync tool has a setting allowing you to compare file contents instead of just sizes and timestamps.

It took about an hour, but I compared c:\testA to c:\testB.  FreeFileSync reported no file differences.

Then, just to be safe I compared the original source directory to c:\testA.  Again, FreeFileSync reported no file differences.

Since I had done a lot of writing and reading of the hard drive I checked the Event Viewer again and found nothing scary.

From a command prompt (Start->Run->CMD[Enter]), I ran chkdsk c: .  It did do a few more fixes but it also gave me a message to the effect of "This does not indicate disk corruption".  Still, I was a little concerned, so

I ran chkdsk /f c: .  The /F switch tells CHKDSK to fix errors on the disk if found.  It can't run while I'm in Windows so it asks if I want to schedule it for the next boot and I answer Y.  Then I reboot.

It ran successfully, but just to be extra safe I ran chkdsk /r c: .  The /R switch tells CHKDSK to scan the entire disk for bad sectors.  Again, it can't run while I'm in Windows so it asks if I want to schedule it for the next boot and I answer Y.  Then I reboot.

It took a long time to run (maybe a couple hours), but it ran successfully.

Checked the Event Viewer again and this time I am seeing a couple entries from yesterday in the System category which seem to indicate a problem occurred:

Warning: "An error was detected on device \Device\Harddisk0\D during a paging operation."

Error: "A parity error was detected on \Device\Ide\iaStor0."

Still nothing from today, though.  Guess I will keep an eye on it.