Troubleshooters.Com Presents

Linux Productivity Magazine

February 2008

AAuSE, Take 2

Copyright (C) 2008 by Steve Litt. All rights reserved. Materials from guest authors copyrighted by them and licensed for perpetual use to Linux Productivity Magazine. All rights reserved to the copyright holder, except for items specifically marked otherwise (certain free software source code, GNU/GPL, etc.). All material herein provided "As-Is". User assumes all risk and responsibility for any outcome.

Recession Busters:
Twenty Eight Tales of Troubleshooting
Troubleshooting: Just the Facts
Manager's Guide to Technical Troubleshooting
Troubleshooting Techniques of the Successful Technologist

[ Troubleshooters.Com | Back Issues |Troubleshooting Professional Magazine ]



Build a Prototype as Soon as Possible.  --  Mike Gancarz, From "Linux and the Unix Philosophy", ISBN 1555582737 

CONTENTS

Editor's Desk

By Steve Litt
This issue of Linux Productivity Magazine has taken on a life of its own. It started as a quick brag about my second picker equipped AAuSE (Application Assembly using Small Executables) application, a midi player. But the more I bragged, the more I explored, and the more I explored, the more I improved the midi player. I actually wrote three completely different versions of it:
  1. A Ruby-core centralized version
  2. A decentralized shellscript version
  3. A shellscript-core centralized version
The more I programmed, the more I learned. The more I learned, the more I wrote. The more I wrote, the more I experimented. The more I experimented, the more I programmed.

Articles were added. As you read this mag, which was written over a period of maybe a week and a half, you'll see it change from beginning to end. It changes in knowledge, in focus, in attitude, as I slowly discovered that yes, AAuSE has real possibilities. By the end, it's less about my midi player and more about a genuine love of Unix, and for the first time understanding the Unix old-timers who, until now, seemed so strange to me.

And now I appreciate Linux a lot more. No longer is Linux just a robust, powerful, secure and inexpensive operating system bundled with software that would cost thousands in the Windows world. Now I love Linux for its Unix underpinnings, and what those underpinnings allow me to do.

There are two ways to look at this LPM issue. One is to call it unfocused and meandering. The other is to call it a journey, from a quick brag about a text mod midi player to a deep appreciation of The Unix Philosophy. Either way, if you want to see and learn some advanced shellscripting, this is the where you need to be.

So join me on this winding journey through AAuSE, shellscripting signals and interprocess communication. And remember, if you use GNU/Linux, or BSD, or Unix or a Unix workalike, this is your magazine.
Steve Litt is the author of Troubleshooting Techniques of the Successful Technologist.   Steve can be reached at his email address.

The Challenge

By Steve Litt
Author's Note

This article was written when I understood much less about shellscripting. As a result, some of my assertions, such as the article's assertion that finding a process ID isn't quite as easy as it might seem, aren't true. Much of this article is contradicted later in the magazine. That's OK, as I said, this was a journey, and be assured that whatever the article says, the code in this article does work quite nicely.

How hard can it be to write a front end for timidity? I mean really, I already had one for mplayer, so couldn't mplayer just be replaced with timidity? After a minute's thought, the answer is obviously no. mplayer has a built in facility for handling playlists. My .ogg my front end offloaded playlist handling to mplayer. Because timidity on my Mandriva distribution has no such built in facility (that I know of) compiled into the executable, I'd have to handle playlist functions with my own code.

Big deal! For the most part, this pseudocode does the job:
forever
	foreach line in the playlist file
		timcommand = "timidity " + song_filename
		system(timcommand)
The preceding is wonderful with one exception. The user must interact with the playing. The user might want to go to the next or previous song, start this one over, quit, or jump to a specific song. So the program must continuously read the incoming command FIFO, and do the bidding of the commands coming in through that FIFO. Still no problem:
forever
	check FIFO and read if necessary
	if FIFO contains command
		if command = quit
			break
		else
			adjust playlist subscript according to command
		endif
	else
		increment playlist subscript, or set back to 1 if already on last song
	endif
	play song pointed to playlist subscript
That was easy, wasn't it! Can you see the problem?

Let's say you want to skip the rest of the boring song now playing. You're one minute into this 10 minute song. So you stuff a "next" command in the FIFO. Trouble is, the algorithm won't look at the command FIFO until the song finishes. You want a much more immediate response, which requires some sort of multitasking.

There are probably a million ways to accomplish this multitasking, though I can think of only two categories. I'd imagine threads would be one category, but I'm no thread expert, so I didn't even try that.

Independent communicating processes is the other category that comes to mind. When the user issues a "next" command in minute 1 of a 10 minute song, the controlling process sends a HUP signal to the timidity process, and then the front end program spawns a new timidity process with the desired song. Trouble is, now that the controller no longer waits for timidity to finish, there must be a way for the controlling program to find out when timidity finishes, so that if no command comes though the FIFO, the next song can be played at the conclusion of the current one. No matter what the mechanism, there must be a way to find the process id of the timidity program. That's not quite as easy as it might seem.

A bash script process can tell its own process id through the $$ environment variable. A program under your control can usually report its process ID one way or another. Trouble is, timidity isn't programmed to report its process id. One possibility is to have a process, whose process id is known, exec timidity. In that situation, the exec'ing process is replaced by timidity. The downside is that any code, in the execing program, that is used to notify the controlling program that timidity has finished, is overwritten by timidity.

Theres a way not involving exec. The ps command can be formatted in process tree format to capture the child of the current program. For instance, if parent.sh runs child.sh in the background, and child.sh first writes its pid ($$) to a file called child.pid and runs timidity in the foreground and then finally writes "finished" to the fifo of parent.sh, then parent.sh can find the pid of timidity with the following command:
childpid=$(cat ./child.pid)
timpid=$(ps -aH | grep -A1 ^[[:space:]]*$childpid | tail -n1 | cut -b1-5)
The following scripts provide a proof of concept for this way a shellscript can identify the PID used by timidity:
parent.sh child.sh
#!/bin/bash
echo $$ > parent.pid
./child.sh &
sleep 1
childpid=$(cat ./child.pid)
timpid=$(ps -aH | grep -A1 ^[[:space:]]*$childpid | tail -n1 | cut -b1-5)
echo child=$childpid
echo timidity=$timpid
echo
ps ax | tail -n8

echo
echo -n 'Kill it now? (y or n)==>'
read killflag
if test "$killflag" = "y"; then
	kill $timpid
	kill $childpid
fi
   
#!/bin/bash
echo $$ > child.pid
/usr/bin/timidity test.mid
echo finished > /tmp/steve.fifo
echo FINISHED FINISHED FINISHED

In the preceding, child.sh is simplest so let's review it. It first records its process ID (pid) in file child.pid. Then it runs timidity with song test.mid. Finally, if it's allowed to finish the song without getting killed, it writes to /tmp/steve.fifo and to stdout. The latter is a diagnostic so you know it got that far.

Now let's go over parent.sh. It first records its pid, although that's probably not necessary. It then runs child.sh in the background, meaning that both programs execute simultaneously, and that parent does not wait for the child to complete. The parent then waits 1 second to give the child time to load and write its pid. This might not be necessary; on my machine it's not, but yours might need the extra time. The parent now gets the pid of the child from the pid file left by the child. Armed with the child's pid, it searches a ps -aH command for the command after the child. Because the child ran only one subprocess, timidity, the command after the child must be that for timidity. The cut command at the end of the pipeline simply trims off everything but the pid of that line. So now $timpid contains the pid of the timidity process.

Next it writes the child and timidity pids and then writes the tail of a ps ax command to prove those numbers are right. Last but not least, it asks you if you want to kill the child and timidity now, and if so it kills them. If not it terminates, letting timidity complete, after which the child writes its message and then completes. In other words, you see the "finished" message if it completes, but not if you kill it. If you need to see the "finished" message either way, just comment out the line that kills the child.

If I'd used that method, parent.sh would have read the FIFO, and when it saw "finished" in the FIFO it would have played the next song. If it had first seen "next" in the FIFO, it would have killed the child and timidity, and then run the child with the next song in the playlist (obviously child.sh would have needed modification to accept the song as a command line argument).

It's pretty slick and fairly clean. If I'd thought of it two days ago I'd have used it. Instead I went for an all Ruby solution. But before describing the Ruby solution, let's look at some roads not taken. Read on...

Polling

Maybe instead of timidity or the program that execs it notifying the controlling program, the controlling program could continuously poll for the existance of the pid for timidity. It's as simple as looking for the pid in the /proc directory.

But polling is ugly. How do you time it? Which do you prefer, delays or CPU consumption? Plus the fact, I was already polling the FIFO, but was able to put that polling off in the getNextLine() subroutine. If two polling algorithms were required, the entire program structure would need to be changed so that polling is at its center, and all other activities are controlled by the polling. It's a less obvious algorithm. Polling probably would have ended up a simpler solution, and its performance probably would have been adequate, but I chose not to poll.

Which meant there must be a way for timidity or its execing or calling program to inform the controlling program when timidity terminates. The two ways I can think of is by interrupt/signal, or by writing "finished" to the FIFO. The latter is so simple that's what I did.

Threads

Ruby has threads. From what I understand, this situation is what threads are made for. But I'm not a thread expert, so I didn't even try. If you can rewrite this using threads in Ruby, please let me know.

The Ruby Solution

I used fork twice in Ruby. Ruby's fork works just like any other languages -- it clones the program. When it returns in the original program it returns with the pid of the clone. When it returns in the clone it returns 0.

Like I said, it clones your program. The fork command cannot run a different executable. You know, like timidity. To run a separate program you need to use one of these techniques:
The IO.popen() and IO.pipe() methods look very promising, but if there's one thing I've learned over the past two days, interprocess communication is not without its time consuming surprises, so I limited my search to system() and exec(). Ruby's system() command does not return a pid. If it did, the problem would have been trivial. If I'd remembered the method of using the current process pid to find the child using the ps-aH command, it would have been much easier. Yesterday I could not figure a way to find the pid of a process created with system(), so I used exec() instead.

The exec("timidity test.mid") command is cool because it replaces the current process with the "timidity test.mid". Since you know the pid of the current process, you know the pid of the replaced process. Unfortunately, everything about the process is replaced -- all code after the exec() call, and even the at_exit() code, so there's no way the main program can be informed when the song finishes.

So I worked it like this: From the main process I forked off a subprocess (call it the child), and the child forked off an additional subprocess (call it the grandchild). The grandchild replaces itself with timidity via an exec() call, so it has the same pid as passed back to the child by the fork() call. Back to the child -- after forking the grandchild, the child then issues a Process.wait() that doesn't return until the grandchild terminates. Once Process.wait() returns after the grandchild terminates, the child writes "finished" to the fifo, which lets the main process know the song is done. Interestingly, the child process needs to explicitly kill itself with Process.exit(0) upon its completion, or it hangs around forever.

#!/usr/bin/ruby
def play()
	pid = fork
	if pid == nil
		### THIS IS THE CHILD
		pid2 = fork
		if pid2 == nil
			### THIS IS THE GRANDCHILD
			### REPLACE IT WITH MIDI PLAYER
			Kernel.exec("timidity", @filename.to_s)
		else
			### THE CHILD
			### ITS PURPOSE IS TO
			### FORK OFF TIMIDITY AND
			### WRITE finished TO FIFO
			### WHEN SONG IS FINISHED
			print "dia pid grandchild=" + pid2.to_s + "\n"
			pid2file = File.new("/tmp/pid2.pid", "w");
			pid2file.puts(pid2.to_s);
			pid2file.close()
			@returnstring = "finished"
			Process.wait(pid2)

			### RETURN PROPER STRING THRU FIFO
			writebackfile = File.new("/d/bats/littmidi.fifo", "w");
			writebackfile.puts(returnstring);
			writebackfile.close()
			puts "dia returnstring is #{returnstring}"
			Process.exit(0)
		end
	else
		### THIS IS THE PARENT
		pidfile = File.new("/tmp/pid.pid", "w");
		pidfile.write(pid.to_s)
		pidfile.close()
		Process.detach(pid)
		puts "dia pid child=#{pid}"
	end
end

Note that the parent section writes the child's pid to pid.pid, and the child section writes the grandchild's pid to pid2.pid. The superkill() method (discussion following) reads those files in order find the pids whose processes need killing.

If the main process needs to kill the child and grandchild, it calls the superkill() method via the kill() method. The reason for the two level kill is to avoid complex state calculations, the only thing you can do to a paused song is unpause it, after which your next command will work on it. Here's the kill code:
def superkill
	pidfile = File.new("/tmp/pid.pid", "r");
	pid = pidfile.readline().to_i;
	pidfile.close()
	Process.kill("HUP", pid)

	pidfile = File.new("/tmp/pid2.pid", "r");
	pid2 = pidfile.readline().to_i;
	pidfile.close()
	Process.kill("HUP", pid2)
end

def kill
	self.superkill unless @paused
end

Delays must be added in order to prevent a very rapid succession of commands from aborting the program. Here's the getNextLine() function:

def getNextLine(infile)
	while infile.eof
		sleep(0.5)
	end
	line = infile.gets
	line.chomp!
	sleep(0.5) 
	return line
end

The sleep() inside the loop prevents the program from consuming all CPU. It checks every half second for new input from the FIFO. The sleep() at the bottom slows down repeated calls to getNextLine(). Otherwise, if a user pressed next several times fast, input would come at the main routine quicker than it can run processes, and the program would abort. Throttling the output of getNextLine() like this doesn't drop any info -- it stays in the fifo until subsequent reads. Thus if you pressed next 20 times in 3 seconds, those 20 nexts would take 10 seconds (from the first press) to complete, but at the end of the 10 seconds you'd have advanced 20 songs.

Writing a midi-player that uses mplayer at first seems like a simple task, but as you can see, the devil is in the details. Want to see how it's done? Read on...
Steve Litt is the author of the Troubleshooting: Just the Facts. Steve can be reached at his email address.

My timidity (Ruby) Front End Program

By Steve Litt
As discussed previously, timidity doesn't have facilities to handle playlists, my software had to handle playlists and then run single songs on timidity. Here's a high level diagram of the front end and the way it meshes with aumix and timidity:
Block diagram of the front end   Block diagram of my timidity front end. Major components are UMENU menu and the frontmidi.rb program, which handles playlist functions and interprocess communication between user commands coming through the FIFO, the playlist, and the processes running timidity. Other significant components are the filepicker and recordpicker, aumix and timidity.

frontmidi.rb's sole connection to the user is the commands coming through the fifo. For debugging purposes the programmer can input such commands directly by redirecting text to the fifo.

UMENU is responsible for converting user menu choices to commands useable by frontmidi.rb, and sending them down the FIFO. The song object also writes the FIFO on completion of a song it writes "finished" to the FIFO.

The preceding described the system. The following is the code for frontmidi.rb:

#!/usr/bin/ruby -w

# =======================================================================
#
# frontmidi.rb, version 0.1.0, copyright (C) 2008 by Steve Litt
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer. 
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# 3. The name of the author may not be used to endorse or promote products
# derived from this software without specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# =======================================================================
#
# frontmidi.rb, version 0.1.0 1/30/2008
# This version is somewhat unstable and has been only lightly tested.
# BUGS:
# * Throws useless error messages on uncaught SIGHUP signal to child process
# * Aborts if quickly repeated next commands go past an 
# unplayable song
# * The fifo file is housed in a directory you might not have. Either
# change all instances of the fifo file (and change them in UMENU), 
# or create /d/bats/littmidi.fifo.
#
# =======================================================================

def fifo()
	"/d/bats/littmidi.fifo"
end

class Playlist
	attr_accessor :paused
	attr_reader :fname
	attr_reader :songs
	attr_reader :numsongs
	attr_accessor :songnumber
	attr_reader :songname
	def initialize(fname)
		paused = false
		@fname = fname
		@songs = []
		@numsongs = 0
		playlistfile = File.new(fname, "r");
		playlistfile.each do
			|line|
			line.chomp!
			skip = 1
			if line =~ /\.mid\s*$/
				skip = 0
			end
			if line =~ /^\s*$/
				skip = 1
			end
			if line =~ /^\s*\#/
				skip = 1
			end
			if skip == 0
				@songs.push([line])
				@numsongs += 1
			end
		end
		playlistfile.close()
		@songnumber = 1
		@songname = @songs[@songnumber-1]
	end

	def set(num)
		if(num < 1)
			@songnumber=1
		elsif(num > @numsongs)
			@songnumber = @numsongs
		else
			@songnumber = num
		end
		@songname = @songs[@songnumber-1]
	end

	def next
		@songnumber += 1
		if @songnumber > @numsongs
			@songnumber = 1
		end
		@songname = @songs[@songnumber-1]
	end

	def prev
		@songnumber -= 1
		if @songnumber < 1
			@songnumber = @numsongs
		end
		@songname = @songs[@songnumber-1]
	end

	def write2stdout()
		puts "There are #{@numsongs.to_s} songs in this playlist:"
		ss1 = 1
		@songs.each do
			|song|
			puts "#{ss1.to_s}: #{song}"
			ss1 += 1
		end
	end
end

class Midisong
	attr_reader :returnstring
	attr_reader :filename
	attr_accessor :paused
	def initialize(filename)
	puts "dia top Midisong init filename=#{filename}"
	@filename = filename
	@paused = false
	end

	def superkill
		pidfile = File.new("/tmp/pid.pid", "r");
		pid = pidfile.readline().to_i;
		pidfile.close()
		Process.kill("HUP", pid)

		pidfile = File.new("/tmp/pid2.pid", "r");
		pid2 = pidfile.readline().to_i;
		pidfile.close()
		Process.kill("HUP", pid2)
	end

	def kill
		self.superkill unless @paused
	end

	def play
		pid = fork
		if pid == nil
			### THIS IS THE CHILD
			pid2 = fork
			if pid2 == nil
				### THIS IS THE GRANDCHILD
				### REPLACE IT WITH MIDI PLAYER
				Kernel.exec("timidity", @filename.to_s)
			else
				### THE CHILD
				### ITS PURPOSE IS TO
				### FORK OFF TIMIDITY AND
				### WRITE finished TO FIFO
				### WHEN SONG IS FINISHED
				print "dia pid grandchild=" + pid2.to_s + "\n"
				pid2file = File.new("/tmp/pid2.pid", "w");
				pid2file.puts(pid2.to_s);
				pid2file.close()
				@returnstring = "finished"
				Process.wait(pid2)

				### RETURN PROPER STRING THRU FIFO
				writebackfile = File.new(fifo, "w");
				writebackfile.puts(returnstring);
				writebackfile.close()
				puts "dia returnstring is #{returnstring}"
				Process.exit(0)
			end
		else
			### THIS IS THE PARENT
			pidfile = File.new("/tmp/pid.pid", "w");
			pidfile.write(pid.to_s)
			pidfile.close()
			Process.detach(pid)
			puts "dia pid child=#{pid}"
		end
	end
end

def getNextLine(infile)
	while infile.eof
		sleep(0.5)
	end
	line = infile.gets
	line.chomp!
	sleep(0.5) 
	return line
end


puts "Starting"
puts "dia pid parent=#{Process.pid()}"

playlist=Playlist.new(ARGV[0])

