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 tha