song = Midisong.new(playlist.songname)
song.play()
commandfile = File.new(fifo, "r")
while true
	command = getNextLine(commandfile)
	if song.paused
		puts "Unpausing. Now perform the desired command!"
		command = "unpause"
	end
	puts command
	case command
		when 'finished'
			playlist.next()
			song = Midisong.new(playlist.songname)
			song.play
		when 'next'
			song.kill()
			playlist.next()
			song = Midisong.new(playlist.songname)
			song.play
		when 'prev'
			song.kill()
			playlist.prev()
			song = Midisong.new(playlist.songname)
			song.play
		when 'firstsong'
			song.kill()
			playlist.set(1)
			song = Midisong.new(playlist.songname)
			song.play
		when 'lastsong'
			song.kill()
			playlist.set(playlist.numsongs)
			song = Midisong.new(playlist.songname)
			song.play
		when 'beginsong'
			song.kill()
			song = Midisong.new(playlist.songname)
			song.play
		when 'pause'
			song.kill()
			song.paused = true
			when 'unpause'
			if song.paused
				song = Midisong.new(playlist.songname)
				song.play
			end
		when /^playlist\s/
			temp=command
			temp.gsub!(/playlist\s*/){""}
			temp.gsub!(/\s.*/){""}
			puts "dia playlist=#{temp}"
			playlist=Playlist.new(temp)
			song.kill()
			song = Midisong.new(playlist.songname)
			song.play
		when /^specificsong \d/
			temp=command
			temp.gsub!(/specificsong\s*/){""}
			temp.gsub!(/\D.*/){""}
			puts "dia specificsong=#{temp}"
			song.kill()
			playlist.set(temp.to_i)
			song = Midisong.new(playlist.songname)
			song.play
		when 'quit'
			song.kill()
			break
	end
end
print "Goodbye\n\n"
Ruby program



Open source license





























Bugs









The fifo file.
Must match the fifo defined in UMENU



The playlist object
handles all playlist functionality










Playlist file loop. This defines
which playlist entries are
legitimate. To be legitimate, an
entry must end in .mid, must not
be blank, and must not start with
a comment char (#). This definition
MUST match UMENU'S definition, or
the "jump to song" functionality 
might play the wrong song.




















































Song handler object









superkill method
kills the child
and grandchild









kill method
Don't kill descendents if paused


play method
creates child and grandchild
grandchild is replaced with timidity





Replace grandchild with timidity








Save grandchild pid


Child waits for completion or
termination of grandchild,
which is timidity

Tell main process song is finished


Child process terminates itself
after completion or termination
of grandchild


Save child pid to file

Detach child from main process,
meaning main and child proceed
independently



Function to acquire commands from
the fifo. Eof loop waits for input.



Second sleep necessary to prevent
crashing in the face of rapid,
repetitive commands from fifo






Initialize playlist object

Initialize playlist's first song
Start the song
Open the fifo for input
Command handling loop

When player is paused, the next
command is disabled other than
clearing the pause flag.



Normal song repetition. Child and
grandchild already completed and
terminated. Just incriment playlist
and play the song.

Next. Kill current child and 
grandchild (timidity), increment
playlist and play.

Previous. Kill current child and
grandchild (timidity), increment
playlist and play.


Rewind to playlist's first song. Kill
current child and grandchild 
(timidity), set playlist to first
song, and play.

Proceed to playlist's last song. Kill
current child and grandchild 
(timidity), set playlist to last
song, and play.

Replay current song from its
beginning. Leave playlist untouched,
kill current child and grandchild 
(timidity), and play.

Pause. Kill child and grandchild,
and set paused flag.

Unpause. Resume play. Code at top
of loop already cleared pause flag.


Change playlist. Playlist's filename
follows the "playlist" keyword. Kill
child and grandchild and play new
playlist's first song. Choice of the
playlist is done externally to this
program (typically from UMENU).



Jump to song. Song's 1 based number
follows keyword "specificsong". Kill
child and grandchild, set playlist
to new song number, and play 
playlist's new current song. Actual
choice of song is done externally,
typically from UMENU. UMENU's
definition of legitimate songs on
list must match this program's.

Quit the program. Kill child and 
grandchild, terminate the command
loop, and fall through to program's
end.

The following is the EMDL outline that produces the necessary menus:
ZZM:::Litt's Text Midi Frontender
pAuse
	param
		C: echo pause > /d/bats/littmidi.fifo
Unpause
	param
		C: echo unpause > /d/bats/littmidi.fifo;
Beginning of song
	param
		C: echo beginsong > /d/bats/littmidi.fifo
Next
	param
		C: echo next > /d/bats/littmidi.fifo
Previous
	param
		C: echo prev > /d/bats/littmidi.fifo
First song
	param
		C: echo firstsong > /d/bats/littmidi.fifo
Last song
	param
		C: echo lastsong > /d/bats/littmidi.fifo
Jump to song
	param
		D: /scratch/oldsongs/mid
		C: echo dia1;
		C: FN=$(persist playlist=? $HOME/littmidi.state);
		C: TMPFILE=`mktemp`;
		C: echo dia2;
		C: persist action= $HOME/littmidi.state;
		C: persist recno= $HOME/littmidi.state;
		C: echo dia3;
		C: echo "superselect=n" >> $TMPFILE;
		C: echo "startofdata" >> $TMPFILE;
		C: echo dia4;
		C: echo dia4.5 FN=$FN;
		C: cat $FN >> $TMPFILE;
		C: echo dia5;
		C: cat $FN | grep "\.mid\s*$" | grep -v "^\s*$" |
		C: grep -v "^\s*#" >> $TMPFILE;
		C: echo dia6;
		C: /d/at/cpp/filepicker/rpick $TMPFILE;
		C: echo dia7;
		C: cat $TMPFILE;
		C: grep "action=" $TMPFILE >> $HOME/littmidi.state;
		C: ACTION=$(persist action=? $HOME/littmidi.state);
		C: persist action= $HOME/littmidi.state;
		C: if grep -q "action=select" $TMPFILE; then
		C:   grep "recno=" $TMPFILE >> $HOME/littmidi.state;
		C:   RECNO=$(persist recno=? $HOME/littmidi.state);
		C:   persist recno= $HOME/littmidi.state;
		C:   let RECNO=RECNO+1;
		C:   echo specificsong $RECNO > /d/bats/littmidi.fifo;
		C: fi;
		C: rm -f $TMPFILE;
Run frontmidi.rb
	param
		D: /scratch/oldsongs/mid
		C: TMPFILE=`mktemp`;
		C: echo "dir=/scratch/oldsongs/mid/lists/" > $TMPFILE;
		C: /d/at/cpp/filepicker/fpick $TMPFILE;
		C: if grep -q "action=select" $TMPFILE; then ACTION=SELECT;fi;
		C: if test "$ACTION" = "SELECT"; then
		C:   FN=`grep "^file=" $TMPFILE | sed -e "s/.*=//"`;
		C:   persist "playlist=$FN" $HOME/littmidi.state;
		C:   /d/at/ruby/frontmidi.rb $FN;
		C: fi;
		C: rm -f $TMPFILE;
		S: 1
Change Playlist
	param
		D: /scratch/oldsongs/mid/lists
		C: TMPFILE=`mktemp`;
		C: echo "dir=/scratch/oldsongs/mid/lists/" > $TMPFILE;
		C: /d/at/cpp/filepicker/fpick $TMPFILE;
		C: if grep -q "action=select" $TMPFILE; then ACTION=SELECT;fi;
		C: if test "$ACTION" = "SELECT"; then
		C:   FN=`grep "^file=" $TMPFILE | sed -e "s/.*=//"`;
		C:   persist "playlist=$FN" $HOME/littmidi.state;
		C:   echo playlist $FN > /d/bats/littmidi.fifo;
		C: fi;
cHange Playlist
	param
		D: /scratch/oldsongs/mid/lists
		C: TMPFILE=`mktemp`;
		C: echo "dir=/scratch/oldsongs/mid/lists/" > $TMPFILE;
		C: /d/at/cpp/filepicker/fpick $TMPFILE;
		C: if grep -q "action=select" $TMPFILE; then ACTION=SELECT;fi;
		C: if test "$ACTION" = "SELECT"; then
		C:   FN=`grep "^file=" $TMPFILE | sed -e "s/.*=//"`;
		C:   persist "playlist=$FN" $HOME/littmidi.state;
		C:   echo quit > /d/bats/littmidi.fifo;
		C:   echo Wait two seconds...
		C:   sleep 2;
		C:   /d/at/ruby/frontmidi.rb $FN;
		C: fi;
		C: rm -f $TMPFILE
Kill Littmidi.rb
	param
		C: echo quit > /d/bats/littmidi.fifo
Zap zombie midi players
	param
		C: zapmidizombies
		S: 1
Volume ::: Mplayer volume
	Louder
		param
			C: VOLM=$(persist "volume=?" $HOME/littmidi.state);
			C: if test "$VOLM" = ""; then
			C: let VOLM=60;
			C: persist "volume=$VOLM" $HOME/littmidi.state;
			C: fi;
			C:   if test $VOLM -lt 100; then
			C:   let VOLM=$VOLM+1;
			C:   persist "volume=$VOLM" $HOME/littmidi.state;
			C:   aumix -v $VOLM -w $VOLM;
			C: fi;
			C: echo "Volume is $VOLM";
	Softer
		param
			C: VOLM=$(persist "volume=?" $HOME/littmidi.state);
			C: if test "$VOLM" = ""; then
			C: let VOLM=60;
			C: persist "volume=$VOLM" $HOME/littmidi.state;
			C: fi;
			C:   if test $VOLM -gt 0; then
			C:   let VOLM=$VOLM-1;
			C:   persist "volume=$VOLM" $HOME/littmidi.state;
			C:   aumix -v $VOLM -w $VOLM;
			C: fi;
			C: echo "Volume is $VOLM";
	Mute
		param
			C: aumix -v 0 -w 0;
	Unmute
		param
			C: VOLM=$(persist "volume=?" $HOME/littmidi.state);
			C: if test "$VOLM" = ""; then
			C:   let VOLM=60;
			C:   persist "volume=$VOLM" $HOME/littmidi.state;
			C: fi;
			C: aumix -v $VOLM -w $VOLM;
			C: echo "Volume is $VOLM";
	Observe Volume
		param
			C: VOLM=$(persist "volume=?" $HOME/littmidi.state);
			C: echo Littmidi volume=$VOLM;
			C: aumix -q;
			S: 1
	seT volume to specific number (0-100)
		param
			C: ORG=$(persist "volume=?" $HOME/littmidi.state);
			C: VOLM=%1%Volume please (0-100)%%;
			C: echo "Original volume=$ORG";
			C: echo "$VOLM" | grep "[^[:digit:]]";
			C: NOTANUMBER=$?;
			C: test -z $VOLM;
			C: ZEROLENGTH=$?;
			C: if test "$NOTANUMBER" = "1" -a "$ZEROLENGTH" = "1"; then
			C:   persist "volume=$VOLM" $HOME/littmidi.state;
			C:   aumix -v $VOLM -w $VOLM;
			C:   echo "New volume is $VOLM";
			C:   else
			C:   echo "Bad input: $VOLM";
			C:   echo "Volume unchanged at $ORG";
			C: fi;
			S: 1
	^Quit
Special functions ::: Special Functions Menu
	Drain FIFO
		param
			C: cat /d/bats/littmidi.fifo
			S: 1
	Ps ax
		param
			C: TEMP=`ps ax | grep frontmidi.rb`;
			FIFO Tasks ::: FIFO Task Menu
	Delete current FIFO
		param
			C: rm -f /d/bats/littmidi.fifo;
			C: ls -l /d/bats/littmidi.fifo
			S: 1
	Create FIFO
		param
			C: mkfifo /d/bats/littmidi.fifo;
			C: ls -l /d/bats/littmidi.fifo
			S: 1
	^Quit
	What time is it?
		param
			C: date +%Y/%m/%d__%H:%M:%S
			S: 1
	Edit Litt's Text Music Player menu
		param
			C: gvim /d/bats/zzm.emdl
	Rebuild Litt's Text Music Player menu
		param
			C: /d/bats/rebuild_umenu_zzm.sh
	^Quit
plaYlist ::: Playlist menu
	Get Current Playlist
		param
			C: FN=$(persist playlist=? $HOME/littmidi.state);
			C: echo $FN
			S: 1
	Edit Playlist
		param
			C: gvim $(persist playlist=? $HOME/littmidi.state);
	^Quit
^eXit

























Retrieve current playlist
Make tempfile for recordpicker

erase former persistent action
and record number

cannot select whole directories
start of data flag to recordpicker




Put playlist file lines into record
picker, using same criteria as
frontmidi.rb. Note lack of semicolon
means the line after the cat command
is appended to the end of the cat
command.

Find user action

If use SELECTed, get the record
number, remember it, and send its
one-based equivalent as a
specificsong command to frontmidi.rb.







Make temp file to communicate with
filepicker. Put starting directory
into temp file. Run record picker.


If user selected, get the chosen 
filename and start frontmidi.rb
with that filename as ARG0.




Same as "Run frontmidi.rb", except send
the filename down the fifo with a
playlist command.









Depricated. Change playlists by stopping
and restarting frontmidi.rb.















The zapmidizombies shellscript does a
killall on timidity, and greps ps ax
to obtain pids of all frontmidi.rb
instances, both parents and children,
and issues kill commands for them.



Get the single-value volume number from
storage. If it's not present, set it
to 60 and save it.


Increment the volume number if less than
100, and save it.

Set aumix master volume AND PCM volume
to the single-value number. Crude, but
it works well.

Just like "Louder", except decrements 
the volume number if greater than zero.










Sets audio volumes to zero, but does not
store or change the front end volume number


Retrieves the stored front end volume
number, and sets the aumix master and
pcm volumes to that number.





Shows the front end single value number
followed by all aumix values. The S: 1 at
the end prevents a new menu from
overwriting the output until the user 
presses a key.


Sets the front end single-value number
to the user's input. Retrieves the 
current number, queries for and stores
the user's input. 


It checks for non-numerics and for a zero
length string, and issues an error message
if either is true. Otherwise it changes
the single value number, saves it, and
sets aumix's pcm and master volumes to 
that number.

Error messages on bad input


Stops for user to read messages, until user
acknowledges with a keystroke.
























Edit the EMDL file that is the source for
the menu.


Compiles the source EMDL into a menu system.





The End Product

Here are some screenshots of the end product:
Midi front end, Ruby version, main menu
    Here's the main menu. The top choice is to run the ruby program. Pause, unpause, beginning of song, first song, next, previous, and last song send commands to the ruby program through the FIFO. Change playlist and Jump to song first give the user a list to choose from, and then submit the appropriate command through the FIFO. The Kill Littmidi.rb submits a quit command through the FIFO, and in response the Ruby program ends. The Zap zombie choice uses kill commands to strongarm all the ruby midiplayer programs. The choices with elipses in front of them bring up submenus. Exit exits the menu, but does not terminate the Ruby program.

Midi front end, Ruby version, volumn submenu     Here's the volume submenu. Louder increases the volume a small amount and can be pressed repeatedly. Softer does the same thing but decreases the volume. Mute sets the volume to zero but remembers the former volume. Unmute brings back the former volume. Observe volume looks at persistent storage to tell you the current setting. Set volume to specific number queries the user for a volume number, stores it persistently, and sets the volume accordingly.

All these commands raise and lower volume by calling aumix, and do not submit commands to the Ruby program.

Midi front end, Ruby version, special functions menu     The Special Functions submenu. CLI Run littmidi.rb runs the Ruby program in the terminal now used by the menu. Drain FIFO does what it says, and is used for maintenance and diagnostics when things go wrong. Ps ax shows the process list. FIFO tasks is a submenu. What time is it shows the current time, Edit Litt's Text Music Player menu lets you edit the EMDL file that created the menu, and Rebuild Litt's Text Music Player menu compiles the EMDL file, and after displaying any errors, enables the user to transfer the newly compiled menu files to UMENU.

Midi front end, Ruby version, FIFO task menu     Here's the FIFO task menu. It enables you to delete or create the FIFO. This is used only for troubleshooting when things go very wrong.

Midi front end, Ruby version, Playlist menu     The Playlist submenu. Here you can edit the current playlist (in Vim) or see what the current playlist's filename is.
Steve Litt is the author of Twenty Eight Tales of Troubleshooting.   Steve can be reached at his email address.

The Cool Thing About This Program

Last month's Troubleshooting professional magazine featured me using these techniques to make a front end for mplayer. It might seem that I simply repeated myself, making a front end for timidity this month. It only seems that way.

Last month I made a menu driven front end to the mplayer application. This month I made a menu driven application that uses the timidity utility to play single songs, and the aumix utility to adjust the volume. Last month mplayer handled all volume control and playlist functionality, while this month timidity handled neither volume nor playlist functionality.

I can hear some of you already -- "timidity can handle both volume and playlist, and a lot more!"

Yes, that's true, if the correct interfaces are compiled into timidity, and possibly if I can interface to timidity's midi-server or ALSA-server interfaces. This month I chose not to research those alternatives. Instead, I chose to prove a concept.

The concept is simple -- I can front end a dumb-as-dirt utility. A utility that can only start, play the content on its command line, and terminate upon the content's completion. A utility with absolutely no API or facility for interface.


Block diagram of the front end   This month I proved that concept!

It's a pretty exciting concept. Let's say I wanted playlists comprised of both .ogg and .mid files, and just for good measure maybe a .dia diagram file. No problem -- a bulked up version of my frontmidi.rb could deduce the player from the extension and execute the right command. Perhaps each file in the playlist was recorded at a different volume, such that volume information must be included on each line of the playlist. No problem, it can be done.

OK, it wasn't trivial. I had to build a Ruby program that handled playlists and could spawn and kill timidity at will, and build a FIFO input into the Ruby program so it could be front ended by UMENU. It wasn't trivial, but it just might be easier than compiling more capabilities into timidity or researching how to interface to its ALSA server or midi server modes. Unfortunately, the sad state of documentation in the free software world often means it's easier to write your own program than figure out how to use the other guy's.

And then there's reusability. The next time I need to do something like this it will take much less time. I now know several ways to control spawned processes. That knowledge took an entire day for me to learn.

Check this out. Here's a fairly generic spawner:
Parent program spawner.sh
#!/bin/bash

### DEFINE BINARY COMMAND TO SPAWN, AND COMMAND TO RUN ON ITS TERMINATION
SPAWNEDCOMMAND='sleep 20'
FINISHEDCOMMAND="./test.sh $TMPFILE"

### BUILD THE TEMPORARY FILE TO COMMUNICATE WITH THE CHILD
TMPFILE=mktemp
echo $SPAWNEDCOMMAND > $TMPFILE
echo $FINISHEDCOMMAND >> $TMPFILE

### RUN THE SPAWNER, IN THE BACKGROUND, WITH THE TEMP FILE AS ARG1
./spawn.sh $TMPFILE &

### DEDUCE CHILD AND GRANDCHILD PIDs
MYPID=$$
CHILDPID=$(ps -aH | grep -A1 "^\s*$MYPID" | tail -n1 | cut -b1-5)
GRANDCHILDPID=$(ps -aH | grep -A1 "^\s*$CHILDPID" | tail -n1 | cut -b1-5)

### APPLICATION CODE GOES BELOW
RESPONSE='n'
#echo -n Do you want to kill: $SPAWNEDCOMMAND \(y, n\)? ==\>
read RESPONSE
if test "$RESPONSE" = "y"; then
	# kill $CHILDPID # UNCOMMENT TO PREVENT FINISHEDCOMMAND FROM RUNNING
	kill $GRANDCHILDPID
fi

echo
echo tempfile follows
cat $TMPFILE
echo ===============
sleep 10
echo tempfile follows
cat $TMPFILE
 
#!/bin/bash

TEMPFILE=$1
SPAWNEDCOMMAND=$(head -n1 $TEMPFILE)
FINISHEDCOMMAND=$(head -n2 $TEMPFILE | tail -n1)
$SPAWNEDCOMMAND
$FINISHEDCOMMAND

In the preceding, the top part of the program defines a temporary file with which to communicate, stuffs it with the command to spawn and the command to perform when the spawned program terminates. Then it runs spawner.sh in the background, and then uses the ps -aH command to figure the PIDs of spawner.sh (child) and of the spawned program (grandchild). Everything below that is application specific stuff that handles whether to kill the spawns or not, and how to call them.

As you can see, the top of the parent program is pretty generic. I look forward to finding a way to encapsulate that, making spawning even simpler. One possibility might involve using the trap keyword of bash.

The Next Day

This is written one day after the previous content of this article...

Remember the idea of simpifying spawn even more with trap? Here it is:
#/bin/bash

function on_hup ()
{
	kill -s SIGHUP $COMMANDPID 
	kill -s SIGUSR2 $PARENTPID
	exit 1

}

PARENTPID=$PPID
THISPID=$$
COMMAND2SPAWN=$1
trap on_hup SIGHUP
$COMMAND2SPAWN &
COMMANDPID=$!
echo Parent PID=$PARENTPID
echo This PID=$THISPID
echo CommandPID=$COMMANDPID
wait $COMMANDPID
kill -s SIGUSR1 $PARENTPID
exit 0
Check out  spawn.sh at the left. It traps SIGHUP with function  on_hup(). It records the parent PID, which it will use later to contact the parent as to whether it completed or was HUPped. The first and only argument is the command to get spawned. It runs that command in the background, grabs the spawned command's PID, and waits on that PID.

If someone HUPs spawn.sh, function on_hup() executes, killing the spawned command, sending a USR2 message to the parent that called spawn.sh, and exits.

However, if the spawned command terminates normally, execution falls through the wait, and spawn.sh sends USR1 message to the parent that called spawn.sh, and then the program exits.

In summary, it runs the spawned program in the background. If it gets HUPped, it HUPs the spawned background program and sends USR2 to the calling program. Otherwise, the spawned background program completes and USR1 is sent to the calling program.

To see how clean, simple and reusable this shellscript really is, let's put it into practice by using the following program to run timidity in the background:

#!/bin/bash

### HANDLE THE SONG FINISHING NORMALLY
function on_usr1 () {
	echo "Song ended normally."
	kill -s SIGHUP $sleeppid
}

# HANDLE THE SONG GETTING HUPped
function on_usr2 () {
	echo "Process was killed!"
	kill -s SIGHUP $sleeppid
	exit 1
}

### PROCESSING STARTS HERE
### SET SIGUSR1 AND SIGUSR2 TRAPS
trap on_usr1 SIGUSR1
trap on_usr2 SIGUSR2

### RUN THE SONG IN THE BACKGROUND
./spawn.sh "timidity ./test.mid" &

### GET THE BACKGROUND PROCESS'S PID
spawnpid=$!
echo $spawnpid > ./spawnpid.pid

### SLEEP FOREVER, UNLESS A SIGNAL COMES THROUGH
sleep 1000000 &

### GRAB THE PID OF SLEEP SO YOU CAN KILL IT TO GO FORWARD
sleeppid=$!

### WAIT FOR THE SLEEP TO END
wait $sleepid
echo "Fell through after the song completed normally"
Start by creating functions to handle signals USR1 and USR2, and trap them. Then run spawn.sh to spawn timidity.

It then writes out spawner.sh's PID to file ./spawnpid.pid, and sleeps forever. It will not proceed until that sleep command terminates, which will happen only if the sleep command is killed. The handler functions for USR1 and USR2 both kill that sleep.

One more thing: For some reason, if you perform a ps command, spawner.sh will not appear. Instead, it will appear with the same name as the script that called it.

Now let's illustrate a more practical application:
#!/bin/bash

function on_usr1 ()
{
	echo "Song ended normally."
	./increment_songno.sh
	persist "spawnpid=999999" $HOME/littmidi_b.state
	kill -s SIGHUP $sleeppid
}

function on_usr2 ()
{
	echo "Process was killed!"
	persist "spawnpid=999999" $HOME/littmidi_b.state
	kill -s SIGHUP $sleeppid
	exit 1
}

trap on_usr1 SIGUSR1
trap on_usr2 SIGUSR2
while true; do
	songno=$(persist "songno=?" $HOME/littmidi_b.state)
	songname=$(./songnum2name.sh $songno)
	echo songname=$songname
	command="timidity $songname > /dev/null"
	echo Command=$command
	./spawn.sh "$command" &
	spawnpid=$!
	persist "spawnpid=$spawnpid" $HOME/littmidi_b.state

	### Next line sleeps forever, relies on a signal
	### to kill it.
	sleep 1000000 &
	sleeppid=$!
	wait $sleepid
done
As before, USR1 and USR2 are trapped and referred to functions. Both functions kill the forever sleep. USR1 comes in when the song runs its course, while USR2 comes in when the song (spawner.sh actually) gets HUPped. On USR1 the playlist is incremented, while on USR2 it's not.

Each iteration of main loop gets the song number, spawn timidity on that song, persistently records the PID for spawn.sh so it can be killed from another process, and sleeps.

Because that PID was persistently recorded, the following kill.sh can terminate the current song:

#!/bin/bash
spawnpid=$(persist "spawnpid=?" $HOME/littmidi_b.state)
kill -s SIGHUP $spawnpid

Functionalities such as next, previous, first, last, and set song are implemented by killing the current song, adjusting the song number, and rerunning the script to the left. I made such an animal, which will be discussed later in this TPM issue.


The preceding became the play.sh used to make yet another version of the midi player. This new version used only shellscripts -- not a bit of Ruby. Basically, every menu item killed the running timidity, adjusted the playlist position as appropriate, and ran play again. More on this in another article.
Steve Litt is the author of the Universal Troubleshooting Process Courseware. Steve can be reached at his email address.

Yeah, I Know...

By Steve Litt
Yeah, I know. There are a million ways I could have done this more simply. I could have better used the capabilities of timidity, or I could have gotten playmidi to work on my box, or I could have used yet another midi player, or perhaps I could have configured/compiled mplayer or some other music player to play midi files.

I could have replaced frontmidi.rb with a few shellscripts. [Author's note: Obviously the preceding sentence was written before I actually did replace it with shellscripts, not just once but twice.] I could have used threads instead of processes. Heck, there's probably a Perl module on CPAN to handle midi files. Maybe even a Ruby gem.

One of the benefits of authoring a website getting 5000 visits per day is when you describe a technique, many people email with better ways of doing it. I've learned a heck of a lot from those emails.

If you've read Troubleshooters.Com for awhile, you know I'm not scared to reinvent the wheel. Sometimes it's quicker than searching high and low for something that will do what you want, and then modifying it to your needs. And sometimes it produces a better quality product. There were at least five outliners for Linux when I created VimOutliner. They were pretty and gui and multiuser and oh so fine. But I reinvented the wheel, and it's now either the #1 or #2 most used outliner in the Linux world.

I reinvented the wheel. Guilty as charged. I enjoyed it. Now please email me and tell me all the ways I could have done this easier, quicker and better.
Steve Litt is the author of the Troubleshooting Techniques of the Successful Technologist. Steve can be reached at his email address.

The All-Shellscript Midi Player

By Steve Litt
Here's how it happened. After creating the (Ruby-core) midi player I began writing this magazine about the experience. As the writing continued, various improvements became obvious and were tried, debugged and ultimately deployed. The more improvements were made, the more there was to write about, and the more improvements became obvious and were tried...

The quest for an easy and generic spawner succeeded beyond my hopes, and on viewing it my first reaction was "I should rewrite the midi player around this spawner. Thus was born the all-shellscript midi player.

The persist command, to keep data persistently, was written for last month's magazine. The next step was a way to wait for an incoming signal without silly looping polling. A two hour read of the bash man page yielded the idea of the forever sleep, killed in the signal trap routine. That was 90% of the technology of the bash-only player -- the rest was just writing code and debugging.

To refresh your memory, here's play.sh, the core of the application:

#!/bin/bash

function on_usr1 ()
{
	echo "Song ended normally."
	increment_songno.sh
	persist "spawnpid=999999" $HOME/littmidi_b.state
	kill -s SIGHUP $sleeppid
}

function on_usr2 ()
{
	echo "Process was killed!"
	persist "spawnpid=999999" $HOME/littmidi_b.state
	kill -s SIGHUP $sleeppid
	exit 1
}

trap on_usr1 SIGUSR1
trap on_usr2 SIGUSR2
while true; do
	songno=$(persist "songno=?" $HOME/littmidi_b.state)
	songname=$(songnum2name.sh $songno)
	echo songname=$songname
	command="timidity $songname > /dev/null"
	echo Command=$command
	spawn.sh "$command" &
	spawnpid=$!
	persist "spawnpid=$spawnpid" $HOME/littmidi_b.state

	### Next line sleeps forever, relies on a signal
	### to kill it.
	sleep 1000000 &
	sleeppid=$!
	wait $sleepid
done
As explained before, trap USR1 to react to a finished song, USR2 to a killed process, presumably caused by a user request.

Create a forever loop that spawns timidity with the proper song using the spawn.sh discussed in the The Cool Thing About This Program article. Once spawn.sh has run, the PID for spawn.sh is saved, and then it goes into a forever sleep.

If you look at the code, the USR1 handler increments the song number and kills the sleep, allowing the loop to repeat with the incremented song number. The USR2 handler does the same thing but without the incrementation -- it's assumed that the user will have done whatever is necessary to set the song number.

The next step was to create some utilities:
#!/bin/bash
play.sh > /dev/null &

### WITHOUT THE FOLLOWING SLEEP,
### SUCCESSIVE NEXTS WILL SPAWN
### MULTITUDES OF PLAY.SH
### AND SCREW UP EVERYTHING
sleep 0.1
playinbackground.sh This runs play.sh in the background so it doesn't displace UMENU from the terminal.
#!/bin/bash
songno=$(persist "songno=?" $HOME/littmidi_b.state)
numsongs=$(persist "numsongs=?" $HOME/littmidi_b.state)
echo Currsongno=$songno of $numsongs
if test $songno -lt $numsongs; then
	let songno=$songno+1
else
	let songno=1
fi
persist "songno=$songno" $HOME/littmidi_b.state
increment_songno.sh This takes the song number out of persistent storage, increments it, and puts it back in persistent storage. It handles wraparound.

Because all my shellscripts work from persistent storage, merely changing the value in persistent storage is sufficient.
#!/bin/bash
songno=$(persist "songno=?" $HOME/littmidi_b.state)
numsongs=$(persist "numsongs=?" $HOME/littmidi_b.state)
echo Currsongno=$songno of $numsongs
if test $songno -gt 1; then
	let songno=$songno-1
else
	let songno=$numsongs
fi
persist "songno=$songno" $HOME/littmidi_b.state
decrement_songno.sh Just like increment_songno.sh, except it decrements. It handles wraparound.
let songno=$1
let oldsongno=$(persist "songno=?" $HOME/littmidi_b.state)
let numsongs=$(persist "numsongs=?" $HOME/littmidi_b.state)
echo Currsongno=$songno of $numsongs
if test $songno -gt $numsongs; then
	let songno=1
elif test $songno -lt 1; then
	let songno=$numsongs
fi
persist "songno=$songno" $HOME/littmidi_b.state
set_songno.sh Just like increment_songno.sh, excepts it sets the song number to Arg1. It does quite a bit of testing Arg1 for a valid number between 1 and the length of the playlist, so that a valid new number is stored.
#!/bin/bash
FN=$(persist "playlist=?" $HOME/littmidi_b.state)
cat $FN | grep "\.mid\s*$" | grep -v "^\s*$" | grep -v "^\s*#"
list_playlist.sh This is how you derive a playlist from the persistent playlist filename and the file to which it refers. The grep commands simply filter out blank lines, comments, and any files not ending in .mid.
#!/bin/bash
SONGNO=$1
list_playlist.sh | head -n $SONGNO | tail -n 1
songnum2name.sh Uses head and tail on the output of list_playlist.sh to deliver the filename corresponding to the song number, for this playlist.
#!/bin/bash
grep "$1=" | sed -e 's/.*=\s*//' | sed -e 's/\s*$//'
get_value.sh Given key=value, this delivers the value based on the key.
#!/bin/bash
spawnpid=$(persist "spawnpid=?" $HOME/littmidi_b.state)
kill -s SIGHUP $spawnpid
persist "spawnpid=999999" $HOME/littmidi_b.state
killsong.sh Retrieves the PID of spawn.sh from persistent store, and HUPs it. Remember, upon getting HUPped, spawn.sh sends USR2 to its caller then exits. It also sets the persistent spawnpid as 999999 (in other words, no spawner process running)

NOTE: This shellscript was later fitted with code to check whether the process was running before attempting the kill.
#!/bin/bash
PLAYLIST=$1
persist "playlist=$PLAYLIST" $HOME/littmidi_b.state
NUMSONGS=$(list_playlist.sh | wc -l)
echo dia $NUMSONGS songs in playlist $PLAYLIST.
persist "numsongs=$NUMSONGS" $HOME/littmidi_b.state
persist "songno=1" $HOME/littmidi_b.state
persist "spawnpid=999999" $HOME/littmidi_b.state
change_playlist.sh Arg1 is the filename of the new playlist filename. Stores that, along with the number of songs on the playlist. Stores song number as 1 (start at the beginning).
#!/bin/bash
ps ax | grep 'play.sh' | grep -v grep | cut -b1-5 | xargs kill
nuke_play.sh When something goes wrong and processes multiply like rabbits, this is how you take them all down at once.
#!/bin/bash
ps ax | grep 'play.sh' | grep -v grep | cut -b1-5 | xargs kill
killall timidity
ps ax | grep 'sleep 1000000' | grep -v grep | cut -b1-5 | xargs kill
nuke_all.sh This is the ultimate weapon of mass destruction. When you run this puppy, all processes associated with the midi player are killed.

The preceding commands were just utilities I figured needed doing often enough that it was worth writing scripts for them. None is rocket science, and many are trivially simple. All link together through the persistent storage served by the persist command.

Look at the preceding commands. They define a Doman Specific Language (DSL). You can use pretty much just those, plus UMENU and the pickers, to create a song playing program. That Domain Specific Language was put together in the UMENU EMDL (source) file to create the app. Here's the source EMDL:
ZZB:::Litt's Text Midi Frontender (bash version)
Run Litt's text midi player
	param
		D: /d/at/bash/littmidi
		C: TMPFILE=`mktemp`;
		C: echo "dir=/scratch/oldsongs/mid/lists/" > $TMPFILE;
		C: /d/at/cpp/filepicker/fpick $TMPFILE;
		C: if grep -q "action=select" $TMPFILE; then ACTION=SELECT;fi;
		C: if test "$ACTION" = "SELECT"; then
		C:   FN=`grep "^file=" $TMPFILE | sed -e "s/.*=//"`;
		C:   persist "playlist=$FN" $HOME/littmidi_b.state;
		C:   persist "songno=1" $HOME/littmidi_b.state;
		C:   numsongs=$(list_playlist.sh | wc -l);
		C:   persist "numsongs=$numsongs" $HOME/littmidi_b.state;
		C:   killsong.sh;
		C:   playinbackground.sh > /dev/null;
		C: fi;
		C: rm -f $TMPFILE;
pAuse
	param
		D: /d/at/bash/littmidi
		C: killsong.sh;
Unpause
	param
		D: /d/at/bash/littmidi
		C: killsong.sh;
		C: playinbackground.sh;
Beginning of song
	param
		D: /d/at/bash/littmidi
		C: killsong.sh;
		C: playinbackground.sh;
First song
	param
		D: /d/at/bash/littmidi
		C: killsong.sh;
		C: set_songno.sh 1;
		C: playinbackground.sh;
Next
	param
		D: /d/at/bash/littmidi
		C: killsong.sh;
		C: increment_songno.sh;
		C: playinbackground.sh;
Previous
	param
		D: /d/at/bash/littmidi
		C: killsong.sh;
		C: decrement_songno.sh;
		C: playinbackground.sh;
Last song
	param
		D: /d/at/bash/littmidi
		C: killsong.sh;
		C: numsongs=$(persist "numsongs=?" $HOME/littmidi_b.state);
		C: set_songno.sh $numsongs;
		C: playinbackground.sh;
Jump to song
	param
		D: /d/at/bash/littmidi
		C: echo dia1;
		C: FN=$(persist playlist=? $HOME/littmidi_b.state);
		C: TMPFILE=`mktemp`;
		C: echo dia2;
		C: persist action= $HOME/littmidi_b.state;
		C: persist songno= $HOME/littmidi_b.state;
		C: echo dia3;
		C: echo "superselect=n" >> $TMPFILE;
		C: echo "startofdata" >> $TMPFILE;
		C: echo dia4;
		C: echo dia4.5 FN=$FN;
		C: list_playlist.sh >> $TMPFILE;
		C: echo dia5;
		C: echo dia6;
		C: /d/at/cpp/filepicker/rpick $TMPFILE;
		C: echo dia7;
		C: cat $TMPFILE;
		C: ACTION=$(grep "action=" $TMPFILE | get_value.sh "action");
		C: if test "$ACTION" = "select"; then
		C:   RECNO=$(grep "recno=" $TMPFILE | get_value.sh "recno");
		C:   echo dia recnob4=$RECNO;
		C:   let RECNO=$RECNO+1;
		C:   echo dia recno=$RECNO;
		C:   persist "songno=$RECNO" $HOME/littmidi_b.state;
		C:   killsong.sh;
		C:   playinbackground.sh;
		C: fi;
		C: rm -f $TMPFILE;
		S: 1
Change Playlist
	param
		D: /scratch/oldsongs/mid/lists
		C: TMPFILE=`mktemp`;
		C: echo "dir=/scratch/oldsongs/mid/lists/" > $TMPFILE;
		C: /d/at/cpp/filepicker/fpick $TMPFILE;
		C: if grep -q "action=select" $TMPFILE; then ACTION=SELECT;fi;
		C: if test "$ACTION" = "SELECT"; then
		C:   FN=`grep "^file=" $TMPFILE | sed -e "s/.*=//"`;
		C:   persist "playlist=$FN" $HOME/littmidi_b.state;
		C:   persist "songno=1" $HOME/littmidi_b.state;
		C:   numsongs=$(list_playlist.sh | wc -l);
		C:   persist "numsongs=$numsongs" $HOME/littmidi_b.state;
		C:   cd /d/at/bash/littmidi;
		C:   killsong.sh;
		C:   playinbackground.sh;
		C: fi;
Kill Litts Midi Bash Player
	param
		D: /d/at/bash/littmidi
		C: killsong.sh
Zap zombie midi players
	param
		C: zapmidizombies
		S: 1
Volume ::: Mplayer volume
	Louder
		param
			C: VOLM=$(persist "volume=?" $HOME/littmidi_b.state);
			C: if test "$VOLM" = ""; then
			C:   let VOLM=60;
			C:   persist "volume=$VOLM" $HOME/littmidi_b.state;
			C: fi;
			C: if test $VOLM -lt 100; then
			C:   let VOLM=$VOLM+1;
			C:   persist "volume=$VOLM" $HOME/littmidi_b.state;
			C:   aumix -v $VOLM -w $VOLM;
			C: fi;
			C: echo "Volume is $VOLM";
	Softer
		param
			C: VOLM=$(persist "volume=?" $HOME/littmidi_b.state);
			C: if test "$VOLM" = ""; then
			C:   let VOLM=60;
			C:   persist "volume=$VOLM" $HOME/littmidi_b.state;
			C: fi;
			C: if test $VOLM -gt 0; then
			C:   let VOLM=$VOLM-1;
			C:   persist "volume=$VOLM" $HOME/littmidi_b.state;
			C:   aumix -v $VOLM -w $VOLM;
			C: fi;
			C: echo "Volume is $VOLM";
	Mute
		param
			C: aumix -v 0 -w 0;
	Unmute
		param
			C: VOLM=$(persist "volume=?" $HOME/littmidi_b.state);
			C: if test "$VOLM" = ""; then
			C:   let VOLM=60;
			C:   persist "volume=$VOLM" $HOME/littmidi_b.state;
			C: fi;
			C: aumix -v $VOLM -w $VOLM;
			C: echo "Volume is $VOLM";
	Observe Volume
		param
			C: VOLM=$(persist "volume=?" $HOME/littmidi_b.state);
			C: echo Littmidi volume=$VOLM;
			C: aumix -q;
			S: 1
	seT volume to specific number (0-100)
		param
			C: ORG=$(persist "volume=?" $HOME/littmidi_b.state);
			C: VOLM=%1%Volume please (0-100)%%;
			C: echo "Original volume=$ORG";
			C: echo "$VOLM" | grep "[^[:digit:]]";
			C: NOTANUMBER=$?;
			C: test -z $VOLM;
			C: ZEROLENGTH=$?;
			C: if test "$NOTANUMBER" = "1" -a "$ZEROLENGTH" = "1"; then
			C:   persist "volume=$VOLM" $HOME/littmidi_b.state;
			C:   aumix -v $VOLM -w $VOLM;
			C:   echo "New volume is $VOLM";
			C:   else
			C:   echo "Bad input: $VOLM";
			C:   echo "Volume unchanged at $ORG";
			C: fi;
			S: 1
	^Quit
Special functions ::: Special Functions Menu
	Persist display
		param
			C: cat $HOME/littmidi_b.state
			S: 1
	show pAth:
		param
			C: echo Path=$PATH
			S: 1
	pS ax
		param
			C: ps ax | less;
	What time is it?
		param
			C: date +%Y/%m/%d__%H:%M:%S
			S: 1
	Edit Litt's Text Music Player menu
		param
			C: gvim /d/at/bash/littmidi/zzb.emdl
	Rebuild Litt's Text Music Player menu
		param
			C: /d/bats/rebuild_umenu_zzb.sh
	Timidity killall
		param
			C: killall -s SIGHUP timidity
	^Quit
plaYlist ::: Playlist menu
	Get Current Playlist
		param
			C: FN=$(persist playlist=? $HOME/littmidi_b.state);
			C: echo $FN
			S: 1
	Edit Playlist
		param
			C: gvim $(persist playlist=? $HOME/littmidi_b.state);
	^Quit
^eXit
"Run" first runs a filepicker so the
user can choose a playlist file.

If the user chooses to select and not
cancel, it persistently stores the
playlist filename, the song number
(strongarmed to 1), and the number
of songs in the playlist.

Finally it kills any current song,
and runs play.sh in the background.
Note that this is essentially just 
like the change directory
functionality.

This is much too big to put in an
EMDL file. It really should have
been a script.
=========================
Pause simply kills the current song,
so nothing's playing.
==========================

Unpause re-kills the song just in
case, and then runs play.sh in the
background.
===========================
Beginning kills the current song 
and runs play.sh in the background 
to replay the current song from the
beginning.
==========================
First song sets the song number to 1,
kills the current song and plays the
(now) current song.
==========================


Next kills the current song,
increments the persistent song
number, and plays the (now) current
song.
===========================

Previous is just like Next except it
decrements.
===========================



Last song sets the song number to the
number of songs in the current
playlist, then kills and replays.
===========================




Jump to song uses list_playlist.sh
to create a recordpicker from which
the user chooses a song. The
recordpicker delivers a 0 based
record number, which is incremented
to get a 1 based song number, which
is stored persistently, after which
there's a kill and play.

This is much too big to put in an
EMDL file. It really should have
been a script.

















==========================




Change Playlist runs a filepicker,
stores the user's choice as the 
playlist filename, derives the number
of songs and stores that, stores the
song number as 1, and stores the
spawner PID as 999999, meaning no
spawner.

This is much too big to put in an
EMDL file. It really should have
been a script.



==================================
Kills the current player and does
not replace it.
==================================

This Zap is for the old Ruby based
player, and needs recoding for the
bash based player.
==================================
The volume functions store a single
volume number, and set aumix's
master volume AND pcm to that value.




























































================================
The special functions are mostly
diagnostic or compilation.
================================
Persist Display displays all
variables in peristent storage.
================================
Show path shows the current path.
================================

ps ax shows that command in a less
pager.
================================
What time is it shows the time.
================================

Edit Litt's Text Music Player lets
you edit the EMDL file.
================================
Rebuild compiles the EMDL file into
a menu, shows any errors or warnings,
and gives you the choice of deploying
(transferring) the new menu or not.
================================
Timidity Killall kills all Timidity
processes.
================================

Looking at the preceding, I have one gripe. The menu choices implementing pickers have far too much code. Debugging EMDL is much tougher than debugging shellscripts. Why not take the Domain Specific Language concept to the next level and create shellscripts to run the pickers and deliver the result?
#!/bin/bash
TMPFILE=mktemp
cd /d/at/bash/littmidi
persist "action=cancel" $HOME/littmidi_b.state
echo "dir=/scratch/oldsongs/mid/lists/" > $TMPFILE
/d/at/cpp/filepicker/fpick $TMPFILE
if grep -q "action=select" $TMPFILE; then
	ACTION=SELECT
fi;
if test "$ACTION" = "SELECT"; then
	FN=`grep "^file=" $TMPFILE | sed -e "s/.*=//"`
	change_playlist.sh $FN
	persist "action=select" $HOME/littmidi_b.state
fi
rm -f $TMPFILE
change_playlist_ui.sh This runs the filepicker, stores the action, and if the action was select it runs change_playlist.sh to store everything else that should be stored.

It's up to the UMENU part of the process to actually kill and rerun.
#!/bin/bash

TMPFILE=`mktemp`
echo "superselect=n" >> $TMPFILE
echo "startofdata" >> $TMPFILE
list_playlist.sh >> $TMPFILE
/d/at/cpp/filepicker/rpick $TMPFILE
ACTION=$(grep "action=" $TMPFILE | get_value.sh "action")
persist "action=$ACTION" $HOME/littmidi_b.state
if test "$ACTION" = "select"; then
	RECNO=$(grep "recno=" $TMPFILE | get_value.sh "recno")
	let RECNO=$RECNO+1
	set_songno.sh $RECNO
fi
rm -f $TMPFILE
jump2song.sh This runs the recordpicker against the playlist's songs, accepts the user's choice, stores the action (select or cancel), and if select it runs set_songno.sh to store relevent info.

It's up to the UMENU part of the process to actually kill and rerun.

The preceding scripts make the UMENU EMDL file much simpler:
Change Playlist
	param
	C: change_playlist_ui.sh;
	C: action=$(persist "action=?" $HOME/littmidi_b.state);
	C: if test "$action" = "select"; then
	C:   killsong.sh;
	C:   playinbackground.sh;
	C: fi;
	S: 1
Change Playlist Here change_playlist_ui does almost all the work, including querying the user with a filepicker. All UMENU does is make sure the user selected rather than cancelled, and if so kill and rerun.
Jump to song
	param
	C: jump2song.sh;
	C: action=$(persist action=? $HOME/littmidi_b.state);
	C: if test "$action" = "select"; then
	C:   killsong.sh;
	C:   playinbackground.sh;
	C: fi
	S: 1
Jump to song Here jump2song.sh does most of the work, including querying the user with a recordpicker. All UMENU does is test that the user selected instead of cancelled, and if so kills and replays.
Steve Litt is the author of Twenty Eight Tales of Troubleshooting.   Steve can be reached at his email address.

Build AAuSE Modularly

By Steve Litt
The primary benefit of AAuSE is modularity. Many talk modularity, few practice it. Modular programs are easy to debug, non-modular programs are incredibly difficult to debug.

In your AAuSE program, do as much as you can with anonymous pipes (the | character between processes), with process return codes, and with temporary intermediate files named with mktemp. Intermediate files communicate via streams that are written only by one process and read only by another, so what is happening is unambiguous. When using temporary intermediate files, make sure they're deleted as soon as their full contents are captured. Doing so makes it easier to find the current intermediate file, and prevents the possibility of other programs writing to it.

If you use a FIFO to give commands to a program, be very specific about which processes are supposed to write to the FIFO, and document it. If it's unclear what commands are coming in, you might need to replace the FIFO's recipient with a stub that prints incoming commands:

#!/bin/bash
while true; do
	cat /d/bats/littmidi.fifo
done

Note that even if your program is a Ruby program or a binary, you can replace it, using the same filename, with this stub and you will see everything coming in through the FIFO.

The thing you want to limit is persistent on-disk data. Yeah, you need some of it or your programs can't communicate, but every piece of on-disk data is just like a global variable in a normal program -- it's free game for everyone to modify, and it's there for the reading by every program. You don't know who is writing it and who is reading it. That's not a good situation, so keep persistent on-disk data to a minimum.

With foreground processes, you often can and should use exit codes instead of writing persistent data to disk. For instance:
#!/bin/bash

TMPFILE=`mktemp`
echo "superselect=n" >> $TMPFILE
echo "startofdata" >> $TMPFILE
list_playlist.sh >> $TMPFILE
/d/at/cpp/filepicker/rpick $TMPFILE
ACTION=$(grep "action=" $TMPFILE | get_value.sh "action")
persist "action=$ACTION" $HOME/littmidi_b.state
exitcode=1
if test "$ACTION" = "select"; then
	RECNO=$(grep "recno=" $TMPFILE | get_value.sh "recno")
	let RECNO=$RECNO+1
	set_songno.sh $RECNO
	exitcode=0
fi
rm -f $TMPFILE
exit $exitcode
The code to the right picks a song and delivers the song name to persistent storage. There's no way around that. But it also delivers the user's action (select or cancel) to persistent storage. That's unnecessary because that information can be delivered back via the exit code (0 for select, 1 for cancel).

This way the program that called the code to the left changes the song if this code's exit value is 0, but does nothing if it's nonzero.

If the calling program had looked at the "action" variable in persistent disk storage, and if some other process had subsequently modified that value (difficult but not impossible), the calling program would do the wrong thing and display a hard to find bug.

Exit codes are by their nature modular because only the process' caller can see them.

Sometimes you can replace disk persistent data with the stdout from a foreground process. A perfect example is the songnum2name.sh command:

Foreground Child
(songnum2name.sh)
Parent
#!/bin/bash
SONGNO=$1
list_playlist.sh | head -n $SONGNO | tail -n 1
 
songno=$(persist "songno=?" $HOME/littmidi_b.state)
songname=$(songnum2name.sh $songno)
  The parent gets the song number, then runs songnum2name.sh and sets $songname to the output of songnum2name. sh.songnum2name.sh lists all songs, and uses head and tail to grab the songname corresponding to $1.

In fact, you can completely eliminate the need for persistent disk storage between parent and foreground child, or parent and foreground grandchild, or even foreground siblings, with a combination of passing data to child as commandline arguments, and receiving it from the child as the child's exit status or the child's stdout.

This is just like the fact that you can eliminate global variables in a traditional program using functional arguments to pass down and return integers, strings or even arrays. Like traditional programs, sometimes eliminating a global is too much effort and too much code. With data needed in several places, sometimes it makes sense to go with global variables, or in the case of shellscripts, disk-persistent data.

Another communication method you should keep to a minimum is signals. Signals can provide a handy way to get immediate attention, but if everyone is signalling everyone else, the logic path looks like a bowl of spaghetti. Signals should be used only when necessary.

MODULARITY TIPS
FAVOR THESE
  • Process exit codes
  • Process stdout
  • Anonymous pipes (|)
  • Temporary intermediate files
    • Uniquely named, deleted immediately
  • FIFOs
    LIMIT THESE
  • Disk persistent data
  • Signals
Steve Litt is the author of the Manager's Guide to Technical Troubleshooting. Steve can be reached at his email address.

A Signal Primer

By Steve Litt
AUTHOR'S NOTE

The output of many of the ps ax commands in this article were altered to make the output less wide. In many cases I did things like substitute "gimp", "dia", "timidity ./test.mid", or "gvim test.sh" for much longer lines in the output. I also reduced the number of spaces between the words "hangup" and "sleep".

That being said, in no case did I alter any line containing ./test.sh or sleep, so for the purposes of this article, the ps commands are proof of the existence of nonexistence of the sleep or test.sh processes.

Signals enable independently running processes to communicate with each other without resorting to complex, wasteful polling algorithms. The communication is, for the purposes of an application with a user interface, instantaneous. The only thing process A needs in order to signal process B is process B's process ID (PID).

Handy PID Representations

Every process has a Process ID, or PID for short. The only way to signal a process is with it's PID, unless you want to use the killall command, which is a little like using a hydrogen bomb to kill an ant. So it's important that every process knows the PIDs of every process to whom it wants to send a signal.

There are various ways to get PIDs of running processes:
Representation    What it Represents
$$
PID of current process
$!
PID of last background process that was run
$PPID
PID of parent of current process
$?
Exit status of last foreground process run

PID Persistence

The $! and #? environment variables are very transitory -- the next time you run a process they will change. In order to keep them you must save them to your own variable right away, like this:
grep "George Bush" myfile.txt          # See whether file contains president's name
georgethere=$?                         # Store grep's return code in $georgethere
timidity ./test.mid &                  # Run timidity in the background
timidity_pid=$!                        # Store timidity's PID in timidity_pid
Once so stored, you can use these values anywhere, including in signal handler routines, even after many other processes have been run, both foreground and background. If the return code of a foreground process is important, always save $? immediately upon termination of the foreground process. If the PID of a process your script has run in the background is important, always save $! immediately after running it in the background.

The preceding material explained how to keep these values persistent within a single script. Sometimes you need these PIDs available to several scripts. Doing that is simply a matter of saving the PID to disk:
timidity ./test.mid &                  # Run timidity in the background
timidity_pid=$!                        # Store timidity's PID in timidity_pid
echo $timidity_pid > tim.pid           # Store it to disk
Now, if any process wants to send, let's say, a USR1 signal to that timidity process, it's as simple as this:
kill -s SIGUSR1 $(cat tim.pid)

NOTE
As mentioned in the Build AAuSE Modularly article, storing data on disk decreases modularity, so do it only when necessary. Note also that if you store it to a filename known only to the process receiving the information (perhaps the filename contains the receiver's PID), and if the receiving process deletes it immediately after storing it to a variable, this increases modularity over using a descriptive filename.

In the case of timidity, it's unlikely you'd deliberately have two copies of timidity going at the same time. After all, you have one sound card, one set of speakers and one set of ears. Things might be different when running other subprocesses. In that case you might want to have more specific filenames for the PID information. Perhaps you'd include the parent's PID in the filename so you can get to the exact subprocess.

Another glitch might occur if timidity is run in a loop. Theoretically it should either terminate normally or be killed before it's rerun, but perhaps that might not happen. Under those circumstances, you might want a timestamp to be part of the file. Then, if need be, another process can look at, let's say, every myapp_tim*.pid file, and kill each.

Is the Process Still Running?

If you're writing something quick and dirty, you might kill a PID whether or not it's running, and just ignore the resulting error message or other problem. But if you're writing a robust, professional app used by others besides yourself, you probably want to ascertain that it's still running before killing it. On Linux (not necessarily Unix or BSD), it's done like this:
timidity ./test.mid &                          # Run timidity in the background
timidity_pid=$!                                # Store timidity's PID in timidity_pid
# do other stuff
ls -ldF /proc/$timidity_pid > /dev/null 2>&1   # Check if the PID has a directory in /proc
$stillrunning=$?                               # If it's still running, $stillrunning is 0
                                               # otherwise $stillrunning will be nonzero

Basic Signal Handling

Any untrapped signal will abort a process:

#!/bin/bash

mypid=$$
mycommand="kill -s SIGUSR1 $mypid"
echo $mycommand > $HOME/danger.sh
chmod a+x $HOME/danger.sh
sleep 30
echo FELL THROUGH
[slitt@mydesk ~]$ ./test.sh
User defined signal 1
[slitt@mydesk ~]$ ./test.sh
FELL THROUGH
[slitt@mydesk ~]$
First, notice that the script writes a second script, danger.sh, which kills the job number corresponding to the running script. Recording the PID of the running script so others can signal it is a very common technique. Running danger.sh sends a USR1 signal to the running script -- a fast way to send the signal.

The first time ./danger.sh was run in order to kill the script. The script terminated immediately and did not print the final message. On the second run, the script was not called, so the script ran its 30 seconds and printed the final message.

On my system (Mandriva 2007), contrary to documentation, the interrupt immediately terminates the script, even though the script is running a command (sleep) in the foreground. However, that foreground process (sleep) continues in the background. Wierd but true.

Now let's trap the interrupt by defining a function called donothing() and trapping SIGUSR1 to it:
#!/bin/bash

function donothing()
{
	echo -n
}

trap donothing SIGUSR1
mypid=$$
mycommand="kill -s SIGUSR1 $mypid"
echo $mycommand > $HOME/danger.sh
chmod a+x $HOME/danger.sh
sleep 30
echo FELL THROUGH
[slitt@mydesk ~]$ ./test.sh
FELL THROUGH
[slitt@mydesk ~]$
Here, whether ./danger.sh was run or not, the script ran all the way to the end, SIGUSR1 signal executed function donothing()
instead of killing the script.

I've seen documentation stating you could disable a signal like this:
trap SIGUSR1
I can tell you that on my system, that doesn't work, and the script will be terminated by receipt of a SIGUSR1. On my system, you must include either the function name or an empty string:
trap "" SIGUSR1
The preceding trap completely ignores SIGUSR1, to the extent that a current wait is not breached.

You can get script to do useful work the signal handling routine. In the following, donothing() is changed to dosomething(), which prints a message.

#!/bin/bash

function dosomething()
{
 echo "Hit me with your best shot!"
}

trap dosomething SIGUSR1
mypid=$$
mycommand="kill -s SIGUSR1 $mypid"
echo $mycommand > $HOME/danger.sh
chmod a+x $HOME/danger.sh
sleep 30
echo FELL THROUGH
[slitt@mydesk ~]$ ./test.sh
Hit me with your best shot!
FELL THROUGH
[slitt@mydesk ~]$ ./test.sh
FELL THROUGH
[slitt@mydesk ~]$
Here I ran danger.sh six times, but the message printed only once, and not until the sleep was over. That's not a good example of "doing useful work". What we want is that every time we hit it with a USR1, it prints the "hit me" message.

Could it have to do with the fact that sleep was in the foreground when the interrupts hit? 

Let's test that hypothesis by running sleep in the background and then issuing a wait...

#!/bin/bash

function dosomething()
{
	echo "Hit me with your best shot!"
}

trap dosomething SIGUSR1
mypid=$$
mycommand="kill -s SIGUSR1 $mypid"
echo $mycommand > $HOME/danger.sh
chmod a+x $HOME/danger.sh
sleep 30 &
sleeppid=$!
wait $sleeppid
echo FELL THROUGH
[slitt@mydesk ~]$ ./test.sh
Hit me with your best shot!
FELL THROUGH
[slitt@mydesk ~]$
Yep -- now the signal is handled instantly.

But there's trouble. The instant you issue the ./danger.sh command, the script terminates but performs the signal handler routine and prints the last statement. A ps command shows that the sleep command is still running, so the problem isn't that the SIGUSR1 got passed on to the sleep command -- the problem is that either the interrupt or the starting of the handler terminated the wait command.

Remember, what we want is for the "hit me" message to print immediately after each USR1 signal.

So let's put another wait command in the signal handler...
#!/bin/bash

function dosomething()
{
	echo "Hit me with your best shot!"
	wait $sleeppid
}

trap dosomething SIGUSR1
mypid=$$
mycommand="kill -s SIGUSR1 $mypid"
echo $mycommand > $HOME/danger.sh
chmod a+x $HOME/danger.sh
sleep 30 &
sleeppid=$!
wait $sleeppid
echo FELL THROUGH
[slitt@mydesk ~]$ ./test.sh
Hit me with your best shot!
Hit me with your best shot!
FELL THROUGH
[slitt@mydesk ~]$
I issued the ./danger.sh command five times. The first time the "hit me" message printed instantly. Then the script did nothing more until the sleep terminated, after which it printed one more "hit me" message and then fell through and printed the final message. Ugh!!!


We were on the right track in the preceding script, but the new wait was in the wrong place. Let's try putting the wait in a loop

#!/bin/bash

function dosomething()
{
	echo "Hit me with your best shot!"
}

trap dosomething SIGUSR1
mypid=$$
mycommand="kill -s SIGUSR1 $mypid"
echo $mycommand > $HOME/danger.sh
chmod a+x $HOME/danger.sh
sleep 30 &
sleeppid=$!
loopvar=999999
while test $loopvar -ne 0; do
	wait $sleeppid
	loopvar=$?
done
echo FELL THROUGH
[slitt@mydesk ~]$ ./test.sh
FELL THROUGH
[slitt@mydesk ~]$ ./test.sh
Hit me with your best shot!
Hit me with your best shot!
Hit me with your best shot!
Hit me with your best shot!
Hit me with your best shot!
FELL THROUGH
[slitt@mydesk ~]$
Ah, now that's more like it. This version prints the "hit me" message immediately upon running ./danger.sh, and keeps printing it immediately every time. When the waited for sleep command finally terminates, the loop ends and we fall through.

It turns out that the signal interrupts the wait command, which returns 138 when so interrupted. It returns 0 when the waited-for process times out. So we just loop through waits until one returns 0.

Notice this is NOT polling. The loop is iterated only when a SIGUSR1 is received. Otherwise it sits and waits.

There's a negligibly slight possibility of an infinite loop bug with the preceding algorithm. This would occur if a USR1 hit somehow masked or overwrote the event of the sleep ending naturally. In that case, subsequent calls to wait would return 127, so the 0 would never happen, and it would loop forever. If it's an important script, test for both 0 and 127 to be absolutely sure.

I'm not sure, but I think this risk would become more likely if, within the loop,  there were a sleep command or other commands that take appreciable time.

The preceding script might be overkill, considering that you may simply wish to go past the wait when hit with an interrupt, in which case a single wait in the main routine would be sufficient. That being said, most of the algorithms used in my midi player used a loop that slept until hit with an interrupt, then spawned another program, then slept again. So the loop is a pretty useful algorithm.

There's one problem with the preceding script: The sleep command is a loose cannon. You have to set it for a very long time or else it might terminate on its own, enabling processing without the interrupt. If you set it to something that for practical purposes is forever (1000000 seconds is over 11 days, which is probably close enough to forever to fit the bill), then the sleep is left over, and nobody wants extra 11 day sleeps left hanging around. So kill it before terminating the program:

#!/bin/bash

function dosomething()
{
	echo "Hit me with your best shot!"
	
}

function terminate_loop()
{
	loopvar=0
}


trap dosomething SIGUSR1
trap terminate_loop SIGUSR2
mypid=$$
mycommand="kill -s SIGUSR1 $mypid"
echo $mycommand > $HOME/danger.sh
chmod a+x $HOME/danger.sh
mycommand="kill -s SIGUSR2 $mypid"
echo $mycommand > $HOME/danger2.sh
chmod a+x $HOME/danger2.sh
sleep 1000000 &
sleeppid=$!
loopvar=999999
while test $loopvar -ne 0; do
	wait $sleeppid
done
kill -s SIGHUP $sleeppid
echo FELL THROUGH
ps ax | tail -n 8
[slitt@mydesk ~]$ ./test.sh
Hit me with your best shot!
Hit me with your best shot!
FELL THROUGH
 7612 pts/10 S+ 0:00 /usr/bin/less -isr
 7718 ? S 0:00 kcalc
 7766 ? S 0:00 man bash 
 7986 pts/4 S+ 0:00 gimp
 7987 pts/4 S+ 0:01 dia
 7996 pts/6 S+ 0:00 /bin/bash ./test.sh
 8006 pts/6 R+ 0:00 ps ax
 8007 pts/6 R+ 0:00 tail -n 8
./test.sh: line 32: 7999 Hangup sleep 1000000
[slitt@mydesk ~]$
In the preceding, we ran danger.sh twice to hit it with USR1, and then ran danger2.sh to hit it with USR2, which terminated the loop. The final ps shows that the sleep is no longer running, due to the kill just before the script ends.

The preceding algorithm is perfect except for one thing: Can you guarantee to a certainty that the program will always fall through and execute the kill statement? Can you guarantee that three years from now when seven junior programmers have been doing maintenance on it? Why not kill the sleep the instant the USR2 is received:
x
#!/bin/bash

function dosomething()
{
	echo "Hit me with your best shot!"
	
}

function terminate_loop()
{
	kill -s SIGHUP $sleeppid
	loopvar=0
}


trap dosomething SIGUSR1
trap terminate_loop SIGUSR2
mypid=$$
mycommand="kill -s SIGUSR1 $mypid"
echo $mycommand > $HOME/danger.sh
chmod a+x $HOME/danger.sh
mycommand="kill -s SIGUSR2 $mypid"
echo $mycommand > $HOME/danger2.sh
chmod a+x $HOME/danger2.sh
sleep 1000000 &
sleeppid=$!
loopvar=999999
while test $loopvar -ne 0; do
	wait $sleeppid
done
echo FELL THROUGH
ps ax | tail -n 8
[slitt@mydesk ~]$ ./test.sh
Hit me with your best shot!
Hit me with your best shot!
FELL THROUGH
 7602 pts/10 S+ 0:00 man bash
 7605 pts/10 S+ 0:00 gimp
 7606 pts/10 S+ 0:00 dia
 7612 pts/10 S+ 0:00 /usr/bin/less -isr
 7718 ? S 0:00 kcalc
 7766 ? S 0:00 timidity ./test2.mid 
 8504 pts/6 S+ 0:00 /bin/bash ./test.sh
 8519 pts/6 R+ 0:00 ps ax
./test.sh: line 32: 8507 Hangup sleep 1000000
[slitt@mydesk ~]$
This works the same way, but you know that no matter what kind of break statements are used in the loop, the sleep command will terminate.

Of course, if an untrapped signal terminates the script, sleep will live on forever (or for 11 days).

So let's trap all interrupts, and have all interrupts except USR1 and USR2 kill the sleep command, and then exit immediately (no fallthrough). It's very important they terminate the program so that it can be terminated. Imagine if you couldn't Ctrl+C a program or kill it with a kill command. Now that would be ugly. So here we trap all interrupts, and have all except USR1 and USR2 simply kill the sleep and then exit immediately:
x
#!/bin/bash

function dosomething()
{
	echo "USR1 encountered"
	
}

function terminate_loop()
{
	echo "USR2 encountered"
	kill -s SIGHUP $sleeppid
	loopvar=0
}

function exit_cleanly()
{
	echo "Misc signal encountered"
	kill -s SIGHUP $sleeppid
	exit 1
}


trap exit_cleanly SIGHUP
trap exit_cleanly SIGINT
trap exit_cleanly SIGQUIT
trap exit_cleanly SIGILL
trap exit_cleanly SIGTRAP
trap exit_cleanly SIGABRT
trap exit_cleanly SIGBUS
trap exit_cleanly SIGFPE
trap exit_cleanly SIGKILL
trap exit_cleanly SIGUSR1
trap exit_cleanly SIGSEGV
trap exit_cleanly SIGUSR2
trap exit_cleanly SIGPIPE
trap exit_cleanly SIGALRM
trap exit_cleanly SIGTERM
trap exit_cleanly SIGSTKFLT
trap exit_cleanly SIGCHLD
trap exit_cleanly SIGCONT
trap exit_cleanly SIGSTOP
trap exit_cleanly SIGTSTP
trap exit_cleanly SIGTTIN
trap exit_cleanly SIGTTOU
trap exit_cleanly SIGURG
trap exit_cleanly SIGXCPU
trap exit_cleanly SIGXFSZ
trap exit_cleanly SIGVTALRM
trap exit_cleanly SIGPROF
trap exit_cleanly SIGWINCH
trap exit_cleanly SIGIO
trap exit_cleanly SIGPWR
trap exit_cleanly SIGSYS

trap dosomething SIGUSR1
trap terminate_loop SIGUSR2
mypid=$$
mycommand="kill -s SIGUSR1 $mypid"
echo $mycommand > $HOME/danger.sh
chmod a+x $HOME/danger.sh
mycommand="kill -s SIGUSR2 $mypid"
echo $mycommand > $HOME/danger2.sh
chmod a+x $HOME/danger2.sh
sleep 1000000 &
sleeppid=$!
loopvar=999999
while test $loopvar -ne 0; do
	wait $sleeppid
done
echo FELL THROUGH
ps ax | tail -n 8
[slitt@mydesk ~]$ ./test.sh
USR1 encountered
USR1 encountered
USR2 encountered
FELL THROUGH
 7612 pts/10 S+ 0:00 /usr/bin/less -isr
 7718 ? S 0:00 kcalc
 7766 ? S 0:00 timidity ./test.sh 
 8534 pts/3 Sl 0:01 gimp
 8537 pts/3 S 0:00 gvim test.sh
 8541 pts/3 S 0:01 dia
 8661 pts/6 S+ 0:00 /bin/bash ./test.sh
 8669 pts/6 R+ 0:00 ps ax
[slitt@mydesk ~]$ ./test.sh
Misc signal encountered
[slitt@mydesk ~]$
After running the script, from another terminal I hit it with two USR1 signals and one USR2, which killed the sleep and caused the loop to terminate.

I then ran it again and pressed Ctrl+C, which sent a SIGINT to the script. SIGINT was trapped to exit_cleanly(), which killed sleep and then exited immediately.

Notice that USR1 and USR2 were trapped twice, and that the later traps overrode the earlier ones. That's important.

Notice also that I could have done all the miscellaneous trapping in a single statement, listing all the interrupts after the handler routine. That's perfectly legal, but it makes for a very long line, and also makes it more difficult when you want to use binary search to find out what signal is hitting you. More on that later.

There are more than one way to skin a cat. Trapping every single signal to exit_cleanly() is a pain, and besides, heaven help you if forget a signal. If you notice, I forgot about signals SIGRTMIN, SIGRTMIN+1, SIGRTMIN+2 and the like. If one those signals had terminated the script, exit_cleanly() would not have occurred.

Another way to do it is to create an exit handler for the script, so that the exit handler is executed on script termination, no matter how terminated. This is done by trapping "signal" EXIT, which isn't really a signal, but it puts the script on notice to perform the associated handler at program termination. The syntax looks like this:
trap on_exit EXIT
In the preceding, function on_exit() would be performed upon script termination. Here's our script modified so sleep is killed by the exit routine instead of on every exit except USR1 and USR2:

#!/bin/bash

function dosomething()
{
	echo "USR1 encountered"
	
}

function terminate_loop()
{
	echo "USR2 encountered"
	kill -s SIGHUP $sleeppid
	sleeprunning="no"
	loopvar=0
}

function exit_cleanly()
{
	echo "Running exit routine"
	if test "$sleeprunning" = "yes"; then
		 kill -s SIGHUP $sleeppid
		 echo "Sleep $sleeppid killed."
		 else
		 exit 0
	 fi
}

trap exit_cleanly EXIT

trap dosomething SIGUSR1
trap terminate_loop SIGUSR2
mypid=$$
mycommand="kill -s SIGUSR1 $mypid"
echo $mycommand > $HOME/danger.sh
chmod a+x $HOME/danger.sh
mycommand="kill -s SIGUSR2 $mypid"
echo $mycommand > $HOME/danger2.sh
chmod a+x $HOME/danger2.sh
sleep 1000000 &
sleeppid=$!
loopvar=999999
sleeprunning="yes"
while test $loopvar -ne 0; do
	wait $sleeppid
done
echo FELL THROUGH
ps ax | tail -n 8
[slitt@mydesk ~]$ ./test.sh
USR1 encountered
USR1 encountered
USR2 encountered
FELL THROUGH
7612 pts/10 S+ 0:00 /usr/bin/less -isr
7718 ? S 0:00 kcalc
8534 pts/3 Sl 0:01 gimp
8537 pts/3 S 0:00 timidity ./test.mid
8541 pts/3 S 0:01 dia
8917 ? S 0:00 gvim litt.c
8999 pts/6 S+ 0:00 /bin/bash ./test.sh
9006 pts/6 R+ 0:00 ps ax
./test.sh: line 47: 9002 Hangup sleep 1000000
Running exit routine
[slitt@mydesk ~]$ ./test.sh
Running exit routine
Sleep 9012 killed.
[slitt@mydesk ~]$
Function exit_cleanly() is changed so that it kills $sleeppid only if $sleeprunning is "yes". That variable is set to "yes" when sleep is run, and set to "no" when the USR2 function kills sleep. In this way, there's never an attempt to kill an already killed sleep. Remember, in this version exit_cleanly() runs no matter what causes the program to end.

The trap statement now sports a signal spec of EXIT, the reserved word for setting an exit routine (on_exit handler, etc).

Anonymously Trapping a Signal

If you want to simply make a script immune to a certain signal, you can trap it anonymously like this:
trap "" SIGUSR1
This really makes the script immune to the signal, because unlike traps that go to a function, this trap doesn't even go past the current wait or foreground command.

Reaching Back

Sometimes it's desired to have a child process contact its parent with a signal. That's easy to do. Here's the parent:
#!/bin/bash

function usr1_handler()
{
	echo "Parent received a USR1"
}

trap usr1_handler SIGUSR1
echo "Starting parent..."
mypid=$$
./child.sh &
childpid=$!
echo "Parent sees childpid as $childpid"
echo "Parent is about to sleep 3"
sleep 3
echo "Parent woke up from 3 second sleep"
echo "Parent is about to send USR1 to child"
kill -s SIGUSR1 $childpid
wait $childpid
echo "Parent finished"
The parent traps USR1 into issuing a message saying it got a USR1. It then issues lots of echos to show its progress. It runs child.sh in the background, captures the child's PID, sleeps for 3 seconds, and then sends a USR1 to teh child, and waits on the child for a response.

And here's the child:
#!/bin/bash
function usr1_handler()
{
	echo "Child received USR1"
	echo "Child about to kill sleep"
	kill -s SIGHUP $sleeppid
	echo "Child killed sleep"
	echo "Child about to send a USR1 to parent"
	kill -s SIGUSR1 $parentpid
}

trap usr1_handler SIGUSR1
echo "Child starting"
mypid=$$
parentpid=$PPID
sleep 1000000 &
sleeppid=$!
wait $sleeppid
echo "Child about to sleep 4"
sleep 4
echo "Child now terminating after 4 second sleep"
The child sets a handler to issue a message, then  kill its sleep, issue some more messages then send a USR1 to its parent.

The body of the child issues a start message, captures the parent ID, then sleeps in the background for 11 days, and waits on the sleep.

Sooner or later the parent wakes up the child with a USR1. That terminates the wait, so the child issues a message, sleeps for 4 seconds, and then issues a termination message before terminating.

Here's the output of the parent and child processes:
[slitt@mydesk ~]$ ./parent.sh 2>/dev/null
Starting parent...
Child starting
Parent sees childpid as 10109
Parent is about to sleep 3
Parent woke up from 3 second sleep
Parent is about to send USR1 to child
Child received USR1
Child about to kill sleep
Child killed sleep
Child about to send a USR1 to parent
Child about to sleep 4
Parent received a USR1
Parent finished
[slitt@mydesk ~]$ Child now terminating after 4 second sleep

The parent starts, issues a start message, and then starts the child in the background. The child immediately issues its starting message, saves its and its parent's PIDs, and goes into an 11 day sleep.

Meanwhile, the parent is still working. It issues messages about the child's PID and the fact that it will sleep for 3 seconds, and then runs sleep for 3 seconds.

You can't see it in the printout to the left, but after the parent issues the message about sleeping for 3 seconds, nothing happens for 3 seconds.

Then the parent wakes up, announces it has awoken and that it's going to send a USR1 to the child, and then sends the USR1 to the child. Immediately after, the parent waits on the child.

Meanwhile, due to being hit with the USR1 from the parent, the child's USR1 handler prints that it received the USR1, it's about to kill its sleep. Then it kills the sleep, logic drops through below its wait, and it prints that it's about to send a USR1 to the parent and that it will now sleep for 4 seconds, which it does.

Meanwhile, the parent has received the USR1 from the child. That USR1 aborts the wait, so the parent falls through and writes "parent finished" and terminates.

But due to its 4 second sleep, the child is still a process. After 4 seconds its sleep is over and it prints its exit message, and then terminates. That's why the child's exit message appears after the parent's exit message.

The preceding was a contrived example of a parent sending a USR1 to its child, and the child sending one back. Note that it's a coincidence that the child sent back the same signal it received -- it could have sent back a different signal and everything would have been the same, as long as the parent were set up to receive the different signal.

I can think of two excellent reasons why a child might want to signal its parent:
  1. To tell the parent that it's finished.
  2. To tell the parent there's a message from the child in a file or FIFO.
The preceding two reasons would not be restricted to a child to parent communication. They could be communicated to a third party of any kind, as long as the child had the third party's PID. Actually, none of the parties need be children of any of the others, as long as the senders had the receivers PIDs.

You could actually create a bucket brigade where A signals B, then B signals C, then C signals D. Perhaps then D would even signal A and it would circle forever. I'd be careful of overly complex interprocess communication though -- it provides much opportunity for bugs, and personally I see no compelling reason to do it other than "its kewl", which is great for a LUG installfest, but not for an application you'll actually have to use.

Who Signalled Me?

Everything discussed so far is wonderful when the algorithm defines who is sending what to whom. But what if it's more asyncronous? How does a process know which of many running processes signaled it, and what the signalling process wanted to communicate?

This isn't perfect, but if the signalling process writes its PID to a file named after the signalled process, the signalled process can read the file and find the PID of the signaller. Unless signals are coming in very densely, this should work. Meanwhile, the signaller has written its message to a file named after both the signaller and signalee, which is pretty unabiguous unless a given process signals another given process multiple times quickly.

For simplicity, the following example has a parent and child, but nothing in this example uses the fact that they have that relationship -- the signalling between them could be any two random processes, as long as the signaller knows the PID of the signallee:
#!/bin/bash

mypid=$$
pid2signal=$1
sleeptime=$2
message=$3
commfilename=${pid2signal}.incoming
commfilename2="$pid2signal$mypid.incoming"
sleep $sleeptime

echo $message > $commfilename2
echo $mypid > $commfilename
kill -s SIGUSR1 $pid2signal
This is the "child" process, although it could have a random relationship with the process it's calling. It gets all its information on the command line. The PID of the process to signal is arg1, the time to sleep before signalling is arg2, and the text message to send is arg3.

It constructs two filenames. The first is the PID to signal followed by .incoming. The second is a concatination of the PID to signal and the current process's PID, followed by .incoming.

It sleeps the allotted time, then writes the message to the concatted filename, writes its PID to the other filename, and signals USR1 to the PID to signal.

Notice that this script writes nothing to the screen. Anything that gets written to the screen is written by the parent, which is the signallee.

To summarize, after a sleep period defined by a command line arg, it writes its PID to one file and its message to another, and sends the signal.

The parent (signallee) follows:
#!/bin/bash

function usr1_handler()
{
	echo "Parent received a USR1"
	fn_incoming=${mypid}.incoming
	sender=$(cat $fn_incoming)
	rm -f $fn_incoming
	echo "The message was sent from $sender"
	echo -n "The message is: "
	cat $mypid$sender.incoming
	rm -f $mypid$sender.incoming
}

trap usr1_handler SIGUSR1
echo "Starting parent..."
mypid=$$
./child.sh $mypid 5 first &
sleep 0.2
./child.sh $mypid 3 second &
sleep 0.2
./child.sh $mypid 1 third &
loopvar=9999
while true; do
	sleep 100000000 &
	sleeppid=$!
	echo "Waiting for interrupt"
	wait $sleeppid
done
echo "Parent finished"
You can think of this as the parent, but what it really is is the process that gets signalled by an anonymous process and has to find the message, even though many processes might be sending it messages.

The signal handler quickly grabs the contents of the file with a name that is current processes PID followed by .incoming. If you recall, this is the file to which the child wrote its PID.

Once the parent has the PID of the signaller, it concatinates its own PID with that of the sender to deduce the filename of the message, and reads the message.

The main routine spawns child.sh three times without grabbing child PIDs, so it has no immediate way of knowing which child signalled it at any given time. The messages for the three child.sh are in order, but the sleep times go down, meaning that the third child will signal first, then the second child, then the first. So if everything works as expected, the parent will first write the message of the third child, then that of the second, and then that of the first.

The loop at the bottom is infinite, so the way to break out of this program is with a Ctrl+C.

So let's see if it works as expected...

[slitt@mydesk ~]$ ./parent.sh
Starting parent...
Waiting for interrupt
Parent received a USR1
The message was sent from 12216
The message is: third
Waiting for interrupt
Parent received a USR1
The message was sent from 12213
The message is: second
Waiting for interrupt
Parent received a USR1
The message was sent from 12210
The message is: first
Waiting for interrupt
Perfect. The messages come back in reverse order, as expected. This program is getting messages from several anonymous processes, and is keeping them straight by immediately looking in the proper file in order to find the PID of the signaller. Armed with the signaller's PID, it can concatinate that PID with its own to read the message sent from the signaller.

I'm not saying this is foolproof. If signals are coming in several times per second it's possible that while process A is reading a message from process B, process C signals and then process D overwrites it. I don't know how reentrant this stuff is, but for the purposes of Application Assembly using Small Executables, we don't need to worry about extremely frequent messages. The important thing is to get the information quickly so as to be ready for the next interrupt.

Troubleshooting Signal Based Interprocess Communication

First of all, use the Universal Troubleshooting Process. Beyond that, let me offer a few tips specifically tailored to signal based communication between shellscript processes:

man bash is your friend

AAuSE is, in many ways, a new language. Using it effectively means learning all the features bash has to offer, how to use them, and how to avoid misusing them. The bash man page, all 4800 lines of it, is your friend.

Make an effective kill script early

When backgrounding processes and working with signals, I guarantee you that during debugging, processes will be left running long after the app is done. Debugging my midi player, several times there was a bug where I exited the main program, yet the songs kept playing on, and on, and on, multiplying on and on -- a fork bomb. You'd kill timidity and another one would run, playing the next tune, over and over again.

What was happening was that a session of my program was still running, unconnected to any UMENU. Sometimes there were several such lost-at-sea sessions of the program, each playing their own song. What was necessary was to kill all my processes, all the timidities, and all the sleeps produced by my program (but not sleeps produced by other programs).

Consider this script (call it nuke.sh):
#!/bin/bash
ps ax | grep "$1" | grep -v grep | cut -b1-5 | xargs kill
The preceding lists all processes, greps for arg1, eliminates the grep itself, cuts each line down to the first 5 columns, which correspond to the PID, and then sends those PIDs to the kill command. So to kill everything produced by my midi player, I might do something like this:
nuke.sh '/bin/bash.*/littmidi/play\.sh$'
nuke.sh 'nuke.sh 'timidity /scratch'
nuke.sh 'sleep[[:space:]][[:space:]]*1000000'
The first line kills all my play.sh processes. Because play.sh might not be a unique scriptname, I include the subdirectory it's in, and just for fun I include the /bin/bash in the shebang. The dollar sign at the end is a regular expression to avoid something like play.sharon.jones. The one thing you want to avoid is killing the wrong thing. While researching this material I accidentally killed X, blowing off about 5 content-containing programs, including the NVU session in which I was writing this magazine.

The second line kills all my timidities, hopefully without killing others, which is why I included the base of the subdirectory tree containing all my midis.

The third kills all my sleeps. I could have used killall sleep, but that might have killed some sleeps that were really needed. In hindsight what I should have done is have my sleep command sleep an unusual number of seconds, like 4321234.

In general, it's a good idea to have something unique in every command that might need killing. That way they can be killed with pinpoint accuracy.

Unit test

This is nothing unique to AAuSE. You should unit test everything. The good news is that shellscripts and small executables are much easier to unit test because they have fewer inputs and outputs, and fewer opportunities for side effects.

So for each shellscript or small executable you write, create a small test shellscript to put it through its paces. The shellscript should perform various legal and illegal inputs, and look at outputs such as stdout, ps ax, and on-disk persistent variables to see the effects. On a more complex shellscript or executable a log can be created.

Unit test scripts can be kept in a unit directory below the application's script directory, so they're not confused with actual application scripts.

Use ps, and use it right

To see all processes do this:
ps ax
To see all processes and their parent PIDs and other info, do this:
ps ax -f
To see a text-art tree of processes and their descendents (a process tree), do this:
ps axf
To see the process tree with parent PIDs and other info, do this:
ps axjf
These are just a few of the handy ps commands. See the ps man page for more ideas.

Process filenames might not be right, so use caution. For instance, when my play.sh runs spawn.sh in the background, somehow the ps command picks it up as another play.sh.

Diagnostic statements and logs

The documentation says that stdout doesn't print to the screen on background processes. That hasn't been my experience, at least with processes backgrounded by a trailing ampersand (&). I find that echo statements of such backgrounded processes show up very nicely on the screen, and hence can be used for diagnostics.

If your system really doesn't show echo statements from background processes, just append them to the end of log files, maybe with a timestamp.

I prefer to have all diagnostic statements start with "dia" or "diaX", where "X" is a number, so that they're easy to spot and easy to grep and easy to remove when the time comes. When diagnosing signalling problems, try to see which operations in which scripts happen in which order, and keep devising new diagnostic tests to narrow down the scope of the problem.

kill -l

You can find out all the signals your system uses with this command:
kill -l
If you want to format that list nicely so it can easily be turned into trap commands, use this:
kill -l | sed -e 's/[[:digit:]]\+)/ /g' | sed -e 's/[[:space:]]\+/\n/g' | grep -v "^\s*$"
I'm sure there's a shorter expression, but that's the best I could do in 10 minutes. Obviously if you want to convert it into traps, you'll need to append at least one more pipe to insert trap ""  at the beginning of each line.

Binary search signals, or build test a routine

Which signal does Ctrl+C send? You can trap all the signals to a handler that just says "Interrupt received" and ascertain that the message appears on Ctrl+C. Then you can comment out half and try again. If the message disappears, uncomment half of those and try again, etc. Be sure that as you rule out signals, you move them to an area reserved for ruled out signals.

Here are a shellscript and a short Ruby program you can use to construct a test routine for interrupts:
list_signals.sh
#/bin/bash
kill -l | sed -e 's/[[:digit:]]\+)/ /g' | sed -e 's/[[:space:]]\+/\n/g' | grep -v "^\s*$"

make_signaltest.rb
#!/usr/bin/ruby -w 

signalarray= STDIN.readlines

signalarray.each do
	|line|
	line.chomp!
	line.strip!
end

signalarray.each do
	|signal|
	puts "function #{signal}_handler()"
	puts "{"
	puts " echo Received #{signal} signal."
	puts " kill $sleeppid"
	if signal == "SIGKILL"
	puts " echo Terminating on SIGKILL"
	puts " exit 0"
	end
	puts "}"
	puts
end

puts

signalarray.each do
	|signal|
	puts "trap #{signal}_handler #{signal}"
end

puts "mypid=$$"
puts
puts "echo This is process number $mypid"
puts "echo Hit with signals to diagnose, kill -9 to terminate."
puts "while true; do"
puts " sleep 100000000 &"
puts " sleeppid=$!"
puts " echo -n \"Process $mypid waiting for interrupt: \""
puts " wait $sleeppid"
puts "done"

You make your signal tester like this:
./list_signals.sh | ./make_signaltest.rb > signaltest.sh
In the preceding, signaltest.sh is a script that, on receipt of any signal except SIGKILL, issues a message about what message was sent:
[slitt@mydesk ~]$ ./signaltest.sh
This is process number 15924
Hit with signals to diagnose, kill -9 to terminate.
Process 15924 waiting for interrupt: Received SIGINT signal.
Process 15924 waiting for interrupt: Received SIGHUP signal.
Process 15924 waiting for interrupt: Received SIGUSR1 signal.
Process 15924 waiting for interrupt: Received SIGUSR2 signal.
Process 15924 waiting for interrupt: Received SIGALRM signal.
Process 15924 waiting for interrupt: Received SIGRTMAX-4 signal.
Process 15924 waiting for interrupt: Killed
[slitt@mydesk ~]$

When you send a SIGKILL or do kill -9, the program aborts.

Summary

Signals enable independently running processes to communicate with each other without resorting to complex, wasteful polling algorithms. The only thing process A needs in order to signal process B is process B's process ID (PID).

A bash shellscript can always determine the PIDs of its parent, itself and its latest spawned background child ($PPID, $$ and $! respectively). Those variables can be saved so  even if future events overwrite those variables, the PIDs will still be available. If those PIDs are needed by other processes, they can be saved to files on disk.

If you need to, you can test whether a process is still running with this command:
ls -ldF /proc/$timidity_pid > /dev/null 2>&1
$stillrunning=$?
A zero value means it's running, while a nonzero value means it's not.

A script receiving a signal terminates immediately, unless the script has trapped that signal. The way to trap a signal is to write a signal handler for that signal, and then use the trap builtin command, as follows:
function usr1_handler()
{
	echo "Child received USR1"
}

trap usr1_handler SIGUSR1

Upon receiving the signal, the current foreground process gets assigned to the background, or if the script is on a wait command, the wait is skipped over. Then the handler runs. When coding, you must not only make the handler right, but you must code the main routine to account for the fact that it will background the current foreground or skip over a wait. If you want to make a script totally immune to a signal, use an anonymous trap:

trap "" SIGUSR1

When using sleep and wait to wait for a signal, be sure the signal's handler kills the sleep, or the sleep will persist, as an independent process, for its entire million seconds or whatever its duration was set to.

To trap all interrupts you could list them all with anonomous handlers or with a single "donothing" handler. If what you really wanted to do was make sure that no matter how the program ended, it cleaned up (like killing any children) before exiting, you could make an exit routine instead, trapping it to the reserved signal substitute EXIT:

function exit_routine()
{
	kill $sleeppid
	kill $spawnedpid
}

trap usr1_handler EXIT

One handy technique is for the child to signal the parent when it finishes. It can even send different signals, depending on whether it terminated normally or was signalled itself. A child might also signal the parent to send some sort of message (in a file, presumably).

The process doing the signalling must know the PID of the process it's signalling. However, the process receiving the signal might not know who signalled it. If signaller and signallee use the same intermediate files, the signallee can know who signalled it and what information is being passed. The signaller writes its PID to a file with the signallee's PID in its filename. Then the signallee reads that file and now knows who signalled him. Based on that, the signallee now concatinates PIDs to form a filename containing the message that the signaller wrote.

When troubleshooting a signalling situation, or pretty much any Unix interprocess problems, these are some techniques you can use:
Steve Litt is the author of Twenty Eight Tales of Troubleshooting.   Steve can be reached at his email address.

The Next Feature To Add

By Steve Litt
Midi files are different. 90% of them are for music you've never heard before. Even with the other 10%, they can be done very well, very badly, or in between. Some midis are masterpieces and some are dogs. Some are good for quiet time, and some are good to pump you up. Ultimately there must be a way to report on and sort these songs in order to create appropriate playlists.

Over the course of time, I need to evaluate these songs. Because I've never heard them before, the titles mean nothing to me, so I need to identify them by filename when evaluating. Your mileage may differ, but I cannot say whether I like a song until I've heard it several times, and often even then I change my mind on the song later on. Therefore these evaluations must be available for modification.

The evaluations must have both numeric ratings (beat 1 to 5, production quality 1 to 5) and narrative ratings (has a great beat, I think I can dance to it), categories or genres, and keywords. Given these attributes in each evaluation, it becomes possible for me to run reports in order to create specialized midi playlists for specialized needs. If nothing else, I can create playlists of my very best midis when that's what I want to hear, and playlists excluding the dogs for the times when I want wide ranging material but not poorly produced or badly played material.

Given that I don't yet have a form executable, the reports will most likely be filled out in an editor: default Vim. The editor will probably be spawned in a similar manner to the spawning of timidity.

Evaluation storage is the next question. Should it be stored in an outline, an XML, NoSQL, MySQL, Postgres? With the preceding, how should it be backed up?

The evaluation process will go right into the midi player, probably in the "Special Functions" menu tree. Choosing "Evaluate current song" grabs the song number and playlist out of persistent storage and uses songnum2name.sh to deduce the song name, which will probably, one way or another, be a no-duplicates index for the reports. It probably won't be the primary key because primary keys should be meaningless. If I changed a midi file's directory and the filename were the primary key, it would raise hell with the database.

Anyway, armed with the filename, the database would be searched for the file's evaluation, and if it exists the evaluation would be brought up in Vim or some other editor (probably the choice of editor would be made in a shellscript). The evaluation would be modified, and then if it is changed, it is saved to the database. If the database has no evaluation for the file, an empty "form" would be brought up in Vim, and if the form's contents change, the form's contents would be saved (as numeric answers, categories, keywords and prose) to the database.

Over time more and more songs would get evaluations.

This would be dead-bang easy if the evaluation forms were simply saved as text. Parsing them and finding the best database schema to accommodate them will be the most difficult part of the feature addition. Parsing of the eval form will require some Ruby, as will creating a form from an existing database record, and the form's structure needs to be designed, but these are not difficult things.

This feature points up some of the power of AAuSE. I dare you to just "bolt on" a new feature to a C or even Ruby app. Especially a feature using Vim and Postgres. AAuSE is less like programming and more like plugging software ICs into a software protoboard.
Steve Litt has been writing Troubleshooting Professional Magazine for ten years. Steve can be reached at his email address.

The Centralized Bash Midi Player

By Steve Litt
The Ruby based midi player featured a Ruby program that ran continuously, receiving user commands from UMENU through the FIFO. The first shellscript based midi player was built differently. Its play script played the next song as soon as the previous song ended, but it could not handle user requests. Instead, UMENU killed the play script, set the proper playlist and song number in persistent storage, and reran the play script. That's a very simple program to write, but it has one disadvantage: You can't see the output from timidity, because the play script is killed and rerun every time. Timidity shows the song's title, who wrote it, what instruments are featured, and what instruments cannot be heard.

So I wrote yet another version. This all-shellscript version runs continuously and handles user commands, like the Ruby version. It plays in a specific terminal and is controlled from a different terminal, so you can see timidity doing its thing. It uses most of the small shellscripts I wrote for the first shellscript based midi player.

Instead of getting its commands by polling a FIFO, this new version receives a SIGTRAP signal, after which it looks at the value of the fcn key of persistent storage, and performs that function within a case statement very similar to the one in the Ruby version. It also uses the songno and playlist keys of persistent storage for some functions.

I made a couple small changes to a couple small shellscripts. These changes do not affect the decentralized shellscript version. The jump2song.sh script was changed so if the user cancels out of the song picklist, it returns 1, and if the user selects a song, it returns 0. That relieves the program from having to check persistent storage, so it's both easier and more modular. The killsong.sh script was changed so it verifies the PID is running before killing it. The killsong.sh script also now returns 0 if it actually killed a PID, and 1 if that PID wasn't running in the first place.

Speaking of killsong.sh, there's a bug involving it. Under certain circumstances that I can no longer duplicate, it did not appear to kill the spawner, so I had to substitute a hard coded kill command to the PID of the spawner. That problem went away, but I left a comment in the code should it ever return.

The Tale of the Tape

Let's review the tale of the tape. The Ruby central program, frontmidi.rb, is 272 lines of Ruby. The shellscript central program, superplay.sh, is 104 lines. However, the sum of lines in all of the shellscript version's shellscripts adds up to 271 lines, so the centralized version saves no coding. The decentralized shellscript version uses the shorter play.sh instead of superplay.sh, so it weighs in at 211 lines, but it requires more code in the EMDL file.

In terms of lines of code, there's little difference between the three. I personally believe the shellscript versions are simpler, but I'm not sure the difference is compelling.

The Ruby version uses polling rather than being interrupt driven, and that's somewhat sloppy. However, the polling algorithm works just fine, especially when fitted with a couple strategic and short sleep commands. Also, if I'd known then what I know now, I could have made the Ruby version interrupt driven also.

Personally, I happen to like the bash versions much better. Your mileage may vary.

Show Me the Code

OK, but first be aware that the centralized shellscript version uses most of the same small shellscripts as detailed in the article about the first shellscript based midi player.

The center of this midi player is the persistent superplay.sh, so let's look at that first:
#!/bin/bash

function on_usr1 ()
{
	echo "Song ended normally."
	persist "fcn=normal" $HOME/littmidi_b.state
	kill -s SIGHUP $sleeppid
}

function on_usr2 ()
{
	echo "Process was killed!"
}

function on_trap ()
{
	echo "Process was interrupted by a new function!"
	kill -s SIGHUP $sleeppid
}

trap on_usr1 SIGUSR1
#trap on_usr2 SIGUSR2
trap "" SIGUSR2
trap on_trap SIGTRAP

playerpid=$$
echo Player PID is $playerpid
persist "playerpid=$playerpid" $HOME/littmidi_b.state
persist "fcn=normal" $HOME/littmidi_b.state
pause="false"
while true; do
	if test "$pause" = "false"; then
		songno=$(persist "songno=?" $HOME/littmidi_b.state)
		songname=$(./songnum2name.sh $songno)
		echo songname=$songname
		command="timidity $songname > /dev/null"
		echo Command=$command
		./spawn.sh "$command" &
		spawnpid=$!
		persist "spawnpid=$spawnpid" $HOME/littmidi_b.state
	else
		pause="false"
	fi

	### Next line sleeps forever, relies on a signal
	### to kill it.
	sleep 1000000 &
	sleeppid=$!
	wait $sleepid

	### Kill the current spawner
	killsong.sh # BUG: Why does this not always do the job?
	# kill -s SIGHUP $spawnpid # Do this if killsong.sh doesn't work

	### Handle functionalities
	fcn=$(persist "fcn=?" $HOME/littmidi_b.state)
	case "$fcn" in
		("next")
			echo "NEXT SONG FORCED"
			increment_songno.sh
		;;
		("prev")
			echo "PREVIOUS SONG FORCED"
			decrement_songno.sh
		;;
		("this")
			### songno remains the same
		;;
		("normal")
			echo "NORMAL SONG INCREMENTATION"
			increment_songno.sh
		;;
		("first_song")
			set_songno.sh 1
		;;
		("last_song")
			set_songno.sh $(persist "numsongs=?" $HOME/littmidi_b.state)
		;;
		("set_song")
			### songno set by signaller process
		;;
		("set_playlist")
			### songno and playlist set by
			### signaller process
		;;
		("pause")
			pause="true"
		;;
		("unpause")
			pause="false"
		;;
		("quit")
			break
		;;
		(*)
			echo "UNEXPECTED FCN=$fcn"
			break
		;;
	esac

done
It's a bash script

SIGUSR1 sent by spawner when a song finishes.
Set function to normal
Kill the sleep, and proceed from after the wait.



SIGUSR2 is ignored by trapping an empty string.
This subroutine is not used, but is here only
for possible debugging.



SIGTRAP is sent by UMENU after UMENU has
changed the persistent fcn value and possibly
the song number and/or playlist. Kill the
sleep and proceed with the algorithm.


SIGUSR1 means song has finished
SIGUSR2 is ignored. The spawner normally sends
this signal if it receives a SIGHUP, but that's
not needed.
SIGTRAP is sent by UMENU to indicate a new
command is ready.

Persist the PID so UMENU knows who to SIGTRAP

Not paused
Loop: Wait for next command or end of song,
and act appropriately.

Don't spawn if paused.







Any other function unpauses. Prevents user
confusion.



Wait for interrupt.




Kill current spawner, and therefore current
timidity, in preparation for new command.


Get fcn from persistent storage
Case: do the right thing.












Normal mode means the current song finished 
and you want to play the next one.











Does nothing because UMENU called 
set_playlist.ui.sh to set playlist and songno.


If user wants to pause, set the $pause flag.


Redundent, because the 
if test "$pause" = "false" test resets $pause
if it's true. This logic is a hook for if
that reset is removed.

Break out of the loop if fcn is quit.

Now let's look at the EMDL that created the menu:

ZZC:::Litt's Text Midi Frontender (Centralized bash version)
Run Litt's text midi player
	param
		D: /d/at/bash/littmidi
		C: change_playlist_ui.sh;
		C: action=$(persist "action=?" $HOME/littmidi_b.state);
		C: if test "$action" = "select"; then
		C:   playerpid=$(persist "playerpid=?" $HOME/littmidi_b.state);
		C:   persist "fcn=quit" $HOME/littmidi_b.state;
		C:   kill -s SIGTRAP $playerpid;
		C:   persist "fcn=set_playlist" $HOME/littmidi_b.state;
		C:   aterm -e '/d/at/bash/littmidi/superplay.sh' &
		C:   echo Loading player...;
		C:   sleep 1;
		C: fi; 
		S: 1
pAuse
	param
		C: playerpid=$(persist "playerpid=?" $HOME/littmidi_b.state);
		C: persist "fcn=pause" $HOME/littmidi_b.state;
		C: kill -s SIGTRAP $playerpid;
Unpause
	param
		C: playerpid=$(persist "playerpid=?" $HOME/littmidi_b.state);
		C: persist "fcn=unpause" $HOME/littmidi_b.state;
		C: kill -s SIGTRAP $playerpid;
Beginning of song
	param
		C: playerpid=$(persist "playerpid=?" $HOME/littmidi_b.state);
		C: persist "fcn=this" $HOME/littmidi_b.state;
		C: kill -s SIGTRAP $playerpid;
First song
	param
		C: playerpid=$(persist "playerpid=?" $HOME/littmidi_b.state);
		C: persist "fcn=first_song" $HOME/littmidi_b.state;
		C: kill -s SIGTRAP $playerpid;
Next
	param
		C: playerpid=$(persist "playerpid=?" $HOME/littmidi_b.state);
		C: persist "fcn=next" $HOME/littmidi_b.state;
		C: kill -s SIGTRAP $playerpid;
Previous
	param
		C: playerpid=$(persist "playerpid=?" $HOME/littmidi_b.state);
		C: persist "fcn=prev" $HOME/littmidi_b.state;
		C: kill -s SIGTRAP $playerpid;
Last song
	param
		C: playerpid=$(persist "playerpid=?" $HOME/littmidi_b.state);
		C: persist "fcn=last_song" $HOME/littmidi_b.state;
		C: kill -s SIGTRAP $playerpid;
Jump to song
	param
		C: playerpid=$(persist "playerpid=?" $HOME/littmidi_b.state);
		C: jump2song.sh;
		C: if test $? -eq 0; then
		C:   persist "fcn=set_song" $HOME/littmidi_b.state;
		C:   kill -s SIGTRAP $playerpid;
		C: fi;
		S: 1
Change Playlist
	param
		C: playerpid=$(persist "playerpid=?" $HOME/littmidi_b.state);
		C: change_playlist_ui.sh;
		C: action=$(persist "action=?" $HOME/littmidi_b.state);
		C: if test "$action" = "select"; then
		C:   persist "fcn=set_playlist" $HOME/littmidi_b.state;
		C:   kill -s SIGTRAP $playerpid;
		C: fi; 
		S: 1
Kill Litts Midi Central Bash Player
	param
		C: playerpid=$(persist "playerpid=?" $HOME/littmidi_b.state);
		C: persist "fcn=quit" $HOME/littmidi_b.state;
		C: kill -s SIGTRAP $playerpid;
Zap zombie midi players
	param
		C: zapmidizombies;
		S: 1
Volume ::: Mplayer volume
	Louder
		param
			C: VOLM=$(persist "volume=?" $HOME/littmidi_b.state);
			C: if test "$VOLM" = ""; then
			C:   let VOLM=60;
			C:   persist "volume=$VOLM" $HOME/littmidi_b.state;
			C: fi;
			C: if test $VOLM -lt 100; then
			C:   let VOLM=$VOLM+1;
			C:   persist "volume=$VOLM" $HOME/littmidi_b.state;
			C:   aumix -v $VOLM -w $VOLM;
			C: fi;
			C: echo "Volume is $VOLM";
	Softer
		param
			C: VOLM=$(persist "volume=?" $HOME/littmidi_b.state);
			C: if test "$VOLM" = ""; then
			C:   let VOLM=60;
			C:   persist "volume=$VOLM" $HOME/littmidi_b.state;
			C: fi;
			C: if test $VOLM -gt 0; then
			C:   let VOLM=$VOLM-1;
			C:   persist "volume=$VOLM" $HOME/littmidi_b.state;
			C:   aumix -v $VOLM -w $VOLM;
			C: fi;
			C: echo "Volume is $VOLM";
	Mute
		param
			C: aumix -v 0 -w 0;
	Unmute
		param
			C: VOLM=$(persist "volume=?" $HOME/littmidi_b.state);
			C: if test "$VOLM" = ""; then
			C:   let VOLM=60;
			C:   persist "volume=$VOLM" $HOME/littmidi_b.state;
			C: fi;
			C: aumix -v $VOLM -w $VOLM;
			C: echo "Volume is $VOLM";
	Observe Volume
		param
			C: VOLM=$(persist "volume=?" $HOME/littmidi_b.state);
			C: echo Littmidi volume=$VOLM;
			C: aumix -q;
			S: 1
	seT volume to specific number (0-100)
		param
			C: ORG=$(persist "volume=?" $HOME/littmidi_b.state);
			C: VOLM=%1%Volume please (0-100)%%;
			C: echo "Original volume=$ORG";
			C: echo "$VOLM" | grep "[^[:digit:]]";
			C: NOTANUMBER=$?;
			C: test -z $VOLM;
			C: ZEROLENGTH=$?;
			C: if test "$NOTANUMBER" = "1" -a "$ZEROLENGTH" = "1"; then
			C:   persist "volume=$VOLM" $HOME/littmidi_b.state;
			C:   aumix -v $VOLM -w $VOLM;
			C:   echo "New volume is $VOLM";
			C: else
			C:   echo "Bad input: $VOLM";
			C:   echo "Volume unchanged at $ORG";
			C: fi;
			S: 1
	^Quit
Special functions ::: Special Functions Menu
	Killers ::: Killer functions
		Relevent processes
			param
				C: nuke_all.sh
				S: 1
		Play.sh
			param
				C: nuke_play.sh
				S: 1
		Sleep
			param
				C: ps ax | grep "sleep 1000000" | grep -v grep | cut -b1-5 | xargs kill;
		Timidity
			param
				C: killall timidity
		^Quit
	cLi Run Litt's text midi player
		param
			D: /d/at/bash/littmidi
			C: change_playlist_ui.sh;
			C: action=$(persist "action=?" $HOME/littmidi_b.state);
			C: if test "$action" = "select"; then
			C:   playerpid=$(persist "playerpid=?" $HOME/littmidi_b.state);
			C:   persist "fcn=quit" $HOME/littmidi_b.state;
			C:   kill -s SIGTRAP $playerpid;
			C:   persist "fcn=set_playlist" $HOME/littmidi_b.state;
			C:   /d/at/bash/littmidi/superplay.sh;
			C: fi; 
			S: 1
	Persist display
		param
			C: cat $HOME/littmidi_b.state
			S: 1
	show pAth:
		param
			C: echo Path=$PATH
			S: 1
	pS ax
		param
			C: ps ax | less;
	Current song
		param
			C: songno=$(persist "songno=?" $HOME/littmidi_b.state);
			C: songname=$(songnum2name.sh $songno);
			C: echo "Now on song $songno: $songname";
			S: 1
	What time is it?
		param
			C: date +%Y/%m/%d__%H:%M:%S
			S: 1
	Edit Litt's Text Music Player menu
		param
			C: gvim /d/at/bash/littmidi/zzc.emdl
	Rebuild Litt's Text Music Player menu
		param
			C: /d/bats/rebuild_umenu_zzc.sh
	Timidity killall
		param
			C: killall -s SIGHUP timidity
	^Quit
plaYlist ::: Playlist menu
	Get Current Playlist
		param
			C: FN=$(persist playlist=? $HOME/littmidi_b.state);
			C: echo $FN
			S: 1
	Edit Playlist
		param
			C: gvim $(persist playlist=? $HOME/littmidi_b.state);
	^Quit
^eXit
Menu zzc, and main menu title
Run superplay.sh.

Get desired playlist from user.
If user selected instead of cancel
 kill any existing
 superplay.sh
 then run superplay.sh in a
 X terminal running in the 
 background








Get superplay.sh PID.
Write "pause" to fcn storage.
Send superplay.sh a SIGTRAP


Get superplay.sh PID.
Write "unpause" to fcn.
Send superplay.sh a SIGTRAP


Get superplay.sh PID.
Write "this" to fcn.
Send superplay.sh a SIGTRAP


Get superplay.sh PID.
Write "first_song" to fcn.
Send superplay.sh a SIGTRAP


Get superplay.sh PID.
Write "next" to fcn.
Send superplay.sh a SIGTRAP


Get superplay.sh PID.
Write "prev" to fcn.
Send superplay.sh a SIGTRAP


Get superplay.sh PID.
Write "last_song" to fcn.
Send superplay.sh a SIGTRAP


Get superplay.sh PID.
Ask user to pick a song.
 and store the picked song.
If user didn't cancel
 write "set_song" to fcn.
 send superplay.sh a SIGTRAP



Get superplay.sh PID.
Ask user to pick a playlist
 and store that playlist.
If user didn't cancel,
 write "set_playlist" to fcn
 Send superplay.sh a SIGTRAP




Get superplay.sh PID.
Write "quit" to fcn.
Send superplay.sh a SIGTRAP



ALL VOLUME FUNCTIONS ARE THE
SAME AS IN OTHER VERSIONS.
THEY INTERACT ONLY WITH aumix.


































































Nuke all must be changed to
nuke_all_central for this
to work.

nuke_play must be changed to
nuke_play for this to work.







Run superplay.sh in UMENU's
terminal instead of in
an X terminal. Use this in
CLI environments.









Diagnostic: show all
persistent storage.


Diagnostic: show $PATH.



Diagnostic: Show processes.



Show number and name of
current song.



Show the time.



Edit the EMDL file for the
menu of this application.


Recompile the EMDL for the
menu of this application.
Show errors, give user the
option of replacing current
menu.

So that's the centralized shellscript midi player. Do I like it better than the others? I don't know yet. I'll need to use it for a couple weeks before deciding which version to proceed with, and add my new evaluation feature.
Steve Litt is the author of the Troubleshooting: Just the Facts. Steve can be reached at his email address.

The Holy Grail

By Steve Litt
For as long as I've been coding, "code reusability" has been the holy grail. Sought after by IT management the way the fountain of youth was sought after by Juan Ponce de León, and it's been just as hard to find. And oh my, the sacrifices we've placed on its alter.

If you're a developer, doesn't it seem like so much effort is placed on reusability that it's more economical just to rewrite? And by the time you need to reuse, better technology has made the reuseable code obsolete.

Every year, when IT management designs their program of the year, reusability is always close to the top. Sometimes it's trumpeted as "software ICs". Sometimes as slick new technologies like EJB with its introspection and reflection. Sometimes by mandating that every possible hook be placed in each component. Mind twisting use of class inheritance, including the stomach queazer of all time, multiple inheritance. Interfaces. Code reviews. Remember Egoless-Programming?

What have we gotten for all these programs of the year? More of the same. Most projects are still either late, over budget, or both.

It's too bad, because Ken Thompson and a few of his friends addressed and solved reusability problem back when disco music ruled the airwaves. Most Unix shells, including bash, make no distinctions between builtin commands and programs you write, so shellscripting is a completely extensible language. Just to get you started they gave you the following commands:

Executables What it does Builtins What it does
cat filename
Writes all lines of filename to stdout.
&
Runs the command that precedes it, in the background, as a separate process.
echo string
Writes the string that follows it to stdout.
wait pid
Waits for the process whose PID is pid to complete. Can be interrupted by a trapped or untrapped signal.
head -n numlines
Writes the first numlines of the referenced file or stdin to stdout.
trap function signal
trap "" signal
Assigns a function to a signal, so when that specific signal is received, that specific function runs. Empty string completely ignores the signal.
tail -n numlines
Writes the last numlines of the referenced file or stdin to stdout. Can be combined with xhead to write the 8th line of songfile like this:
cat songfile | head -n 8 | tail -n 1
exec program
Replaces the current process with the program, so the program now has the PID of the (former) current process.
grep pattern filename
cat filename | grep pattern
Writes all lines of filename that match, via regular expressions, pattern.
source filename
Imports shellscript commands from the referenced file at the point of the source builtin command.
sed
Parses and transforms strings and files

      
cut
Removes parts of strings.

      
mktemp
Makes a temporary file with a guaranteed unique filename.

      
kill
Sends a signal to another process.

      

Every one of these commands is optimized, tested, and used in field conditions for years. Bash gives several options for communication between processes:
With all these resources plus any self-contained, thin interface executables you can create with bash, perl, python, ruby, C, C++, Java or whatever, it's easy to put  together an app with reusable parts.
Steve Litt is the author of the Manager's Guide to Technical Troubleshooting. Steve can be reached at his email address.

Licensing Advantages

By Steve Litt
If you're anything like me, there have been times you've wanted to license most of your app's code as GNU GPL, but some of it as proprietary. In most development environments, no can do. With AAuSE, no problem!

My UMENU, filepicker and recordpicker are all GPL. So are cat, echo, grep, head, tail, mktemp, sed and cut. If I want to use these free components to make a proprietary application, that's just fine (as per the license). I could use a C compiler with non-GPL libraries to build a proprietary program that takes commands and issues output, and control that proprietary program with UMENU, pickers, and the GNU executable utilities. That would be just fine, because UMENU, the pickers and the GNU utilities require nothing proprietary to do their job.

The GPL requires any program whose source code contains GPL code to be GPL. It requires any program whose executable is linked with compiled GPL code to be GPL. But it says nothing about the interaction of GPL executables with non-GPL executables.

Look at the beauty of this. You can write 90% of your app with nice, well tested GPL executables, or BSD licensed executables, or whatever. The 10% that makes your product unique and proprietary can go in a separate executable compiled binary. Likely that 10% would contain the business logic and data access. User interface would then be bolted onto it with the free executables.

If you're like me, there have been times you'd have loved to use a GPL development environment to make a proprietary product for a client. But you don't. GPL won't allow it, and LGPL can be a pain in the rear to comply with. LGPL requires the ability to recompile, which effectively means the client's customers must be given the source code to the proprietary part. AAuSE enables your client to give the client just the binary to the proprietary part.

Go Free Unless You Have a Good Reason

Nothing in this article should be construed to mean you shouldn't make Free Software. Absent a very strong reason to go proprietary, give back to the community and make your software free software. That's what I do. I'm just saying that if you have to make some software proprietary, you can use AAuSE to proprietarize just the part that needs to be proprietarized.
Steve Litt has been writing Troubleshooting Professional Magazine for ten years. Steve can be reached at his email address.

AAuSE and Performance

By Steve Litt
Shellscripting was not designed to be fast. As a matter of fact, looping inside a shellscript can be pig slow for the simplest possible reason -- the testcommand is really an executable that must be loaded on every iteration. Never loop in a shellscript unless one of the following is true:
  1. The bottleneck in each iteration is not the shellscript or test or utilities like sed, echo, cat, cut, grep and the like.
  2. You're doing a small number of iterations (less than 1000).
Generally speaking, if you have a loop expected to iterate more than 10,000 times (about a half second delay on my machine, for an empty), do not run that loop in a shellscript. Go Ruby, or Perl or C or whatever. If the loop has several commands, you might want to go to a different language as low as 1000 iterations or even less.

Even more generally speaking, in AAuSE, loops are for querying the user and then doing his bidding, not for batch processes. Batch processes with substantial iterations are best done with Perl, Python, Ruby, C, C++ etc as an executable. Call that executable from your AAuSE app.

Sometimes AAuSE can speed up a perl script that's hit hard. Here's a snippet from my web log evaluation program:
cat `./logfilelist.cgi` | grep -v "\.gif " | grep -v "\.ico " | grep -v "\.png
 " | grep -v "\.js H" | grep -v "\.css" | grep -v "index.cgi" | grep " 200 " | 
grep -v "\.jpg " | grep "\"GET " | grep -v "\.class " | grep -v "65\.94\.113\.
101" | ./logeval_worker.cgi

In the preceding, perlscript ./logeval_worker.cgi must parse hundreds of thousands of records. I'm not interested in reporting on .gif, .ico, .png, .js, jpg or .css files. I'm not interested in web hits sporting results of 200 or GET requests, nor from IP address 65.94.113.101 (apparently they were doing something funky to the site).

I could have had the perl script filter them out, but grep is sooo much faster. Plus the fact that because I put it in a pipeline, multiple processors will be used effectively. What I did is order the pipes in the grep so the first grep ruled out the most, and then the second grep ruled out the second most, etc. The idea is to have everything downstream do minimal work, especially the (slower) perlscript.

Sometimes you can use the xargs command to perform what otherwise would have been a loop. Here's a oneliner to delete all those pesky "Copy of" files that Windows likes to create:
find /d -type f | grep "Copy of" | xargs -P10 -n1 rm
Personally I get a little spooked by xargs -- do one wrong thing and you've trashed everything. Personally, I'd write a Ruby program to delete every file that comes in through stdin (call it rubydel.rb), and then do this:
find /d -type f | grep "Copy of" | ./rubydel.rb
Shellscript commands find and grep efficiently screen out most of the files, and then rubydel.rb does the heavy lifting on the remainder. Because rubydel.rb is called once, there's almost no program loading overhead.

Premature optimization is the root of all evil?

Donald Knuth said that "Premature optimization is the root of all evil." I'm a big believer in that. Starting with optimisation in mind slows the coding process, makes code less readable, and often provides a code environment where bugs can hide.

Nevertheless, when coding a shellscript, if you see a loop whose iterations do not include either a wait for user input or a wait for a process to finish, the hair on the back of your neck should stand on end! Shellscripts are really inefficient at loops, and your loop could be hundreds of times slower than equivalent Perl code.

When considering a shellscript loop, ask yourself these questions about the loops iterations:
  1. Do they each wait on user input or action?
  2. Do they each wait for a process to finish?
  3. Do they each run non-utility code (not grep, cat, etc) that takes more than a tenth of a second to run?
  4. Will there always be less than 1000 iterations during the course of the program?
If the answer to all four questions is "no", do yourself a favor, optimize prematurely, and don't put this loop in a shellscript. You'll be creating a nasty bottleneck. It will most likely come back to bite you.
So when you program AAuSE, be on the lookout for bad shellscript loops, and don't do them. Redesign, find a better way.
Steve Litt is the author of Samba Unleashed. Steve can be reached at his email address.

Lessons Learned

By Steve Litt
You don't learn AAuSE in a day, or even a week. Like any language, there are easy ways to do it and hard ways. Like any language, there are gotchas that can jump up and bite you if you're not looking for them.
Steve Litt has been writing Troubleshooting Professional Magazine for ten years. Steve can be reached at his email address.

Life After Windows: Falling in Love with Unix

Until now, my view of Linux was a stable, secure, inexpensive operating system packaged with compilers, interpreters, servers, databases, office products, and multimedia. It was a wishing well of data processing necessities for the price of a ten dollar CD.

Now it's obvious I was just scratching the surface.

I'm in awe of the guys who created Unix. To this day I believe their brilliance overshadows the OOP, Enterprise objects, reflection and introspection, and development environments of the twenty first century. Trouble is, history is written by the winners and those guys lost the war. If you don't count Linux as Unix, Unix is almost gone. And if you do count Linux, it's unfortunate that way too many Linux people are like me...

The RT-11 Guy

While still in Santa Monica Community College, I got a job programming Pascal on a PDP-11 running RT11. If that job had been C on Unix, my entire life would have been different. But it was only a couple years after the 1982 recession, and every C/Unix job had lines of experienced C/Unix programmers knocking on the door. I took what I could get, no matter how obscure.

There was a company called Quotron, with abundent C/Unix jobs. They paid a fortune. I quickly learned C on the PDP-11, and under the guidance of the head programmer, began to write many of our C routines. But I still couldn't get into Quotron. Getting Unix experience was almost impossible if you didn't get it at college. Santa Monica College used a Prime computer running Primos -- not bad, but useless in the marketplace.

The 1980's barrier to entry into the Unix world is hard to imagine today. It's not like you could have a Unix box in your living room -- not unless you had ten thousand bucks to spend. Mainframe programmers took all the mainframe jobs, Unix programmers took all the Unix jobs, Defense programmers took jobs requiring Jovial and ADA, and the rest of us circled like vultures picking up scraps.

Whatever else you might say about Bill Gates, he freed us from vulturehood.

The DOS Newbie

1986 still saw me working for the RT-11 company, but now they were transitioning to the PC with MS-DOS. While the head programmer maintained the PDP-11, and most of the other programmers kludged PC-Focus into recreating our medical management system, I found a niche using Turbo Pascal to do what the PC-Focus guys couldn't. I even used Turbo Pascal to create a client program to query a central VAX for eligibility and interface the data to PC-Focus. My income went up.

Litt Blows His Chance

In 1987 a problem with politics cost me my best customer, and the 1987 stock market crash made work hard to find. Nevertheless I interviewed for a job in a Unix shop, and probably could have gotten it. Trouble was, it was a job, not a contract, probably 60 hours a week, for about 2/3 the money I'd been making.

But the technology! These guys had some kind of load sharing Unix based OS that was the ultimate big-iron. When you submitted a job, the system found the least used computer in the network and ran your job there. Today this is no big deal, but in 1987 it was head and shoulders above what everyone else was doing.

After several days of consideration, I turned down the job. Yes, it would have been the most technologically exciting job I'd ever had, and I would have been working with a much more talented group of people, but the low salary would have made paying rent a little challenging, and the 60 hour weeks precluded moonlighting. I had money saved up and could have used it as a stepping stone until they gave me a raise or until I took my new skills elsewhere, but instead I just turned it down. A couple weeks later I had a contract, at a large law firm, making more than double the per-hour amount I would have made at the Unix job.

I made the decision not to take the Unix job, and never second guessed that job until yesterday.

The DOS Hired Gun

By the early 1990's I was happy and well off, and smugly pleased that the once snobby Unix guys were starting to have trouble in the job market, while we DOS guys could write our own ticket. With 20 to 30 hours per week at the law firm, and frequent other contracts, I was making good money and getting well known for excellent work.

I was a Turbo C guru. Given a DOS computer, Turbo C, and Microsoft assembler for things like TSRs, I could make DOS walk and talk.

I then branched out into Clarion -- a rapid application development so rapid I've never since seen anything that came close. Clarion, and the lack of anything that compared to it, is what led me to AAuSE -- but that's later in the story. Suffice it to say that by 1994 I was pleased with what DOS had done for my career.

I was even thankful for not having taken the career wrong turn of going into Unix...

There is None as Blind as He Who Will Not See

So 1994 rolls around, and we're at the end of another recession. Unless you were in California, you probably thought the recession was over by 1994. But throughout California, military bases had closed, military spending was off, and the Defense programmers were looking hard for work, as were the now obsolete mainframe programmers, and even many of the Unix programmers. They all came looking for DOS programming jobs.

So I began tech writing for a litigation support outfit, and as usual was soon using Turbo C to solve the problems their other programmers couldn't or didn't have time for. These guys had some amazing technology. Their courtroom case display program was DOS with video on specialized, $6000.00 Toshiba laptops. They had a video toaster made of an Amiga. But their technological pride and joy was a group of Unix machines that rendered video from individual frames. High power hardware and software to do their most challenging job.

An old-time Unix guy named Roger ran the bank of Unix boxes, as well as doing most of the other non-programming tech work and network management. He liked my work, took me under his wing, and tried to teach me Unix. At first I was enthusiastic, but the more he taught the less I liked it. Why should I need to memorize directories like /usr, /var, /etc and /home? And why, oh why, are users in /home and not in /usr.

Perhaps the thing that turned me off the most was the user interface he'd cobbled together. He used shellscripts full of arcane Unix commands like grep, head, tail, cat and the like, with some sort of home grown menuing and form completion. This guy was running a hundred thousand dollars worth of iron on batch files! The very thought offended my programmer sensibilities. I commented that the work would be better if done in C.

Roger and I drifted apart -- I continued working on the DOS product, which was where I wanted to be. When I began to use Linux in 1998 I briefly wondered if I should have listened to Roger, but heck, I could learn Linux myself and I had the local LUG to help me. And come on, shellscripts?

A couple days ago I remembered Roger, and for the first time I realized how stupid I'd been not to learn from him. Many or most Linux guys I work with are too young to have learned the Unix way, and although they don't program with a DOS accent, in a lot of repects they're still a lot like me.

Roger was different. I now know he thoroughly understood the point of Unix. For Roger, writing a monolithic C program was a last resort. Roger worked only part time on that network of Unix boxes, yet he'd programmed their entire operation himself. He was using AAuSE!

The Master Programmer

Even in California, the recession finally ended in 1995. From 1995 through most of 1998 I got all the work I wanted, at high rates, cranking out Turbo C and Clarion code. Toward the end I began using some rather disturbing technologies like Powerbuilder and Visual Basic, but I also used some great stuff like Perl.

The Linux Guy

In September 1998 I moved to Orlando Florida. Separated by 2500 miles from all my clients, I had to regroup. One way I regrouped was by ordering Red Hat 5.1 Linux, installing it 40 times to learn it, and writing about it on Troubleshooters.Com. I gained a heck of a lot of traffic. Better still, Sams Publishing came knocking -- first with offers to write chapters in other peoples' Linux books, then to write my own book, "Samba Unleashed".

Because of my Rapid Learning Process and my Universal Troubleshooting Process, I was able to write those books in a way that helped thousands of readers, even though I understood little of Linux' Unix underpinnings. Things like processes, PIDs, pipes, sockets and signals were distant magic to me, but my cookbook writing style guaranteed the reader would reproduce everything I did, which was considerable. 

From September 1998 through February 2001, Linux was a source of writing revenue, a source of web traffic, and a kind of cool hobby, while Windows was my daily driver operating system. Then things changed.

The 24/7/365 Linux Guy

For reasons beyond the scope of this article, by 2001 Windows had become inadquate for my daily work life. In March 2001 I transitioned my entire business to Linux. Linux was missing some things I used daily:
Within a few months I'd written an outline processor called VimOutliner, so that was no longer a problem. For many years I limped along making diagrams with the dia diagramming software and other graphics with Gimp. This was slower and less convenient than the Micrografx Windows Draw I'd used in Windows, but I kinda-sorta got the job done. By 2006 I'd discovered Inkscape, at which time I had almost everything Windows Draw had given me.

What I've missed for years was Clarion. Once every couple years I call Clarion Software or whatever they're called these days, begging them to port to Linux. The answer is always the same -- something like "as soon as the demand is there".

Back in Windows, I could use Clarion to create a 5 table app in a single day. Any time I needed to store or manipulate any kind of data, Clarion got it done fast. Clarion's the fastest development environment I've ever used. I've used Delphi, Powerbuilder, VB, Perl and Ruby. None can hold a candle to Clarion. Maybe Ruby on Rails can, but you know what -- Rails isn't satisfying the way Clarion was.

The lack of Clarion made me search for ever faster ways to write software, as a replacement for Clarion. I used all sorts of different paradigms -- All OOP All the Time, Program as Machine, Data Centered Programming -- none came close to Clarion.

Meanwhile, when I needed to automate something that once upon a time I would have automated with Clarion, I used Perl or Ruby to create a batch interface, and front ended it with UMENU, using various flat file or tab indented outline formats for the data. It worked, in a clumsy sort of way. It was enough to continue my business.

Some email discussions a couple years ago led me to NoSQL, which in turn led me to the idea of letting Unix do all the heavy lifting for you, via files, directories and pipes. I jumped head first into NoSQL only to discover it still had no front end useful for anyone but a programmer.

A month ago I needed a music player (.ogg files etc), and decided what I really wanted was a text mode music player. It was created with my home-grown modular filepicker and recordpicker executables combined with mplayer and UMENU, after which I coined the term Application Assembly using Small Executables (AAuSE). AAuSE had worked well!

A week ago I wanted to play my midi files the same way as my .oggs. If I'd read the fine print in the timidity man page, I'd have noticed its Gnome interface makes a pretty decent music player, complete with playlist handling. In other words, if I'd read the fine print in the timidity man page, you wouldn't be reading this magazine. Instead, I overlooked those capabilities and decided to write my own. It was much more challenging because unlike mplayer, the "dumb interface" for timidity has no easy to use API from which you can control it from another process. To make a long story short, I had to program all the interprocess communications.

I did it in Ruby and front-ended it with the filepicker, recordpicker and UMENU. The interprocess communication was kind of hairy, but Ruby supplied the needed tools. So I started writing this magazine to brag about the victory. Writing the magazine suggested more things, including the tantalizing idea of a generic spawn program that can reach back and communicate with the caller. The day after discussing the generic spawner, I created it, and then out of curiosity rewrote the midi player, using the generic spawner, as a spawn off of each menu item. Then, to provide another example, I turned around and rewrote it again, this time as a central shellscript doing all the spawns, with menu items simply sending text commands to the central shellscript. A sort of client-server model.

This work brought me up close and personal with forking, foreground and background processes, waiting and detaching, signals and signal handlers, methods using forever-sleeps to wait on signals without polling, bouncing interrupts between caller and subprocess, and much more.

The Enlightened Guy

It hit me yesterday -- this is why Unix old timers love Unix. It's why they live and breath Unix. It's why their first impulse is to do it the Unix way. Because the Unix way is so powerful. I wrote this email to Greater Orlando Linux User Group (GoLUG):

[GoLugTech] NOW I appreciate Linux
 Date: 02/01/2008 @ 12:13:53
 From: Steve Litt <slitt@troubleshooters.com>  (Troubleshooters.Com)
 To: tech@golug.org
 Reply to: tech@golug.org
 
Hi all,

Up until now I viewed Linux as a nice, free, stable and efficient operating system that came bundled with lots of apps.

In the three days fooling around with signal based interprocess communication, I understand why the old-time-Unix guys LOVE Linux (and all Unices and workalikes). I came from the DOS world, where assembling different executables was a kludge at best, and probably wouldn't work. So I rewrote everything in Turbo C and thought I was a stud. I thought the limit of using canned programs was the system() command.

With Linux I can pretty much assemble anything I want the way you could assemble circuitry with 74xx chips (or whatever they use now) and a protoboard.

Now I REALLY appreciate Linux.

SteveT

Unix guys have used AAuSE since Unix's inception. Roger at the litigation support outfit used it. But I came from the DOS world and thought shellscripting a kludge. I mean really, if you can't program it in C, you're just not very good!

My generation of programmers can't be blamed -- the OS we grew up with didn't have Unix's facilities. In DOS and Windows, Turbo C and Clarion were the best it got.

The Beginner

So here I stand, an AAuSE beginner. The Ruby-core midi player I programmed four days ago already seems like the work of an amateur. My AAuSE work is slow and unconfident, but that will change as ever more of my work gets done in AAuSE.

I need to automate my business so my wife and daughter can fill book orders when I'm away on business. Cutting and pasting from web pages to Gnumeric, and running the results through Vim macros to make mailing labels won't work for them. My plan is to use AAuSE.

Being new with AAuSE, it's daunting. My plan is to start by creating a Ruby program to parse a Paypal order web page, and present it to me as fields for final edit. Not yet having a form handler, that final edit will probably be in an editor -- Vim when I use the system, a more Notepad like editor for my wife and daughter.

At first all that will be done with the data is to put it in a comma delimited format suitable for easy pasting into my Gnumeric order history. But soon enough, it will also automatically go into Postgres. Next, I'll code up a small program to convert the Postgres data into mailing labels. Then I'll use AAuSE to incorporate reports. Then to edit records, mark orders as shipped. I'll begin an inventory system (until now I've just looked at the stacks to see how many books are left, and printed more when the stacks go below a certain point).

After a suitable time running the two systems parallel, at some point I'll shut down the Gnumeric system.

A Few Final Words

You reap what you sow. Once upon a time I turned my nose up at Roger, with his simplistic shellscripted front ends. I bragged about my colorful, windowed C programs and wondered why he didn't understand. Uncharitably, I even though maybe Roger made shellscript front ends because he wasn't "good enough" to do it in C.

Now it's my turn to have people look at me like I'm crazy. They comment the ugliness of my UMENU and shellscript based programs. My pickers have (how scandalous) screen flicker! Everything's black and white! And it's so retro, and no, that's not a complement!

I can't afford to listen to such comments. I've got bigger fish to fry. I'm just one guy whose responsibilities encompass much more than programming, so when I program it has to be fast. And robust. Like Roger before me,
Life After Windows is a regular Linux Productivity Magazine column, by Steve Litt, bringing you observations and tips subsequent to Troubleshooters.Com's Windows to Linux conversion.
Steve Litt is the founder and acting president of Greater Orlando Linux User Group (GoLUG).   Steve can be reached at his email address.

GNU/Linux, open source and free software

By Steve Litt
Linux is a kernel. The operating system often described as "Linux" is that kernel combined with software from many different sources. One of the most prominent, and oldest of those sources, is the GNU project.

"GNU/Linux" is probably the most accurate moniker one can give to this operating system. Please be aware that in all of Troubleshooters.Com, when I say "Linux" I really mean "GNU/Linux". I completely believe that without the GNU project, without the GNU Manifesto and the GNU/GPL license it spawned, the operating system the press calls "Linux" never would have happened.

I'm part of the press and there are times when it's easier to say "Linux" than explain to certain audiences that "GNU/Linux" is the same as what the press calls "Linux". So I abbreviate. Additionally, I abbreviate in the same way one might abbreviate the name of a multi-partner law firm. But make no mistake about it. In any article in Troubleshooting Professional Magazine, in the whole of Troubleshooters.Com, and even in the technical books I write, when I say "Linux", I mean "GNU/Linux".

There are those who think FSF is making too big a deal of this. Nothing could be farther from the truth. The GNU General Public License, combined with Richard Stallman's GNU Manifesto and the resulting GNU-GPL License, are the only reason we can enjoy this wonderful alternative to proprietary operating systems, and the only reason proprietary operating systems aren't even more flaky than they are now. 

For practical purposes, the license requirements of "free software" and "open source" are almost identical. Generally speaking, a license that complies with one complies with the other. The difference between these two is a difference in philosophy. The "free software" crowd believes the most important aspect is freedom. The "open source" crowd believes the most important aspect is the practical marketplace advantage that freedom produces.

I think they're both right. I wouldn't use the software without the freedom guaranteeing me the right to improve the software, and the guarantee that my improvements will not later be withheld from me. Freedom is essential. And so are the practical benefits. Because tens of thousands of programmers feel the way I do, huge amounts of free software/open source is available, and its quality exceeds that of most proprietary software.

In summary, I use the terms "Linux" and "GNU/Linux" interchangably, with the former being an abbreviation for the latter. I usually use the terms "free software" and "open source" interchangably, as from a licensing perspective they're very similar. Occasionally I'll prefer one or the other depending if I'm writing about freedom, or business advantage.
Steve Litt has used GNU/Linux since 1998, and written about it since 1999. Steve can be reached at his email address.

Letters to the Editor

All letters become the property of the publisher (Steve Litt), and may be edited for clarity or brevity. We especially welcome additions, clarifications, corrections or flames from vendors whose products have been reviewed in this magazine. We reserve the right to not publish letters we deem in bad taste (bad language, obscenity, hate, lewd, violence, etc.).
Submit letters to the editor to Steve Litt's email address, and be sure the subject reads "Letter to the Editor". We regret that we cannot return your letter, so please make a copy of it for future reference.

How to Submit an Article

We anticipate two to five articles per issue. We look for articles that pertain to the GNU/Linux or open source. This can be done as an essay, with humor, with a case study, or some other literary device. A Troubleshooting poem would be nice. Submissions may mention a specific product, but must be useful without the purchase of that product. Content must greatly overpower advertising. Submissions should be between 250 and 2000 words long.

Any article submitted to Linux Productivity Magazine must be licensed with the Open Publication License, which you can view at http://opencontent.org/openpub/. At your option you may elect the option to prohibit substantive modifications. However, in order to publish your article in Linux Productivity Magazine, you must decline the option to prohibit commercial use, because Linux Productivity Magazine is a commercial publication.

Obviously, you must be the copyright holder and must be legally able to so license the article. We do not currently pay for articles.

Troubleshooters.Com reserves the right to edit any submission for clarity or brevity, within the scope of the Open Publication License. If you elect to prohibit substantive modifications, we may elect to place editors notes outside of your material, or reject the submission, or send it back for modification. Any published article will include a two sentence description of the author, a hypertext link to his or her email, and a phone number if desired. Upon request, we will include a hypertext link, at the end of the magazine issue, to the author's website, providing that website meets the Troubleshooters.Com criteria for links and that the author's website first links to Troubleshooters.Com. Authors: please understand we can't place hyperlinks inside articles. If we did, only the first article would be read, and we can't place every article first.

Submissions should be emailed to Steve Litt's email address, with subject line Article Submission. The first paragraph of your message should read as follows (unless other arrangements are previously made in writing):

Copyright (c) 2003 by <your name>. This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, version  Draft v1.0, 8 June 1999 (Available at http://www.troubleshooters.com/openpub04.txt/ (wordwrapped for readability at http://www.troubleshooters.com/openpub04_wrapped.txt). The latest version is presently available at  http://www.opencontent.org/openpub/).

Open Publication License Option A [ is | is not] elected, so this document [may | may not] be modified. Option B is not elected, so this material may be published for commercial purposes.

After that paragraph, write the title, text of the article, and a two sentence description of the author.

Why not Draft v1.0, 8 June 1999 OR LATER

The Open Publication License recommends using the word "or later" to describe the version of the license. That is unacceptable for Troubleshooting Professional Magazine because we do not know the provisions of that newer version, so it makes no sense to commit to it. We all hope later versions will be better, but there's always a chance that leadership will change. We cannot take the chance that the disclaimer of warranty will be dropped in a later version. 

Trademarks

All trademarks are the property of their respective owners. Troubleshooters.Com(R) is a registered trademark of Steve Litt.

URLs Mentioned in this Issue


_