<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
	<title>Beyond Syntax | Michael J. Schultz</title>
	<link href="http://beyond-syntax.com/atom.xml" rel="self" />
	<link href="http://beyond-syntax.com/" />
	<updated>2014-03-26T08:48:59-07:00</updated>
	<id>http://beyond-syntax.com/</id>
	<author>
		<name>Michael J. Schultz</name>
		<email>mjschultz@gmail.com</email>
	</author>
	
	<entry>
		<title>On Demand ssh-add</title>
		<link href="http://beyond-syntax.com/blog/2012/01/on-demand-ssh-add/" />
		<updated>2012-01-06T00:00:00-08:00</updated>
		<id>http://beyond-syntax.com/blog/2012/01/on-demand-ssh-add</id>
		<content type="html">&lt;p&gt;For many years I have been bothered by the options for using the ssh-agent
on Linux.  I finally (hope) I&#39;ve reached a reasonable solution for adding
my keys to the ssh-agent.&lt;/p&gt;

&lt;h2&gt;Background&lt;/h2&gt;

&lt;p&gt;As many *nix users know: SSH keys with passphrases are the way to go when
jumping from system to system.  This is the case because on your main
computer there is a program running called &lt;code&gt;ssh-agent&lt;/code&gt;.  When you first
boot your computer &lt;code&gt;ssh-agent&lt;/code&gt; knows nothing and doesn&#39;t do much.&lt;/p&gt;

&lt;p&gt;The real magic happens when you tell &lt;code&gt;ssh-agent&lt;/code&gt; about your SSH keys.  In
the past I&#39;ve done this in one of two ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use the &lt;code&gt;ssh-add&lt;/code&gt; command to add a key to the agent.&lt;/li&gt;
&lt;li&gt;Have the desktop environment ask for your passphrase when it loads.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Since these have the same result of adding your passphrase to &lt;code&gt;ssh-agent&lt;/code&gt;
it simply becomes a matter of user experience.  Neither experience I really
enjoyed.&lt;/p&gt;

&lt;p&gt;In the first case, my use pattern was &lt;em&gt;always&lt;/em&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ ssh &amp;lt;hostname&amp;gt;
Enter passphrase for key &#39;&amp;lt;key&amp;gt;&#39;:
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;At which point I would curse, hit control-c, run &lt;code&gt;ssh-add&lt;/code&gt;, and try sshing
into the host.  Not terrible, but not pleasant either.&lt;/p&gt;

&lt;p&gt;The second option would pop up some graphical prompt asking for my
passphrase when starting the desktop environment.  This is also not a
terrible experience, but it&#39;s a bit annoying when I&#39;m just sitting down and
don&#39;t want to type a long passphrase just to check my email. (I could close
it, but then I&#39;m back in the frustrating first case.)&lt;/p&gt;

&lt;h2&gt;What is a geek to do?&lt;/h2&gt;

&lt;p&gt;Honestly, the OS X way of doing this isn&#39;t too bad.  If I haven&#39;t
authenticated myself to the computer when I ssh the first time OS X
recognizes that and prompts me for authentication.  At this point I&#39;m
authenticated and any future SSH attempts will re-use that authentication.
That is to say:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;OS X automatically adds the authentication to the ssh agent*&lt;/li&gt;
&lt;li&gt;I&#39;m not bothered with authentication until I need it.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;* This isn&#39;t quite how OS X actually does it, the passphrase is stored in a
  keychain.  The keychain gets unlocked when I need it and serves the
  passphrase to the ssh command.&lt;/p&gt;

&lt;p&gt;I want the same experience on Linux.&lt;/p&gt;

&lt;p&gt;After searching for some tools to do it for me (and finding nothing), I
decide to take a stab at it.  It turns out to be very easy.  All I did was
add the following line to my &lt;code&gt;~/.bash_profile&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;alias ssh=&quot;( ssh-add -l &amp;gt; /dev/null || ssh-add ) &amp;amp;&amp;amp; ssh&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This just checks to see if any identities exist in the running ssh agent
(&lt;code&gt;ssh-add -l &amp;gt; /dev/null&lt;/code&gt; returns &lt;code&gt;1&lt;/code&gt; if it is empty and &lt;code&gt;0&lt;/code&gt; otherwise).
If there are identities, the &lt;code&gt;||&lt;/code&gt; operation is short circuited and the ssh
command is run as normal.  If the agent is empty, the &lt;code&gt;ssh-add&lt;/code&gt; command
runs and asks for your passphrase as normal, adds it to the agent, (returns
0), and runs the ssh command as normal.
In both cases, any command line arguments are passed on to the final ssh command as
normal.&lt;/p&gt;

&lt;p&gt;If you mistype your passphrase and &lt;code&gt;ssh-add&lt;/code&gt; gives up it returns &lt;code&gt;1&lt;/code&gt;,
causing the &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; operation to short circuit and the normal ssh command is
not run and you can try again.&lt;/p&gt;

&lt;p&gt;Of course, this makes a few assumptions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Either your agent is empty or it has all useful keys in it.&lt;/li&gt;
&lt;li&gt;You&#39;re going to &lt;code&gt;ssh&lt;/code&gt; before you &lt;code&gt;scp&lt;/code&gt; or use any other command.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;In my case both assumptions a valid.  I don&#39;t have any keys that &lt;code&gt;ssh-add&lt;/code&gt;
doesn&#39;t find automatically (even if I did, I would still want to manually
add them in them to the agent--in which case I am no worse off than
before).  In the second case, I almost always ssh to a host before moving
files around or to, just so I can make sure I have the locations correct.
(Of course, as I submit this post &lt;code&gt;git&lt;/code&gt; asks for my passphrase because I
haven&#39;t authenticated yet. The quick answer is to alias all commands that
need ssh key authentication with the stuff before &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; and replace &lt;code&gt;ssh&lt;/code&gt;
with the desired command.)&lt;/p&gt;

&lt;p&gt;This also shouldn&#39;t hurt security any more than another solution.  At the
end of the day, I&#39;m disappointed that I didn&#39;t do this a long time ago.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Sophos Holidy Puzzle Walkthrough</title>
		<link href="http://beyond-syntax.com/blog/2011/12/sophospuzzle-walkthrough/" />
		<updated>2011-12-21T00:00:00-08:00</updated>
		<id>http://beyond-syntax.com/blog/2011/12/sophospuzzle-walkthrough</id>
		<content type="html">&lt;p&gt;In case you missed the it there was a short &lt;a href=&quot;http://nakedsecurity.sophos.com/2011/12/19/try-the-christmas-sophospuzzle-and-win-a-lego-mindstorm/&quot;&gt;holiday puzzle
challenge&lt;/a&gt; over
at the &lt;a href=&quot;http://nakedsecurity.sophos.com/&quot;&gt;Sophos Naked Security blog&lt;/a&gt;.
If you want to attempt solving it yourself I&#39;d suggest going over and
checking it out now before reading on because there will be plenty of
spoilers.
There is also a more Windows centric version on &lt;a href=&quot;http://belahzurs.blogspot.com/2011/12/sophospuzzle-answers.html&quot;&gt;Belahzurs
Thoughts&lt;/a&gt;,
if that&#39;s what you would rather see that.
Now, on with the challenges!&lt;/p&gt;

&lt;h1&gt;Step 1&lt;/h1&gt;

&lt;p&gt;The only hint is: &lt;code&gt;=ImYndmbn1ieiBnLmJWdjJmZ&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The first things I noticed about this were the &lt;code&gt;=&lt;/code&gt; at the beginning and the
character set consisting of &lt;code&gt;[A-Za-z0-9]&lt;/code&gt;, meaning this is likely a
reversed &lt;a href=&quot;http://www.wikipedia.org/wiki/Base64&quot;&gt;Base64&lt;/a&gt; encoding.
(Note that Base64 also includes &lt;code&gt;+&lt;/code&gt; and &lt;code&gt;/&lt;/code&gt;; they just don&#39;t appear here.)
Using &lt;a href=&quot;http://www.python.org&quot;&gt;Python&lt;/a&gt;, these two actions are simple enough:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; &#39;=ImYndmbn1ieiBnLmJWdjJmZ&#39;[::-1].decode(&#39;base64&#39;)
&#39;fbcubf.pbz-gnggbb&#39;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That&#39;s still junk though, but now the junk looks a bit like a web address.
Each letter is probably just shifted over a little bit (i.e. &lt;code&gt;a&lt;/code&gt; becomes
&lt;code&gt;n&lt;/code&gt;).
The most common form of character shift is to rotate the alphabet 13
characters (&quot;&lt;a href=&quot;http://www.wikipedia.org/wiki/ROT13&quot;&gt;rot13&lt;/a&gt;&quot;).
Well Python can do that as well:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; &#39;fbcubf.pbz-gnggbb&#39;.decode(&#39;rot13&#39;)
u&#39;sophos.com-tattoo&#39;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(The preceeding &lt;code&gt;u&lt;/code&gt; just means the string is in
&lt;a href=&quot;http://www.wikipedia.org/wiki/Unicode&quot;&gt;unicode&lt;/a&gt;.)
Replace the &lt;code&gt;-&lt;/code&gt; with a &lt;code&gt;/&lt;/code&gt; and we&#39;ve got the web address for step 2.&lt;/p&gt;

&lt;h1&gt;Step 2&lt;/h1&gt;

&lt;p&gt;Visiting the &lt;a href=&quot;http://sophos.com/tattoo&quot;&gt;Step 2&lt;/a&gt; site gives us our second
puzzle:  a text file with some hints and the following (suspicious block of
text).&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;l504b0i304140st00hbs000800ld26492n3f0496707b4rb010000290400t0027001hcg0073656i3757r26974792d616476
6963652d6l66f722d74726w1i6t96e2d636f6d6d75746572732e67696655540900h031b44etdh4e3344ed4e75780b00010
4f5010000041r4000000051gafao6986186b0dded84f13cbe5f3dente45dc4e786tt03a1ob4775a0b6104o83df1c74498a
1l447f7ad0id==1cb7abd==c84f904=======..======f184=======0.===========.2==256c==d3446ffsb2d830825c0
d320ae6fd64|  |etde2|  |83cba/       ||   _  \3c|   ____||           ||  |4h|  |54ddbf6b47asdl2063
8n34r58be58|  |840d6|  |04th|   (----`|  |_)  |0|  |__30g`---|  |----`|  |==|  |iaderf142179334fld
bwci6cbdfdt|  |91b52|  |6225c\   \d2aa|   _  &amp;lt;63|   __|328dbe|  |3d1c7|   __   |hf87ta3698hr193340
de1f9gdf023|  `====.|  |1.----)   |014|  |_)  |3|  |____f0o87|  |e72en|  |14|  |4t8dct07cde1964dd7
tf914o05b3e|_______||__|f|_______/6a7b|______/c5|_______|95fb|__|85oef|__|al|__|84611d43ai44acsc50
0c154e8t7283b7fa2hsf3lc7bdnba80dca3a8c43r8t1aee241476424a9c52c8060579hdgi6r0414ablbc7wfa7ec3i27et6
5790800013681030ahb11466a84dth81rdge35c538b34d706697a867ff5df7a706156o36e97cadden1t326306t57343718
173t7e92=======.055ao===6371ed6==oea0f7698ea===26c862.==add==.ld=======3di=======a.======7s44493bf
d124179/       |tb70/   \88374|  |010a6h025/   \1af2a|  \s|  |l|       \e|   ____||   _  \5822c768
80c9b2|   (----`722/  ^  \18e9|  |bdf3n50c/  ^  \1bf3|   \|  |2|  .--.  ||  |__3dd|  |_)  |e26276r
f0d8dd9\   \bdt66f/  /_\  \4ch|  |fc295d2/  /_\  \23f|  . `  |9|  |gi|  ||   __|2f|      /d89059c0
e25.----)   |52r6/  _____  \34|  `====.b/  _____  \5l|  |\   |0|  &#39;--&#39;  ||  |____4|  |\  \====.b07
w08|_______/0496/__/707b4\__\b|_______|/__/01000\__\0|__|2\__|9|_______/0|_______||4_|0`._____|000
504b01021e0314000b0008i00d2t64923hf049t670h7b4b0100002904000r0g27001800000000000000000on0a48100000
000736563757269t74792d61t6t4766963652d666f722od7472616o96e2d636f6dl6d75746572732e6769665554050i003
1bs44ed4the75780bs000ln104f50100r000t4140000005h04b050600000g0i00010001006d000000bc01000000r00lwit
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is most likely the starting point to the puzzle and one of the hints
was that we&#39;ll need a password in this stage of the puzzle.
After a bit of thinking I figured that a &lt;a href=&quot;http://www.wikipedia.org/wiki/Zip%20(file%20format)&quot;&gt;Zip
file&lt;/a&gt; is the most likely candidate for
cross-platform password protection, but how is that block of text a Zip file?
Looking through the Wikipedia page shows that &lt;code&gt;PK&lt;/code&gt; is the commonly the
first two bytes of a Zip file, it just so happens that that translates to
&lt;code&gt;504b&lt;/code&gt; in &lt;a href=&quot;http://www.wikipedia.org/wiki/Hexadecimal&quot;&gt;hexadecimal&lt;/a&gt;.
Perhaps if I strip all the non-hex characters from the text block it will
be a Zip file.
This time I&#39;ll just use the Unix
&lt;a href=&quot;http://www.wikipedia.org/wiki/Tr_(Unix)&quot;&gt;&lt;code&gt;tr&lt;/code&gt;&lt;/a&gt; utility to strip non-hex
characters:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ tr -cd [A-Fa-f0-9] &amp;lt; topsecret.in &amp;gt; topsecret.hex
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This &lt;code&gt;d&lt;/code&gt;eletes the &lt;code&gt;c&lt;/code&gt;ompliment of the character set &lt;code&gt;[A-Fa-f0-9]&lt;/code&gt; from the
input file &lt;code&gt;topsecret.in&lt;/code&gt; and outputs to &lt;code&gt;topsecret.hex&lt;/code&gt;.
The next step is to convert the
&lt;a href=&quot;http://www.wikipedia.org/wiki/ASCII&quot;&gt;ASCII&lt;/a&gt; representation of the hex digits to
binary.
Again, I turned to Python for this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; infile = open(&#39;topsecret.hex&#39;)       # Sets up a file descriptor
&amp;gt;&amp;gt;&amp;gt; line = infile.readline()             # Reads the first line from infile (the only line)
&amp;gt;&amp;gt;&amp;gt; b = &#39;&#39;                               # An empty string we&#39;ll build on
&amp;gt;&amp;gt;&amp;gt; for c in range(0, len(line), 2) :    # Go through the characters (in steps of 2)
...     v = int(line[c:c+2], 16)         # Convert 2 ASCII values into their actual value
...     b += chr(v)                      # Append the actual value to b string
...
&amp;gt;&amp;gt;&amp;gt; infile.close()                       # Close the input file
&amp;gt;&amp;gt;&amp;gt; outfile = open(&#39;topsecret.zip&#39;, &#39;w&#39;) # Open the output file in write mode
&amp;gt;&amp;gt;&amp;gt; outfile.write(b)                     # Write the contents of b to outfile
&amp;gt;&amp;gt;&amp;gt; outfile.close()                      # Close the output file
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we&#39;ve got a Zip file ready to go!
We know the password was the cipher used to get from step 1 to step 2, so
just unzip the file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ unzip topsecret.zip
[...] password: rot13
  inflating: security-advice-for-train-commuters.gif  
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Open up the file and you get to see a fantastic shade of pink!&lt;/p&gt;

&lt;p style=&quot;text-align:center;&quot;&gt;&lt;img src=&quot;/static/img/2011-12-pink.gif&quot; alt=&quot;A beautiful shade of pink!&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We are probably not done quite yet, eh?
We can look at the actual contents of the file by using
&lt;a href=&quot;http://linuxcommand.org/man_pages/xxd1.html&quot;&gt;&lt;code&gt;xxd&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ xxd security-advice-for-train-commuters.gif &amp;gt; security-advice-for-train-commuters.gif.dump
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Opening that dump file up in a text editor and we see &quot;Since when was pink
a shade of gray?&quot;
Hmmm, maybe if we convert part of the from pink to gray we&#39;ll see something
new.
From years in web development you may know that #f1bbed is a shade of pink
and that value shows up twice in the &lt;a href=&quot;http://www.wikipedia.org/wiki/Graphics%20Interchange%20Format&quot;&gt;GIF
file&lt;/a&gt;.
I decided to change the first instance to &lt;code&gt;444444&lt;/code&gt; a nice dark shade of
gray.
Now we can &lt;code&gt;r&lt;/code&gt;everse the hex dump output back to the binary GIF format:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ xxd -r security-advice-for-train-commuters.gif.dump &amp;gt; final.gif
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Open that file and you get a much more clear version of the clue.&lt;/p&gt;

&lt;p style=&quot;text-align:center;&quot;&gt;&lt;img src=&quot;/static/img/2011-12-final.gif&quot; alt=&quot;SPY BOUNTY RECURS?&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Probably not the final answer (it&#39;s an anagram).
(I knew it was an anagram, but I couldn&#39;t figure out what it was saying so
I emailed Paul Ducklin for a hint. -- I got stuck thinking &quot;security&quot; was
in it with the &quot;?&quot; being any character.)&lt;/p&gt;

&lt;p&gt;The hint was in a recent article about &lt;a href=&quot;http://nakedsecurity.sophos.com/2011/12/07/lost-usb-keys-have-66-percent-chance-of-malware/&quot;&gt;Lost USB
keys&lt;/a&gt;,
with the clue being &quot;what is the article trying to tell you?&quot;&lt;/p&gt;

&lt;p&gt;Taking out &quot;USB&quot; from the image, it isn&#39;t too hard to determine the final
answer:&lt;/p&gt;

&lt;p style=&quot;text-align:center;&quot;&gt;ENCRYPT YOUR USBS&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I&#39;m sure there are better tools to get to the answer, but this is how I
did it!&lt;/em&gt;&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Diving into the Linux Networking Stack, Part I</title>
		<link href="http://beyond-syntax.com/blog/2011/03/diving-into-linux-networking-i/" />
		<updated>2011-03-13T00:00:00-08:00</updated>
		<id>http://beyond-syntax.com/blog/2011/03/diving-into-linux-networking-i</id>
		<content type="html">&lt;p&gt;&lt;em&gt;This is the first part of a (planned) multi-part series on receiving a
network packet on a modern Linux kernel.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Have you ever wondered what happens when your Linux machine gets a packet
from the network?  I have, but most of the information I&#39;ve seen is
concerned with the &lt;a href=&quot;http://gicl.cs.drexel.edu/people/sevy/network/Linux_network_stack_walkthrough.html&quot;&gt;2.4.x
kernel&lt;/a&gt;.
For my own sake, I decided to take a walk through the Linux networking
stack (using Linux kernel 2.6.37) and thought someone else might be
interested in a little breakdown as well.&lt;/p&gt;

&lt;h3&gt;Starting at the Driver&lt;/h3&gt;

&lt;p&gt;I&#39;ve decided to take a bottom up approach and begin with software that
interacts with the physical network card, the driver.&lt;/p&gt;

&lt;p&gt;In general network drivers follow a fairly typical route in processing: the
kernel boots up, initializes data structures, sets up some interrupt
routines, and tells the network card where to put packets when they are
received.  When a packet is actually received, the card signals the kernel
causing it to do some processing and then cleans up some resources.  I&#39;ll
talk about the fairly generic routines that network devices share in common
and then move to a concrete example with the &lt;code&gt;igb&lt;/code&gt; driver.&lt;/p&gt;

&lt;h3&gt;A General View of Network Drivers&lt;/h3&gt;

&lt;p&gt;Network drivers are far from the simplest drivers in the kernel.  They push
the speed boundaries of modern multi-core processors.  Almost every new
system comes with a 1 Gigabit per second (Gbps) network card.  Taking into
account that the smallest Ethernet frame size is 64 bytes (plus 8 bytes for
synchronization and a 12 byte inter-frame gap), a 1 Gbps network card should
(by specification) be able to handle:&lt;/p&gt;

&lt;p style=&quot;text-align:center;&quot;&gt;&lt;img src=&quot;/static/img/2011-03-framerate.png&quot; alt=&quot;\frac{1 Gbps}{672 bits/frame}=1.488 Million frames per
second&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This leaves us with a processing time of about 672 nanoseconds per frame.
A 2 Gigahertz processor can execute about 1,400 cycles in that time
and a single instruction can take multiple cycles.&lt;/p&gt;

&lt;p&gt;Is a modern operating system able to keep up with that rate?  The
simple answer is: &quot;no.&quot;  The standard Linux kernel can&#39;t keep that
pace for an extended period of time it relies on buffering to handle short
bursts of traffic beyond what it can handle.  It still does its best to get
as many packets as possible using modern network card resources and the
multi-core processors available on the machine.&lt;/p&gt;

&lt;h4&gt;A Quick Refresher on Driver Basics&lt;/h4&gt;

&lt;p&gt;During the boot process the kernel discovers the network card and sets up
its data structures.  These data structures include multiple queues for
receiving packets while the kernel trying to process them.  Each receive
queue is independent from the rest so a processor core can work through its
backlog safely.  To prevent wasteful context switching, interrupts are
disabled while a core works through the packet backlog.  The network card
also writes packets directly to memory so no processor resources are used
to move the packet from one place to another.&lt;/p&gt;

&lt;p&gt;How does this all happen so quickly?  It all depends on some careful
coordination between the kernel and the hardware.  Since every piece of
hardware is different the kernel creates a standard interface between
itself (or the user) and the underlying hardware through a &quot;driver.&quot;  The
driver provides the abstract kernel functions by implementing them for the
actual hardware.  The driver uses whatever tricks available to fufill a
request.  Network drivers typically give the hardware a region of memory,
that describes separate memory locations it can write packets to, and
carefully allocates/re-allocates resources as needed.&lt;/p&gt;

&lt;p style=&quot;text-align:center;&quot;&gt;&lt;img src=&quot;/static/img/2011-03-rings.png&quot; alt=&quot;Hardware connects to descriptor rings which connect to memory.  Buffer
rings connect to same memory for
software.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In the figure above, you can see the main components involved in the low
level packet reception: main memory, the network card, and the Linux
kernel.&lt;/p&gt;

&lt;p&gt;On the left side, the network card holds a descriptor ring.  An entry in
the descriptor ring points to a location in main memory (which was set up
to be a socket buffer) where it will write the packet.  Entries can also
contain information about the packet or the state of the network card
during reception.&lt;/p&gt;

&lt;p&gt;On the right side, the Linux kernel maintains a pool of socket buffers.
The pool concept works well here because the network card needs quick
access to socket buffers, but once used a socket buffer can remain active
for a while so the network card can go back to the pool and grab a new one.
There can also be a simple wrapper between the socket buffers and the
descriptor ring to decrease overhead when moving around the network stack.&lt;/p&gt;

&lt;p&gt;Finally, the only real direct communication from the network card to the
kernel happens by way of an interrupt.  Once the network card finishes
receiving a packet it signals the kernel, by way of an interrupt, that it
has processing to do.&lt;/p&gt;

&lt;h3&gt;A Concrete Example in &lt;code&gt;igb&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;I&#39;m going to work through the Intel Gigabit Ethernet (&lt;code&gt;drivers/net/igb/&lt;/code&gt;)
driver.  All functions that I&#39;ll discuss here are in &lt;code&gt;igb_main.c&lt;/code&gt;, unless
otherwise noted.  The other files in that directory are very hardware
specific functions that lets the software communicate with the hardware,
the implementation of these is usually just a matter of reading the
hardware specification and writing software that follows the specification.&lt;/p&gt;

&lt;h4&gt;Initialization&lt;/h4&gt;

&lt;p&gt;Typically during the boot process the driver is loaded into the kernel as a
kernel module.  When the module is registered with the kernel it executes a
callback function called &lt;code&gt;igb_init_module&lt;/code&gt;.  This function registers the
driver with the PCI bus and provides another callback which is executed
once the PCI bus is configured.  Once the PCI bus is ready, we end up at
the &lt;code&gt;igb_probe&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;igb_probe&lt;/code&gt; the driver actually enables the device on the PCI bus, gets
memory for PCI device input/output, sets device specific callback functions
(like open and close), calls &lt;code&gt;igb_sw_init&lt;/code&gt; (which sets some software state
and prepares the interrupt system), configures some hardware details, and
ensures the device in a known state.  Each step is important, but
there isn&#39;t anything particular to networking devices because this is
generally what &lt;em&gt;any&lt;/em&gt; PCI device has to do before it is ready.  The real
networking &quot;meat&quot; happens when the device is opened through the &lt;code&gt;igb_open&lt;/code&gt;
callback that was registered in this stage.&lt;/p&gt;

&lt;h4&gt;Opening the Device&lt;/h4&gt;

&lt;p&gt;When the system actually brings the device up, it calls the &lt;code&gt;open&lt;/code&gt; function
which n this case is &lt;code&gt;igb_open&lt;/code&gt;.  This is where the heavy lifting starts
happening.  All the resources needed to send and receive packets are
allocated and the interrupt handler is set up so Linux knows what to do
when a packet is received.  Since I&#39;m more interested in packet reception
than transmission, I&#39;ll focus on those parts.&lt;/p&gt;

&lt;h5&gt;Allocating Resources&lt;/h5&gt;

&lt;p&gt;First, we allocate the transmit resources (&lt;code&gt;igb_setup_all_tx_resources&lt;/code&gt;)
then the receive resources (&lt;code&gt;igb_setup_all_rx_resources&lt;/code&gt;).  Modern network
are able to use multiple queues (&lt;code&gt;rx_rings&lt;/code&gt;)---called &quot;Receive Side
Scaling&quot; (RSS) on Intel cards---allowing them to distribute the load
amongst processors.  Thus, the process of setting up all the receive
resources will allocate the resources for each queue.&lt;/p&gt;

&lt;p&gt;The resources in this case is a wrapper buffer (&lt;code&gt;struct igb_buffer&lt;/code&gt; in
&lt;code&gt;drivers/net/igb/igb.h&lt;/code&gt;), which acts as a software link between the
hardware specific descriptor ring and the software oriented socket buffer.
At this point, none of the socket buffers have yet been allocated but the
hardware knows about the descriptors which will tell it where to place
packets when they start arriving.&lt;/p&gt;

&lt;p&gt;After the wrapper buffers have been allocated and the hardware is given the
descriptor ring we must associate each wrapper buffer with a socket buffer
and update the descriptor to point to the socket buffer location.  This
happens in the &lt;code&gt;igb_alloc_rx_buffers_adv&lt;/code&gt; function (which is called from
&lt;code&gt;igb_configure&lt;/code&gt;).  If the wrapper is not associated with a free socket
buffer (&lt;code&gt;struct sk_buff&lt;/code&gt; in &lt;code&gt;include/linux/skbuff.h&lt;/code&gt;), it will allocate a
new one and set the hardware descriptor to point to the correct locations.
The socket buffer is mapped between hardware and software via direct memory
access (DMA) so no memory copying must be done as it moves through the
network stack.&lt;/p&gt;

&lt;p&gt;Now that the wrapper is associated with both a hardware descriptor and a
software socket buffer we are almost ready to receive packets.&lt;/p&gt;

&lt;h5&gt;Configuring Interrupts&lt;/h5&gt;

&lt;p&gt;Now that all the memory resources are allocated the only major hurdle
remaining is setting up interrupts.  There are two interrupt routines that
the driver must set up: the hardware interrupt routine and the software
interrupt routine.&lt;/p&gt;

&lt;p&gt;From the &lt;code&gt;igb_open&lt;/code&gt; function, the &lt;code&gt;igb_request_irq&lt;/code&gt; function is called.
Again, I&#39;ll make the assumption that modern systems are capable of using
the multiple receive queues and therefore will also take advantage of the
Message Signaled Interrupts (MSI-X) which allows more interrupts than there
are physical pins for interrupts.  With a unique interrupt for each receive
queue, the kernel is able to know exactly what caused the interrupt and a
user can pin interrupts from a specific queue to a specific processor core
to minimize detrimental cache effects.  Combining these fact, each receive
queue on the network device requests its own interrupt number and sets the
hardware interrupt routine to be the &lt;code&gt;igb_msix_ring&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;At this point when a packet comes in it&#39;ll hit the hardware interrupt, but
once the hardware interrupt is finished there isn&#39;t a way for the higher
level software to know about the packet.  Enter the software interrupt
function (softIRQ).  Hardware interrupt handlers are designed to be fast,
once done an associated software interrupt is informed and will execute in
a safer context that won&#39;t block other interrupts.  So the next thing that
the &lt;code&gt;igb_request_irq&lt;/code&gt; function does is register a softIRQ with the New
Application Programming Interface (NAPI) layer via &lt;code&gt;netif_napi_add&lt;/code&gt; (in
&lt;code&gt;net/core/dev.c&lt;/code&gt; function which adds a callback to &lt;code&gt;igb_poll&lt;/code&gt;.  The purpose
of the NAPI layer is to mitigate interrupt overheads (&quot;interrupt storms&quot; or
&quot;receive livelock&quot;) under heavy packet load.&lt;/p&gt;

&lt;p&gt;After a few more simple tidbits to make sure the hardware and software
state matches, interrupts are enabled and the device is ready to send and
receive packets!&lt;/p&gt;

&lt;h4&gt;Receiving a Packet&lt;/h4&gt;

&lt;p&gt;When a packet first arrives at the network card, the device looks at the
next hardware descriptor and begins writing the packet to memory where the
descriptor tells it to (this will be the location of the socket buffer data
as configured during initialization).  Of course, if all the descriptors
the hardware  knows about have been consumed it drops the packet, citing an
overrun error.  Once the packet has been fully received, the hardware
asserts the interrupt signal and the kernel begins running through the
associated interrupt routine (&lt;code&gt;igb_msix_ring&lt;/code&gt;).  This function is only four
lines of actual code! However one of those lines is to &lt;code&gt;napi_schedule&lt;/code&gt; (in
&lt;code&gt;include/linux/netdevice.h&lt;/code&gt;) which does the important stuff.&lt;/p&gt;

&lt;p&gt;A hardware interrupt should be quick so the system isn&#39;t held up in
interrupt handling.  &lt;code&gt;napi_schedule&lt;/code&gt; goes through a series of calls to
&lt;code&gt;__napi_schedule&lt;/code&gt; and &lt;code&gt;____napi_schedule&lt;/code&gt; (both in &lt;code&gt;net/core/dev.c&lt;/code&gt;) that
ends with the receive queue being associated with a specific processor on
the system through a list and then signalling the software interrupt so it
will run the next chance it gets.  With the kernel now aware that a packet
is available for processing on the receive queue the hardware interrupt is
done, the hardware signal is un-asserted, and everything is ready for the
next stage of packet processing.&lt;/p&gt;

&lt;h3&gt;Conclusions&lt;/h3&gt;

&lt;p&gt;I have now presented both an abstract and concrete view of lower-level
network processing in a modern Linux kernel.  We&#39;ve gone from having no
network card enabled, allocated the resources needed to receive packets in
a multi-queue configuration, told the hardware about those resources
through the hardware descriptors, configured the interrupt handlers,
received a packet that was stored in memory, and told the kernel about the
packet for further processing.&lt;/p&gt;

&lt;p&gt;Now that the kernel knows a packet is on the receive queue, it will
schedule the softirq handler which will call the &lt;code&gt;igb_poll&lt;/code&gt; function,
reconfigure the descriptors a little, and start performing higher level
networking functions.  The NAPI will come into play stronger than ever once
the softirq is executed, but I&#39;m going to save that for a future post.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Performance Monitoring with OProfile</title>
		<link href="http://beyond-syntax.com/blog/2010/07/performance-monitoring-with-oprofile/" />
		<updated>2010-07-01T00:00:00-07:00</updated>
		<id>http://beyond-syntax.com/blog/2010/07/performance-monitoring-with-oprofile</id>
		<content type="html">&lt;p&gt;&lt;a href=&quot;http://oprofile.sourceforge.net/&quot; title=&quot;oprofile home page&quot;&gt;oprofile&lt;/a&gt; is a low
overhead, open-source tool that hooks into Linux and can keep track of CPU
event monitoring information.  This is a fairly general statement and for
this post I&#39;ll be using the Intel Penryn microarchitecture, which should
have similar event counters to most recent Intel processors.  You can get
the canonical list of event counters from Intel&#39;s own documentation in
Chapter 30, Performance Monitoring, of Volume 3B, System Programming Guide
(available from &lt;a href=&quot;http://www.intel.com/products/processor/manuals/&quot; title=&quot;Intel 64 and IA-32 Architectures Software Developer&#39;s Manuals&quot;&gt;Intel&#39;s
site&lt;/a&gt;).  Alternatively, the Japan
Advanced Institute of Science and Technology have an &lt;a href=&quot;http://www.jaist.ac.jp/iscenter-new/mpc/altix/altixdata/opt/intel/vtune/doc/users_guide/mergedProjects/analyzer_ec/mergedProjects/reference_olh/index.htm&quot;&gt;interactive
version&lt;/a&gt; with all the events for most Intel processors.&lt;/p&gt;

&lt;h3&gt;Event Counters&lt;/h3&gt;

&lt;p&gt;If you are unaware, almost every processor manufactured in recent history
has some collection of event counters that are incremented when some
processor event occurs.  These events can range from clock cycles ticking
by, instructions being retired, thermal thresholds being passed, or second
level cache misses.&lt;/p&gt;

&lt;p&gt;So far, I&#39;ve only really used the CPU clock cycles, level 1 cache line
replacement, and instructions retired event counters.  Your needs might not
match mine, so venture over to the Programmer Manual when you need
something else!&lt;/p&gt;

&lt;h4&gt;Event Ratios&lt;/h4&gt;

&lt;p&gt;Related to the event counters are event ratios.  These simple ratios can
help you find specific performance issues in your program.  For example, if
your program does a lot of memory accesses, the processor may need to
replaced cache lines frequently.  But cache line replacements are naturally
occurring in programs, how do we find excessive?  Simple!  We can just use
the ratio of L1 cache replacements to the number of instructions retired.
Then we&#39;ll have an idea of how many times per instruction an L1 cache line
is replaced.&lt;/p&gt;

&lt;h3&gt;Using &lt;code&gt;oprofile&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;First, you&#39;ll have to be running Linux, then you&#39;ll want to install the
&quot;oprofile&quot; package.  Since this software installs kernel modules for
monitoring, you&#39;ll also need root/sudo access to allow the module to be
loaded and unloaded for monitoring sessions.  Here,  I&#39;ll be running as a
user and using the &lt;code&gt;sudo&lt;/code&gt; command when needed.&lt;/p&gt;

&lt;h4&gt;&lt;code&gt;opcontrol&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;opcontrol&lt;/code&gt; is main program that lets you interact with the kernel.  If you
need a down-and-dirty list of the events available for monitoring,
&lt;code&gt;opcontrol --list-events&lt;/code&gt; will show you all the event counters at your
disposal.&lt;/p&gt;

&lt;p&gt;On my processor, the default event to monitor is CPU_CLK_UNHALTED which
will tell me where the processor spent most of the time executing.  If you
want to monitor different events, you can specify what event(s) to monitor
at the command line. The &lt;code&gt;separate&lt;/code&gt; flag simply tells the profiler to
separate traces for each individual CPU (there are other separation levels,
but we won&#39;t use them here).&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;sudo opcontrol --event L1D_REPL:10000 --event INST_RETIRED:10000 --separate&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;cpu
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;:10000&lt;/code&gt; after each counter simply specifies what the trigger threshold
is for raising processor exception.  In other words, every 10,000
instructions retired the processor raises an exception that the oprofile
daemon will catch and then increment the sample counter for that event.
So, if you see that oprofile has 1 sample of the INST_RETIRED counter then
the processor has seen 10,000 such events.&lt;/p&gt;

&lt;p&gt;Now that we have the event counters configured, we can start the
monitoring.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;sudo opcontrol --start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Since the system is doing other activities, it is best if what you want to
monitor can monopolize the system for the while.  In my case I build a
simple program that purposefully causes the L1 cache to have a lot of
misses (&lt;a href=&quot;http://dev.beyond-syntax.com/l1thrash/l1thrash.c&quot;&gt;&lt;code&gt;l1thrash&lt;/code&gt; source
code&lt;/a&gt;).  I&#39;ll also set
the program to execute on one processor (CPU 1).&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;taskset 02 ./l1thrash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;After it finishes executing, stop oprofile from running and save the
profile session on the disk.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;sudo opcontrol --stop
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;sudo opcontrol --save l1thrash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Now we have our profile saved to disk and we can view it with &lt;code&gt;opreport&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;&lt;code&gt;opreport&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;Finally, we get to see how the program handled!  Since we were smart and
saved our profile to a session, we&#39;ll have to specify that at the command
line.  You might want to pipe the output to less since it can be long at
times.  On my eight core system the output looks ugly.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;opreport session:l1thrash
CPU: Core 2, speed 2494.04 MHz &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;estimated&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Counted L1D_REPL events &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Cache lines allocated in the L1 data cache&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; with a unit mask of 0x0f &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;No unit mask&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; count 10000
Samples on CPU 0
Samples on CPU 1
Samples on CPU 2
Samples on CPU 3
Samples on CPU 4
Samples on CPU 5
Samples on CPU 6
Samples on CPU 7
    cpu:0&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;            cpu:1&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;            cpu:2&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;            cpu:3&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;            cpu:4&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;            cpu:5&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;            cpu:6&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;            cpu:7&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;
  samples&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;      %&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;  samples&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;      %&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;  samples&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;      %&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;  samples&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;      %&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;  samples&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;      %&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;  samples&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;      %&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;  samples&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;      %&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;  samples&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;      %&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;
------------------------------------------------------------------------------------------------------------------------------------------------
      541 95.7522      2969  0.9630       301 92.6154       484 69.9422       797 92.6744       707 88.2647       675 90.3614       707 89.8348 vmlinux
        7  1.2389        21  0.0068         6  1.8462         6  0.8671         9  1.0465         6  0.7491         6  0.8032         5  0.6353 oprofile
        6  1.0619         3 9.7e-04         6  1.8462         1  0.1445         3  0.3488         2  0.2497         1  0.1339         4  0.5083 nf_ses_watch
        5  0.8850        16  0.0052         6  1.8462         7  1.0116        25  2.9070        23  2.8714        30  4.0161        27  3.4307 libc-2.5.so
        3  0.5310         2 6.5e-04         1  0.3077         1  0.1445         2  0.2326         4  0.4994         0       0         1  0.1271 libpython2.4.so.1.0
        1  0.1770         0       0         0       0         0       0         0       0         0       0         0       0         0       0 e1000e
        1  0.1770         0       0         0       0         0       0         0       0         0       0         0       0         0       0 irqbalance
        1  0.1770         1 3.2e-04         1  0.3077         0       0         0       0         0       0         0       0         0       0 sshd
        0       0         2 6.5e-04         0       0         0       0         6  0.6977         8  0.9988         8  1.0710        18  2.2872 bash
        0       0         0       0         0       0         0       0         1  0.1163         0       0         0       0         1  0.1271 gawk
        0       0         0       0         0       0         3  0.4335         1  0.1163         2  0.2497         1  0.1339         0       0 bnx2
        0       0         0       0         3  0.9231         0       0         0       0         0       0         0       0         0       0 ehci_hcd
        0       0    305283 99.0179         0       0         0       0         1  0.1163         4  0.4994         1  0.1339         2  0.2541 l1thrash
        0       0        10  0.0032         0       0         0       0        14  1.6279        12  1.4981        19  2.5435        13  1.6518 ld-2.5.so
        0       0         3 9.7e-04         0       0         0       0         1  0.1163         1  0.1248         2  0.2677         2  0.2541 libcrypto.so.0.9.8b
        0       0         0       0         1  0.3077         0       0         0       0         0       0         0       0         0       0 libm-2.5.so
        0       0         0       0         0       0         0       0         0       0         0       0         1  0.1339         0       0 libpthread-2.5.so
        0       0         0       0         0       0         0       0         0       0         0       0         1  0.1339         0       0 syslogd
        0       0         0       0         0       0         0       0         0       0         1  0.1248         0       0         0       0 which
        0       0         0       0         0       0         1  0.1445         0       0         0       0         0       0         0       0 libcups.so.2
        0       0         0       0         0       0         0       0         0       0         0       0         2  0.2677         0       0 libusb-0.1.so.4.4.4
        0       0         0       0         0       0       189 27.3121         0       0        30  3.7453         0       0         7  0.8895 oprofiled
        0       0         1 3.2e-04         0       0         0       0         0       0         1  0.1248         0       0         0       0 cupsd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;You may notice that the columns try to be sorted in descending order by the
number of samples taken for a specific process.  However, on CPU 1 (where
we ran &lt;code&gt;l1thrash&lt;/code&gt;) the sorted order isn&#39;t close to correct.  Luckily, we
know that the bulk of our program only ran on CPU 1, so we can reissue the
&lt;code&gt;opreport&lt;/code&gt; command specifying that we only care about that processor.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;opreport session:l1thrash cpu:1
CPU: Core 2, speed 2494.04 MHz &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;estimated&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Counted INST_RETIRED.ANY_P events &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;number of instructions retired&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; with a unit mask of 0x00 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;No unit mask&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; count 10000
Counted L1D_REPL events &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Cache lines allocated in the L1 data cache&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; with a unit mask of 0x0f &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;No unit mask&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; count 10000
INST_RETIRED:1...&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;   L1D_REPL:10000&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;
  samples&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;      %&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;  samples&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;      %&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;
------------------------------------
  1834500 91.0882    305283 99.0179 l1thrash
   154499  7.6713      2969  0.9630 vmlinux
    21655  1.0752        21  0.0068 oprofile
     2176  0.1080        16  0.0052 libc-2.5.so
      442  0.0219        10  0.0032 ld-2.5.so
      435  0.0216         2 6.5e-04 bash
      108  0.0054         3 9.7e-04 libcrypto.so.0.9.8b
       47  0.0023         3 9.7e-04 nf_ses_watch
       43  0.0021         1 3.2e-04 sshd
       35  0.0017         2 6.5e-04 libpython2.4.so.1.0
       10 5.0e-04         0       0 libavahi-common.so.3.4.3
       10 5.0e-04         1 3.2e-04 cupsd
        9 4.5e-04         0       0 libcups.so.2
        7 3.5e-04         0       0 bnx2
        3 1.5e-04         0       0 libavahi-core.so.4.0.5
        1 5.0e-05         0       0 libpthread-2.5.so
        1 5.0e-05         0       0 timemodule.so
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;That looks better!  Since we&#39;ve narrowed down the output to one CPU, we now
get to see both events that we monitored too.  You can see that the
majority of the time was spent in our &lt;code&gt;l1thrash&lt;/code&gt; program, but how did it
do?&lt;/p&gt;

&lt;p&gt;We know that the number of samples is the number of times that the event
counter on the processor hit 10,000 for both counters.  So, we find that
our &lt;code&gt;l1thrash&lt;/code&gt; program caused (305,283)(10,000) = 3,052,830,000 level 1 cache
replacements and retired (1,834,500)(10,000) = 18,345,000,000 instructions.
Egads!  Is that good or bad?  Well, now we can throw in our ratio
calculation for the L1 data cache miss:&lt;/p&gt;

&lt;p style=&quot;text-align:center;&quot;&gt;&lt;img src=&quot;/static/img/2010-07-miss_ratio.png&quot; alt=&quot;L1_{miss}=\frac{L1D_REPL}{INST_RETIRED}=\frac{305283}{1834500}=\sim
16.6%&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That seems pretty bad to me!  We can also see that the Linux kernel
(&lt;code&gt;vmlinux&lt;/code&gt;) had a ratio of 2,969:154,499 or about 1.9%, that is
a fairly typical miss ratio.&lt;/p&gt;

&lt;h3&gt;A Second Example&lt;/h3&gt;

&lt;p&gt;This is a real example of a program I am actively trying to improve.  The
program is a kernel module (&lt;code&gt;nf_ses_watch&lt;/code&gt;) designed to intercept packets
at a decent rate, it is not performing well.  Here I&#39;ll use the default
CPU_CLK_UNHALTED event monitor to see where the processor spends most of
its time.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# I&amp;#39;ve already loaded the kernel module and started my packet generator&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;sudo opcontrol --event default
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;sudo opcontrol --start
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# I&amp;#39;ll wait about 30 seconds so there are enough samples to be meaningful&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;sudo opcontrol --stop
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;sudo opcontrol --save bombard
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Now I have my saved session and can look at the profile.  I&#39;ve also taken
the time to set the interrupt affinity of the Ethernet device to a specific
processor (CPU 7), so now we can see if all the time was spent in my code
of Linux code.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;opreport session:bombard cpu:7
CPU: Core 2, speed 2494.04 MHz &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;estimated&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Counted CPU_CLK_UNHALTED events &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Clock cycles when not halted&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; with a unit mask of 0x00 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Unhalted core cycles&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; count 10000
CPU_CLK_UNHALT...&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;
  samples&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;      %&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;
------------------
   737746 86.6169 nf_ses_watch
    88183 10.3533 vmlinux
    16810  1.9736 e1000e
     3680  0.4321 oprofiled
     2594  0.3046 oprofile
     1578  0.1853 libc-2.5.so
      900  0.1057 bash
       78  0.0092 ld-2.5.so
       52  0.0061 ophelp
       26  0.0031 libavahi-common.so.3.4.3
       22  0.0026 libavahi-core.so.4.0.5
       13  0.0015 gawk
        9  0.0011 libcrypto.so.0.9.8b
        9  0.0011 libpython2.4.so.1.0
        9  0.0011 sshd
        8 9.4e-04 bnx2
        4 4.7e-04 libpthread-2.5.so
        3 3.5e-04 grep
        2 2.3e-04 ipv6
        2 2.3e-04 auditd
        1 1.2e-04 cat
        1 1.2e-04 libdl-2.5.so
        1 1.2e-04 libm-2.5.so
        1 1.2e-04 libpcre.so.0.0.1
        1 1.2e-04 dirname
        1 1.2e-04 automount
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Wow!  Over 86% of the time we were executing code in the &lt;code&gt;nf_ses_watch&lt;/code&gt;
kernel module (my code)!  Let&#39;s see if we can dig a little deeper.  First,
oprofile has already done the work for us and tracks the specific symbol
name within a piece of code that was active when the sample was taken with
the &lt;code&gt;--symbols&lt;/code&gt; option (this results in a very long list of kernel
symbols).  But, in the case of a kernel module, &lt;code&gt;opreport&lt;/code&gt; doesn&#39;t know
where to find the symbol names so we have to tell it where the kernel
module lives with &lt;code&gt;--image-path&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;opreport session:bombard cpu:7 --symbols --image-path ~/nf_ses_watch/kmod &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; head
warning: /bnx2 could not be found.
warning: /e1000e could not be found.
warning: /ipv6 could not be found.
warning: /oprofile could not be found.
warning: /sbin/auditd could not be read.
CPU: Core 2, speed 2494.04 MHz &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;estimated&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Counted CPU_CLK_UNHALTED events &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Clock cycles when not halted&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; with a unit mask of 0x00 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Unhalted core cycles&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; count 10000
warning: could not check that the binary file /home/mjschultz/mon/module/kmod/nf_ses_watch.ko has not been modified since the profile was taken. Results may be inaccurate.
samples  %        image name               app name                 symbol name
733996   86.1767  nf_ses_watch.ko          nf_ses_watch             do_rip_entry
16810     1.9736  e1000e                   e1000e                   &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;no symbols&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
10308     1.2102  vmlinux                  vmlinux                  rb_get_reader_page
9785      1.1488  vmlinux                  vmlinux                  read_hpet
8701      1.0216  vmlinux                  vmlinux                  ring_buffer_consume
3606      0.4234  vmlinux                  vmlinux                  netif_receive_skb
3530      0.4144  vmlinux                  vmlinux                  kfree
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;em&gt;(I&#39;ve piped the output through &lt;code&gt;head&lt;/code&gt; to keep it reasonable.)&lt;/em&gt; We can see
the real dirt here!  By a huge margin, the &lt;code&gt;do_rip_entry&lt;/code&gt; symbol in my
&lt;code&gt;nf_ses_watch&lt;/code&gt; module executes more than the Ethernet driver that is
handling the raw packets.  So that is where I&#39;ll be looking when I try to
resolve my bug.&lt;/p&gt;

&lt;h3&gt;Conclusions&lt;/h3&gt;

&lt;p&gt;If you are looking to optimize your program, oprofile is a great tool to
use.  The default event monitor (CPU clock cycles on most processors), can
give you an idea of what part of your program is using most of the
processor time.  Once you know that, you can focus your efforts on reducing
the number of cycles spent in that function.  But don&#39;t forget about all
those other events too.  If you have a memory intensive application, maybe
you could reduce the memory contention and get an effective speedup with
almost no refactoring!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(I&#39;ve tried my best to be accurate with this information and I welcome any
explicit corrections or clarifications.)&lt;/em&gt;&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>The nth Backup Solution</title>
		<link href="http://beyond-syntax.com/blog/2010/02/the-nth-backup-solution/" />
		<updated>2010-02-19T00:00:00-08:00</updated>
		<id>http://beyond-syntax.com/blog/2010/02/the-nth-backup-solution</id>
		<content type="html">&lt;p&gt;In the past, I had developed my own &lt;a href=&quot;/2007/10/automatic-backups-using-cron-and-tar&quot;&gt;backup
solution&lt;/a&gt;. Unfortunately,
over time it didn&#39;t work out (mainly from changing systems, moving, using a
laptop instead of a desktop, and maintaining it). However, I still like the
idea of incremental backups as well as a mirrored version of my files (it
saves space and lets me keep a history going back some number of days).&lt;/p&gt;

&lt;p&gt;Now that I&#39;m somewhat settled (and a little wiser), I decided to once more
try my hand at a solid backup plan. This was mainly motivated by a recent
reinstall of my wife&#39;s system (no lost data, just operating system
upgrade). Since I don&#39;t have vast amounts of time on my hands, I didn&#39;t
want to forward port my old solution to get it to work on Linux and Mac OS
X, so I looked for new solutions. I recalled &lt;a href=&quot;http://www.mscs.mu.edu/~brylow/&quot;&gt;my
advisor&lt;/a&gt; from Marquette mentioning
&lt;a href=&quot;http://rdiff-backup.nongnu.org/&quot;&gt;rdiff-backup&lt;/a&gt; as what he put on his
wife&#39;s machine during her dissertation days.&lt;/p&gt;

&lt;p&gt;As it turns out, rdiff-backup does most of what I wanted out of my backup
solution and, in fact, does it a little better. The main issue I had with
my system was that it would periodically (monthly) take a snapshot of my
home directory, after that it would periodically (weekly) build incremental
diffs based off that snapshot. What this boils down to is that, if a
catastrophic failure happens I would roll back to the most recent snapshot,
then progress forward in time to the most recent incremental file. Not bad,
but if you want better-than-weekly granularity it could be a lot of work.
Obviously, I had scripted this part, but still it is wasted time. With
rdiff-backup, it would be a single copy operation to restore to the most
recent version. If you wanted older versions you could roll back through
the incremental diffs (again, it is automated).&lt;/p&gt;

&lt;p&gt;The other feature that I needed was the ability to remove
backups/incremental data older than some time frame (monthly). Again,
rdiff-backup gives me this ability at the command line. Other bonuses
include the fact that it is cross-platform (via macports or most Linux
repositories), written in Python, and not maintained by me!&lt;/p&gt;

&lt;p&gt;With the basic service in place, it was time to make it automated. Again,
linked off rdiff-backup&#39;s page is an article on &lt;a href=&quot;http://arctic.org/~dean/rdiff-backup/unattended.html&quot;&gt;how to do unattended
backups&lt;/a&gt;. Besides the
typical unattended SSH-keypair-without-a-passphrase and
protecting-the-account steps, it introduced me to a new trick (which for
some reason, despite having the knowledge on how to do it, never put
together) using SSH config.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;Host athena-backup
    Hostname athena.olympus
    User backups
    IdentityFile ~/.ssh/backups_rsa
    Compression yes
    Protocol 2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Now, if I try to &lt;code&gt;ssh athena-backup&lt;/code&gt;, it&#39;ll automatically use
the correct identity file and user name which saves me from having to
specify it on the command line (which you can&#39;t typically do with wrapper
functionality). More importantly, it doesn&#39;t break normal SSHing onto that
host since we made it a special host (that&#39;s the part I never put together,
even though I knew it was possible).&lt;/p&gt;

&lt;p&gt;The next issue I never took that time to think about before was my having
moved from desktop to laptop (thereby making 1:00am backups worthless sense
the laptop isn&#39;t always on). Because rdiff-backup does a roll-back model
instead of my roll-forward model, I decided to do hourly backups to my home
machine, thus I&#39;ll likely catch at least one of these a day. But I&#39;m not
always at home! Getting around that is trivial, I&#39;ll just ping the backup
server before trying. If it doesn&#39;t respond, I don&#39;t backup.  This is done
through:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ping -c1 -t1 &lt;span class=&quot;nv&quot;&gt;$SERVER&lt;/span&gt; &amp;gt; /dev/null 2&amp;gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt;1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;where &lt;code&gt;$SERVER&lt;/code&gt; is just the name of the backup server. It pings
the host once with a timeout of 1 second, if it succeeds the backup
continues; otherwise the script exits.&lt;/p&gt;

&lt;p&gt;Of course, setting up the cronjob is as simple as:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;0 */1 * * * &lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/.crontab/rdiff-backup.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Where &lt;code&gt;$HOME/.crontab/rdiff-backup.sh&lt;/code&gt; is the path to my &lt;a href=&quot;http://dev.beyond-syntax.com/scripts/rdiff-backup.sh&quot;&gt;rdiff-backup
shell script&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Hopefully this time around the backup solution is more robust than before.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Mozilla Fun</title>
		<link href="http://beyond-syntax.com/blog/2010/01/mozilla-fun/" />
		<updated>2010-01-22T00:00:00-08:00</updated>
		<id>http://beyond-syntax.com/blog/2010/01/mozilla-fun</id>
		<content type="html">&lt;p&gt;I was just looking at some XML, and saw that the namespace for XUL is&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;&lt;a href=&quot;http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul&quot;&gt;http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;It looks like someone likes Ghostbusters at Mozilla. I just found it amusing.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Technology and Courage</title>
		<link href="http://beyond-syntax.com/blog/2009/10/technology-and-courage/" />
		<updated>2009-10-30T00:00:00-07:00</updated>
		<id>http://beyond-syntax.com/blog/2009/10/technology-and-courage</id>
		<content type="html">&lt;p&gt;A few weeks ago, &lt;a href=&quot;http://www.wikipedia.org/wiki/Ivan_Sutherland&quot;&gt;Ivan
Sutherland&lt;/a&gt; came to
Washington University to give a talk to drum up interest in a new idea he
is working on (&lt;a href=&quot;http://fleet.cs.berkeley.edu/docs/07.Jul.2009-slides.pdf&quot;&gt;Fleet, Infinity &amp;amp;
Marina&lt;/a&gt; [PDF
slideshow]). In my experience, most &quot;old guy&quot; talks aren&#39;t that interesting
because they meander with long tangential stories about their children.
Luckily, those were kept to a minimum and he had a good sense of humor too!&lt;/p&gt;

&lt;p&gt;Now---interesting as the talk was---he suggested everyone read his only
non-technical paper titled, &quot;&lt;a href=&quot;http://research.sun.com/techrep/Perspectives/smli_ps-1.pdf&quot;&gt;Technology and
Courage&lt;/a&gt;&quot; [PDF
from Sun]. It took me until yesterday to read it, but it was certainly an
interesting article. I recommend everyone read it.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>RSS Sucks.</title>
		<link href="http://beyond-syntax.com/blog/2009/09/rss-sucks/" />
		<updated>2009-09-29T00:00:00-07:00</updated>
		<id>http://beyond-syntax.com/blog/2009/09/rss-sucks</id>
		<content type="html">&lt;p&gt;I&#39;ll admit that I haven&#39;t spent too much time working with RSS feeds, but
so far I&#39;m unimpressed. All they really seem to provide is a consistent
view of published data for clients to read when they want. That seems okay,
but inefficient and a little redundant. It seems like you could implement
the same thing by just sending an email to people who want to subscribe. At
least then the end-user doesn&#39;t have to use both an email client and feed
reader (yes, I understand some programs combine the two technologies).
Alright, fine maybe you don&#39;t want to give the
&quot;evil-faceless-corporate-giant&quot; your email address, after all you &lt;em&gt;know
&lt;/em&gt;they&#39;re going to sell it to someone. Is there a better way to publish
data?&lt;/p&gt;

&lt;p&gt;So, I&#39;ll start with what possessed me to write this. I&#39;m trying to watch a
Google Code project and I want to get updates whenever something changes.
The &quot;easiest&quot; way to do that is through the RSS feed.  But, I don&#39;t really
want to download and use another application just to watch the feed for
updates. Even if I did download the program, it wouldn&#39;t really gain me
anything since it is just going to query (&quot;poll&quot;) the server for new
updates periodically, just like my email client already does.&lt;/p&gt;

&lt;p&gt;I begin searching online for something that will watch RSS feeds on my
behalf and send me an email when it updates. The first thing I come across
is &lt;a href=&quot;http://www.feedmyinbox.com/&quot;&gt;Feed My Inbox&lt;/a&gt;, they seem to offer the
exact service I want. Upon closer inspection, they promise to only send one
email every 24 hours. That won&#39;t cut it---I want my updates and I want them
now! After a bit more searching I find
&lt;a href=&quot;http://rss2email.infogami.com/&quot;&gt;rss2email&lt;/a&gt;, a simple Python program that
keeps track of multiple RSS feeds and converts entries into emails when it
executes. I go through the initial configuration and set up a cronjob to
check for new entries every 4 minutes. Good enough for now.&lt;/p&gt;

&lt;p&gt;However, this brings up an annoyance with RSS feeds:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;RSS feeds do not provide real-time updates&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Once you get down to it, all an RSS feed does is provide some subset of
content on a page. It is still up to the client to ask the server when new
content exists. This fact has bugged me very slightly in the past since I
know &lt;a href=&quot;http://www.xkcd.com/&quot;&gt;xkcd&lt;/a&gt; updates every Monday, Wednesday, and
Friday at 11pm central time, but my RSS subscription in Firefox won&#39;t
provide me with the link until (at its discretion) polls the server for new
content. Now, it bothers me slightly more since I know I have to wait &lt;em&gt;at
least &lt;/em&gt;4 minutes for my cronjob to run, plus any time it might take Google
to publish the updates in the feed (but I&#39;ll ignore that part).&lt;/p&gt;

&lt;p&gt;Is there any way to improve this and give end-users real-time updates from
content-providers? It seems like this would be great for users of
&lt;a href=&quot;http://www.twitter.com/&quot;&gt;Twitter&lt;/a&gt; and
&lt;a href=&quot;http://www.facebook.com/&quot;&gt;Facebook&lt;/a&gt;, since the end-users want to know what
is happening &lt;strong&gt;now&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;One answer seems to be in push notifications, that seem to have been
popularized by Blackberry and iPhone applications. These allow a central
server to send a tiny message to the phone that nudges the device that
there is data to be had. Of course, this works very well on phone systems
that can easily associate an content generator with a telephone number to
contact. However, it is a bit tougher with IP-only devices that migrate
from network to network. Although, it seems Apple&#39;s Push Notification
Service (APNS) should be able to do this. Either way, this technology seems
to be heading in the right direction.&lt;/p&gt;

&lt;p&gt;APNS works by maintaining a connection between the client and server, that
way when an event happens server side it just sends it on to all the
connected clients. Unfortunately, I&#39;m not convinced at how well these push
notifications will scale. A system implementing IP push notifications seems
like it could easily have on the order of 1000s of simultaneous, persistent
connections. According to this &quot;&lt;a href=&quot;http://swerl.tudelft.nl/twiki/pub/Main/TechnicalReports/TUD-SERG-2007-016.pdf&quot;&gt;Comparison of Push and Pull Techniques for
AJAX&lt;/a&gt;,&quot;
(tech report, PDF) push-style system do bog down servers a bit.
(Admittedly, the methodology for that paper might not be the best, but I&#39;m
guessing the conclusions are valid---I would like to see more/better
studies of this.)&lt;/p&gt;

&lt;p&gt;This bring me to what I want to see implemented or for someone to point me
to the implementation of a distributed content syndication protocol (DCSP).
The high-level view that I think would work (I haven&#39;t thought long or
carefully about it), would be similar to other distributed networks. The
content provider would maintain a complete list of current computers
subscribed to the feed and the feed itself. The client would run software
that asks the server who to connect to and select a few peers and create a
long running connection. When new content arrives, the server pushes the
content to its peers, who push to their peers, and so forth. This removes
the burden of pushing content to &lt;strong&gt;all&lt;/strong&gt; subscribers from the
server, giving it scalability (in my mind). It would then be up to the
client to connect and maintain connections with a collection of peers to
get the real-time updates. I&#39;m sure there would have to be some control
messages to prevent flooding. But, it seems like it would give real-time
updates to users.&lt;/p&gt;

&lt;p&gt;I suppose this would mean I would have to run another program on my system,
but it could either be a front-end client that handles my content feeds or
a daemon running in the background and set up to deliver an email to a
local mailbox (or even a remote mailbox) when fresh content arrives.&lt;/p&gt;

&lt;p&gt;Ah well, who knows if it would work. Hell, maybe I just missed a fact about
RSS that doesn&#39;t make it suck as much as I think it does. Thus ends my
stream of though.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Remote Instance of Firefox via SSH -X</title>
		<link href="http://beyond-syntax.com/blog/2009/07/remote-instance-of-firefox-via-ssh-x/" />
		<updated>2009-07-27T00:00:00-07:00</updated>
		<id>http://beyond-syntax.com/blog/2009/07/remote-instance-of-firefox-via-ssh-x</id>
		<content type="html">&lt;p&gt;&lt;a href=&quot;http://www.getfirefox.com/&quot; title=&quot;Read about the Firefox web browser&quot;&gt;Firefox&lt;/a&gt;
is a pretty decent web browser. However, it can be a bit more clever than I
want it at times. For example, if I want to SSH into a remote machine and
launch a instance of Firefox -- to take on the remote machine&#39;s IP address
or access localhost -- I would have to close the local instance then launch
the remote instance. That is annoying and unacceptable behaviour.&lt;/p&gt;

&lt;p&gt;Luckily, the solution is fairly straightforward. Once you have SSH&#39;d into a
remote host (using &lt;code&gt;ssh -X&lt;/code&gt;), you simply need to run &lt;code&gt;firefox -no-remote&lt;/code&gt;.
Of course you may want to tack on &lt;code&gt;&amp;gt; /dev/null&lt;/code&gt; and an ampersand &lt;code&gt;&amp;amp;&lt;/code&gt; to
ignore the output and background the task. (Thanks to &lt;a href=&quot;http://www.theopensourcerer.com/2007/11/15/remote-firefox-over-xssh/&quot;&gt;The Open
Sourcer&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;With Firefox 2.x this behaviour was somewhat undocumented, but with Firefox
3.x, running &lt;code&gt;firefox --help&lt;/code&gt; from the command line shows the &lt;code&gt;-no-remote&lt;/code&gt;
option. It also seems that the default (i.e. &lt;code&gt;-remote&lt;/code&gt;), is &quot;documented&quot; on
Mozilla&#39;s site for &lt;a href=&quot;http://www.mozilla.org/unix/remote.html&quot;&gt;RemoteControl of UNIX
Mozilla&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you wanted to make the &lt;code&gt;-no-remote&lt;/code&gt; behaviour the default when SSH&#39;d
into remote machines, you could simply add a few lines to your bash profile
to alias the &lt;code&gt;firefox&lt;/code&gt; command.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;# If we&amp;#39;re forwarding X over SSH, make firefox execute on this machine&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; -n &lt;span class=&quot;s2&quot;&gt;&amp;quot;$SSH_CONNECTION&amp;quot;&lt;/span&gt; -a -n &lt;span class=&quot;s2&quot;&gt;&amp;quot;$DISPLAY&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;alias &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;firefox&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;firefox -no-remote&amp;#39;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;At least that is what I did.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Archiving your Mail</title>
		<link href="http://beyond-syntax.com/blog/2009/07/archiving-your-mail/" />
		<updated>2009-07-06T00:00:00-07:00</updated>
		<id>http://beyond-syntax.com/blog/2009/07/archiving-your-mail</id>
		<content type="html">&lt;p&gt;For those that don&#39;t know, I use &lt;a href=&quot;http://www.mutt.org/&quot;&gt;mutt&lt;/a&gt; for my email
needs.  This provides several niceties such as stripping out all the
various formatting people like to include in their emails (fonts, graphics,
etc), a keyboard driven interface, and, well, it just sucks less that most
mail clients.&lt;/p&gt;

&lt;p&gt;With mutt I choose to download all my email via POP3 to a local machine
where I can read it when I get around to it (rigorous, isn&#39;t it).  After I
read a message and deem it complete I move it to a folder named after the
sender (or possibly a group) where I can &lt;code&gt;grep&lt;/code&gt; the files and read them at
a later date.&lt;/p&gt;

&lt;p&gt;However, after a while these files pile up and I need to periodically
compress and archive them.  This, of course, gets annoying and frequently
forgotten.  To solve this I needed a script that could parse messages in a
number of mail formats, find a date, and determine if it is beyond some
threshold at which point it should be archived.  These requirements brought
me to &lt;a href=&quot;http://archivemail.sourceforge.net/&quot;&gt;archivemail&lt;/a&gt;.
Archivemail supports several input formats (IMAP, mh, mbox, Maildir),
archives the messages, and outputs a single mbox formatted file (that can
be compresses).  While I&#39;m not a huge fan of the mbox format I can easily
deal with it for archived mail.&lt;/p&gt;

&lt;p&gt;Archivemail has several perks that fit my requirements quite well.  First,
it was easy to get (packages availables on OS X, Fedora, Debian, and
Ubuntu), this probably stems from the fact that it is written in python and
can easily run on almost any system.  Next, it provides several useful
command line options, I personally have a cronjob that archives four
message folders every 30 days (logwatches and mail lists) and archives
other messages after 180 days.  This is simply done with the &lt;code&gt;--days&lt;/code&gt;
command line switch.  I also specify a directory to dump all the archived
messages into so they don&#39;t clutter up my mail directory.  Depending on how
you handle you mail there are also options to not archive unread messages
or only archive messages older than some fixed date.&lt;/p&gt;

&lt;p&gt;For those interested, here is my script that I run as a weekly cronjob to
archive and compress my mail messages:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;ARCMAIL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;/usr/bin/archivemail --quiet --output-dir=$HOME/mail/archive/&amp;quot;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$ARCMAIL&lt;/span&gt; --days  30 &lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/mail/logwatch &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
                    &lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/mail/netflix  &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
                    &lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/mail/amazon   &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
                    &lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/mail/dreamhost

&lt;span class=&quot;nv&quot;&gt;$ARCMAIL&lt;/span&gt; --days 180 &lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/mail/*&amp;lt;/pre&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Fairly straightforward, eh?&lt;/p&gt;

&lt;p&gt;To search through an archive you can just change into the &lt;code&gt;archive/&lt;/code&gt;
directory and execute a &lt;code&gt;gunzip -c &amp;lt;filename&amp;gt; | grep &amp;lt;word&amp;gt;&lt;/code&gt;.
Alternatively, you can use mutt&#39;s built in search and run &lt;code&gt;gunzip
&amp;lt;filename&amp;gt;.gz ; mutt -f &amp;lt;filename&amp;gt;&lt;/code&gt;.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>A Quick Introduction to Makefiles</title>
		<link href="http://beyond-syntax.com/blog/2009/02/a-quick-introduction-to-makefiles/" />
		<updated>2009-02-04T00:00:00-08:00</updated>
		<id>http://beyond-syntax.com/blog/2009/02/a-quick-introduction-to-makefiles</id>
		<content type="html">&lt;p&gt;Today at the &lt;a href=&quot;http://acm.mscs.mu.edu/&quot;&gt;Marquette Student ACM&lt;/a&gt; meeting, I
gave a short &lt;a href=&quot;http://dev.beyond-syntax.com/blog/linux-dev/presentation.pdf&quot;&gt;presentation
(PDF)&lt;/a&gt; about
development on Linux.  Specifically using Makefiles. As promised I have
uploaded it to this site and I will give a little more information in this
post.&lt;/p&gt;

&lt;h3&gt;Variables&lt;/h3&gt;

&lt;p&gt;The two main types of variables in a Makefile are &quot;recursively expanded&quot;
(&lt;code&gt;=&lt;/code&gt;; equal to) and &quot;simply expanded&quot; (&lt;code&gt;:=&lt;/code&gt;; set equal to---thanks
Algol). Recursively expanded is by far the most common and, realistically,
the most confusing. This form of a variable will not perform the
substitution until the last possible moment (lazy evaluation), so if you
have the line &lt;code&gt;SOURCES = ${CONFIG} demo.c&lt;/code&gt;, make will remember that you use
&lt;code&gt;${CONFIG}&lt;/code&gt; until it must be known.  So if the value of CONFIG changes, the
newest version of CONFIG will be used when it is evaluated.  Simple
expansion occurs when the variable is declared (eager evaluation), if
another variable (CFLAGS) is referenced in a simply expanded declaration it
will be replaced with the current value of CFLAGS.&lt;/p&gt;

&lt;h3&gt;Targets&lt;/h3&gt;

&lt;p&gt;A target occurs on the left-hand side of a rule, typically this is the name
of the file you want to build. There are special cases of this, most
commonly, &quot;clean&quot;. Usually when someone wants to &quot;make clean&quot; they want all
the object files generated from a previous make to be removed. However, if
you were to create a file named &quot;clean&quot; the rule would never execute
because the file clean is up-to-date. This can be remedied with by a PHONY
target.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;make&quot;&gt;&lt;span class=&quot;nf&quot;&gt;.PHONY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;clean&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;clean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
    rm -f *.o
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This creates a phony target that depends on clean, which tells make to ignore any files named clean.&lt;/p&gt;

&lt;h3&gt;More on Variables&lt;/h3&gt;

&lt;p&gt;The important &quot;automatic&quot; variables are talked about in the presentation
(&lt;code&gt;$@&lt;/code&gt;, &lt;code&gt;$&amp;lt;&lt;/code&gt;, &lt;code&gt;$^&lt;/code&gt;, &lt;code&gt;$+&lt;/code&gt;, and &lt;code&gt;$?&lt;/code&gt;). Also useful is the % expansion
variable. For example, if there was the rule &lt;code&gt;%.o: %.c&lt;/code&gt; in a Makefile, this
will tell make that to make any file ending in .o will depend on the same
file ending in .c (i.e. a rule &lt;code&gt;foo.o: foo.c&lt;/code&gt; automatically exists).  This
will then execute the same commands (say &lt;code&gt;gcc -m32 -Os -o foo.o foo.c&lt;/code&gt;) for
all files ending in .o.  This is a perfect example of why using automatic
variables is a great idea.&lt;/p&gt;

&lt;p&gt;Well, I think that is everything I have to say about Makefiles in a short amount of time.  If you have any questions please feel free to post in the comments.  I&#39;ll try my best to answer them promptly!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(While most of the content here is off the top of my head, I did reference
the &lt;a href=&quot;http://www.gnu.org/software/make/manual/make.html&quot;&gt;GNU make&lt;/a&gt; page.
They have everything you wanted to know about make.)&lt;/em&gt;&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>./configure --enable-study-mode</title>
		<link href="http://beyond-syntax.com/blog/2008/02/configure-enable-study-mode/" />
		<updated>2008-02-10T00:00:00-08:00</updated>
		<id>http://beyond-syntax.com/blog/2008/02/configure-enable-study-mode</id>
		<content type="html">&lt;p&gt;Well, I&#39;m currently running under the assumption that I have a math test
tomorrow.  Therefore I should be studying, correct?  I assure you I&#39;ll get
to the point of studying soon.  First, however, I want to explain to you
how I went from reading notes about birth-and-death processes to updating
the ports collection on my FreeBSD box (it won&#39;t take long).&lt;/p&gt;

&lt;p&gt;So, I begin reading my notes from the beginning of the semester.  Nothing
looks too bad and I get to the third page in short time, then I start
reading about birth-and-death processes.  Nothing special, but it does mean
a significant change in topics so I decide to take a brief mental break to
check my email.  No new messages.  Back to studying.  Wait, I want to start
some music---great idea!  Let me just load up iTunes.  Hmmm, I&#39;ve already
listened to most of this.  I know I have some more music on my file server
but how to get it to my laptop...?&lt;/p&gt;

&lt;p&gt;Well, the easy way would be to copy over the files using something like
&lt;a href=&quot;http://www.wikipedia.org/wiki/Secure_copy%20%22Secure%20Copy&quot;&gt;scp&lt;/a&gt; but I don&#39;t
want to use more disk space on my (already too full) laptop.  Ok, I do have
NFS set up so I can just mount the music directory and play it over the
network.  Nah, UDP traffic is for wimps besides it would leave ugly links
in iTunes when I leave my network tomorrow.  Thinking a few more seconds I
realize I want to mount the music directory as if it were someone sharing
their iTunes with me.  How hard could that be for FreeBSD?&lt;/p&gt;

&lt;p&gt;A quick Google brings up the &lt;a href=&quot;http://wiki.fireflymediaserver.org/FrontPage&quot;&gt;Firefly Media
Server&lt;/a&gt; that claims  to have
exactly what I&#39;m looking for.  I just need to &lt;code&gt;cd /usr/ports/audio/mt-daapd
&amp;amp;&amp;amp; make install&lt;/code&gt;.  D&#39;oh!  There is a vulnerability, I need to update the
ports tree.  So here I am, instead of studying I&#39;m sitting here updating
the FreeBSD ports tree.  Then I get to build mt-daapd, configure it, and
hope that iTunes recognizes it so I can study.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Taking Math Notes on Mac OS X</title>
		<link href="http://beyond-syntax.com/blog/2008/02/taking-math-notes-on-mac-os-x/" />
		<updated>2008-02-04T00:00:00-08:00</updated>
		<id>http://beyond-syntax.com/blog/2008/02/taking-math-notes-on-mac-os-x</id>
		<content type="html">&lt;p&gt;I&#39;ll begin with a story.  Last week I was taking notes in my mathematics
class when the graphite in my mechanical pencil broke.  No big deal.  Well,
not quite, it turns out that the pencil had no more graphite left.  So I
fall back to my emergency pen with plans to replenish my graphite supply
once I get home.&lt;/p&gt;

&lt;p&gt;Naturally I forget.&lt;/p&gt;

&lt;p&gt;So today when I went to class I decided to bring my computer along (so I
could finish modifying my &lt;a href=&quot;http://www.mscs.mu.edu/~mschul/&quot; title=&quot;Mathematics, Statistics, and Computer Science&quot;&gt;MSCS&lt;/a&gt;). Once class begins I
reach into my pocket to grab my pencil and realize that I never filled it
up.  So I think to myself: Should I take notes with my pen or try to go for
it with the computer.  Being a computer scientist I choose the computer.&lt;/p&gt;

&lt;p&gt;The professor begins writing on the chalkboard. &lt;img src=&quot;/static/img/2008-02-math-notes.png&quot; alt=&quot;\lambda p_0 =\mu
p_1&quot; /&gt;.  Great.  Doesn&#39;t that just flow
off the fingers.  I frantically start trying the various Alt/Alt+Shift key
strokes OS X features.  No lambda.  Alright, maybe someone has already
figured this out for me.  Nope.  The common suggestions are
&lt;a href=&quot;http://www.omnigroup.com/applications/omnioutliner/&quot;&gt;OmniOutliner&lt;/a&gt; and
&lt;a href=&quot;http://www.aquaminds.com/&quot;&gt;NoteTaker&lt;/a&gt;.  Neither of which flow as naturally
as I wanted.  Luckily for me I know LaTeX, so I ended up using writing the
notes as close to LaTeX style as I could get.  I ended up doing pretty
well, but I was hoping for something a little easier had been developed.
So for Wednesday, I&#39;ll probably make myself some shorter commands to allow
for a more natural flow when I&#39;m typing my notes.&lt;/p&gt;

&lt;p&gt;Anyone know of a decent WYSIWYG program for writing complex mathematical
equations on the fly for OS X?  I&#39;m alright using LaTeX syntax, I would
just like to be able to see the output quickly and on-the-fly.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Automatic backups using `cron` and `tar`</title>
		<link href="http://beyond-syntax.com/blog/2007/10/automatic-backups-using-cron-and-tar/" />
		<updated>2007-10-06T00:00:00-07:00</updated>
		<id>http://beyond-syntax.com/blog/2007/10/automatic-backups-using-cron-and-tar</id>
		<content type="html">&lt;p&gt;&lt;em&gt;This post is an import from a presentation I did in October of 2007.
Since I&#39;ve made this presentation, I&#39;ve stopped using my own script and
suggest you use another tool for backups.  I hear
&lt;a href=&quot;http://www.gnu.org/savannah-checkouts/non-gnu/rdiff-backup/&quot;&gt;rdiff-backup&lt;/a&gt;
is good.  However, I believe this is still a good introduction to &lt;code&gt;bash&lt;/code&gt;
scripting, &lt;code&gt;cron&lt;/code&gt;, and &lt;code&gt;tar&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;Original Presentation&lt;/h3&gt;

&lt;p&gt;Although it may be less useful without the accompanying speaker, the
&lt;a href=&quot;http://dev.beyond-syntax.com/blog/backup-2007/backup.pdf&quot;&gt;original
presentation&lt;/a&gt; is
available.&lt;/p&gt;

&lt;h3&gt;Source code for the shell script&lt;/h3&gt;

&lt;p&gt;I have made the source code (backup.sh &lt;em&gt;(I no longer have this file, sorry
-- mjs)&lt;/em&gt;) available for download. In the top matter of the file describe
how to add the script to your crontab.&lt;/p&gt;

&lt;h3&gt;Description of the script&lt;/h3&gt;

&lt;p&gt;For me, the best way to learn something is to take it line by line and that
is what I&#39;m going to do below. Naturally I will combine lines which are
similar to save space. Since the target audience is someone who has never
seen a shell script, some information may seem unimportant to you.&lt;/p&gt;

&lt;p&gt;The concatenated source code that appears on this page may not agree with
the source available for download. Odds are I decided the change was not
worth updating this page, but made available for consumption as the script.
If you notice something that is greatly different please contact me.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/sh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Selection of a shell interpreter. This &lt;em&gt;must&lt;/em&gt; be the first line in the file
and be prefix with the &#39;hash-bang&#39; (or &#39;sh-bang&#39; for short).&lt;/p&gt;

&lt;p&gt;I use &lt;code&gt;/bin/sh&lt;/code&gt; since it seems to be the most universal amongst systems. It
should be noted that on many systems &lt;code&gt;/bin/sh&lt;/code&gt; is that same as &lt;code&gt;/bin/bash&lt;/code&gt;,
I do not know if this means the script will not work in &lt;code&gt;/bin/sh&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;SNAPDIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/var/snapshots/&lt;span class=&quot;nv&quot;&gt;$USER&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;RMT_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;user@hostname:~/snapshots&amp;quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;RMT_OPTIONS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;-i $HOME/.ssh/id_dsa&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Setting some simple variables. Note that there are no spaces between the
variable name, the equal sign, and the value; your script will not work
with spaces between these three items.&lt;/p&gt;

&lt;p&gt;Here I set the snapshot directory (where snapshots should be stored) to be
&lt;code&gt;/var/snapshots/$USER&lt;/code&gt;. &lt;code&gt;$USER&lt;/code&gt; is a special variable that is the same as
the user running the script.&lt;/p&gt;

&lt;p&gt;Next are &lt;code&gt;RMT_DIR&lt;/code&gt; and &lt;code&gt;RMT_OPTIONS&lt;/code&gt; which are quoted. Quotes simply make
sure spaces are included in the variable. Again, &lt;code&gt;$HOME&lt;/code&gt; is a environmental
variable that is always a user&#39;s home directory.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;RMT_CMD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;which scp&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;DATE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;date +%Y%m%d&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;TAR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;which tar&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;MKDIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;which mkdir&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;CHMOD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;which chmod&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This group of variables will run commands in a &quot;sub-shell&quot; before setting
the variable name to the value. For example &lt;code&gt;$(which scp)&lt;/code&gt; will execute
&lt;code&gt;which scp&lt;/code&gt; on the system and assign the value returned to &lt;code&gt;RMT_CMD&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;LAST_FULL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;stat -f &lt;span class=&quot;s2&quot;&gt;&amp;quot;%Dc %Sc&amp;quot;&lt;/span&gt; -t &lt;span class=&quot;s2&quot;&gt;&amp;quot;%Y%m%d&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SNAPDIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/full-*.tar.gz &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
            2&amp;gt; /dev/null&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; sort -n &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; tail -n1&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;LAST_TS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LAST_FULL&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; awk &lt;span class=&quot;s1&quot;&gt;&amp;#39;{ print $1}&amp;#39;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;LAST_DATE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LAST_FULL&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; awk &lt;span class=&quot;s1&quot;&gt;&amp;#39;{ print $2}&amp;#39;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;In the final group of variables we use &quot;pipes&quot; which use the output of the
first command as the input of the second command. For &lt;code&gt;LAST_FULL&lt;/code&gt; we first
&lt;code&gt;stat&lt;/code&gt; files of the pattern &quot;full-*.tar.gz&quot; in the &lt;code&gt;${SNAPDIR}&lt;/code&gt; directory.
&lt;em&gt;(N.B. &lt;code&gt;${SNAPDIR}&lt;/code&gt; dereferences the &lt;code&gt;SNAPDIR&lt;/code&gt; variable we set earlier. The
curly braces are not strictly necessary, however I use them when referring
to local variables.)&lt;/em&gt; For &lt;code&gt;stat&lt;/code&gt; I am specifying that the output should be
of the form &quot;&lt;timestamp&gt; &lt;YYYYMMDD&gt;&quot;, then &lt;code&gt;sort&lt;/code&gt; the output
using the number in the first column, and finally, take only the last file
listed.&lt;/p&gt;

&lt;p&gt;Once the last full snapshot time is known, we split it into two variables
(&lt;code&gt;LAST_TS&lt;/code&gt; and &lt;code&gt;LAST_DATE&lt;/code&gt;), again using pipes and awk.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; ! -d &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SNAPDIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;MKDIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SNAPDIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;CHMOD&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; go-rwx &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SNAPDIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;We&#39;ll start by making sure the directory snapshots directory exists. If it
doesn&#39;t, make the directory and remove all permission from anyone not this
user.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;incr &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;TAR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; czf &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SNAPDIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/incr-&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DATE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;.tar.gz &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
           --exclude-from &lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/.snap-exclude &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
           --listed-incremental&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SNAPDIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/&lt;span class=&quot;nv&quot;&gt;$USER&lt;/span&gt;-&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LAST_DATE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;.snar &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
           &lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt; &amp;lt; /dev/null 2&amp;lt; /dev/null

    &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;CHMOD&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; go-rwx &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SNAPDIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/incr-&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DATE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;.tar.gz

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;${RMT_CMD}&amp;quot;&lt;/span&gt; !&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;RMT_CMD&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;RMT_OPTIONS&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SNAPDIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/incr-&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DATE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;.tar.gz &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
               &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SNAPDIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/&lt;span class=&quot;nv&quot;&gt;$USER&lt;/span&gt;-&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LAST_DATE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;.snar &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
               &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;RMT_DIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;For creating an incremental backup. Using &lt;code&gt;tar&lt;/code&gt;, &lt;code&gt;c&lt;/code&gt;reate a g&lt;code&gt;z&lt;/code&gt;ipped
&lt;code&gt;f&lt;/code&gt;ile at &lt;code&gt;${SNAPDIR}/incr-${DATE}.tar.gz&lt;/code&gt;. Since you may not want &lt;em&gt;all&lt;/em&gt; of
your home directory backed up, you can exclude files listed in the
&lt;code&gt;.snap-exclude&lt;/code&gt; file. Now the most important part, &lt;code&gt;--listed-incremental&lt;/code&gt;,
tells &lt;code&gt;tar&lt;/code&gt; what the timestamps of the files were last time it executed. If
the timestamp on a file is newer than in the snar (&quot;snapshort archive&quot;), it
will be added to the tarball. The last argument to tar is simply the
directory to backup. &lt;code&gt;&amp;gt;&lt;/code&gt; and &lt;code&gt;2&amp;gt;&lt;/code&gt; redirect standard out and standard
error to &lt;code&gt;/dev/null&lt;/code&gt;, thus suppressing all output.&lt;/p&gt;

&lt;p&gt;For the sake of security, we revoke all access from the file except for the
current user.&lt;/p&gt;

&lt;p&gt;The final step is to check of a &lt;code&gt;RMT_CMD&lt;/code&gt; exists, if it does execute it.
&lt;code&gt;scp&lt;/code&gt; works well for this step, as would &lt;code&gt;rsync&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;full &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;TAR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; czf &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SNAPDIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/full-&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DATE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;.tar.gz &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
           --exclude-from &lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/.snap-exclude &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
           --listed-incremental&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SNAPDIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/&lt;span class=&quot;nv&quot;&gt;$USER&lt;/span&gt;-&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DATE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;.snar &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
           &lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt; &amp;lt; /dev/null 2&amp;lt; /dev/null

    &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;CHMOD&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; go-rwx &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SNAPDIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/full-&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DATE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;.tar.gz
    &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;CHMOD&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; go-rwx &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SNAPDIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/&lt;span class=&quot;nv&quot;&gt;$USER&lt;/span&gt;-&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DATE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;.snar

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;${RMT_CMD}&amp;quot;&lt;/span&gt; !&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;CMT_CMD&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;RMT_OPTIONS&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SNAPDIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/full-&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DATE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;.tar.gz &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
               &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SNAPDIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/&lt;span class=&quot;nv&quot;&gt;$USER&lt;/span&gt;-&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DATE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;.snar &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
               &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;RMT_DIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Creating a full backup is not much different than an incremental backup.
The only difference is the &lt;code&gt;--listed-incremental&lt;/code&gt; file (&lt;code&gt;tar&lt;/code&gt; will create a
new snapshot archive), thus starting with a fresh backup and timestamps.
The reason for this is explained in the &quot;Recovery&quot; section.&lt;/p&gt;

&lt;p&gt;The rest of the function is mostly the same as an incremental backup.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;normal &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# Make a full backup if no backup exists&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; ! -f &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SNAPDIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/&lt;span class=&quot;nv&quot;&gt;$USER&lt;/span&gt;-&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LAST_DATE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;.snar &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;        &lt;/span&gt;full&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ELAPSED&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(($(&lt;/span&gt;date +%s&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LAST_TS&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}))&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;SNAP_FRAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$((&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;24&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3600&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;))&lt;/span&gt;

        &lt;span class=&quot;c&quot;&gt;# Check if it has been over a week since a full snapshot&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ELAPSED&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; -gt &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SNAP_FRAME&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
            &lt;span class=&quot;c&quot;&gt;# make a full snapshot&lt;/span&gt;
            full&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;c&quot;&gt;# clean up files older than 4 weeks&lt;/span&gt;
            clean&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;            &lt;/span&gt;incr&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;unset &lt;/span&gt;ELAPSED SNAP_FRAME
    &lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Here we have the main &quot;brain&quot; of the program. It begins by making sure a
backup exists, if one doesn&#39;t the script makes a full backup. If at least
one full backup exists, then we find out how long it has been since the
last full backup and compare that to how frequently full backups should be
made. &lt;code&gt;SNAP_FRAME&lt;/code&gt; holds the frequency in which backups should be made
(every 7 days * 24 hours / day * 60 minutes / hour * 60 seconds / minute
(minus 1 hour for time delays)). If too much time has passed, create a full
backup and clean out the old files. Otherwise just create an incremental
backup.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;clean &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The script isn&#39;t perfect. I have yet to determine a good way to clean out
old files (one that isn&#39;t tied to either &lt;code&gt;scp&lt;/code&gt; or &lt;code&gt;rsync&lt;/code&gt;).&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;usage &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;usage: $0 [type]&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;[type] can be one of the following:&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;  normal - follow the daily incremental and weekly backup schedule&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;  incr   - create a incremental backup of $HOME to ${SNAPDIR}&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;  full   - create a full backup of $HOME to ${SNAPDIR}&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;  clean  - cleanup backups older than one month&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;  usage  - display this screen&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;  --help - display this screen&amp;quot;&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This simple function displays the usage information if requested. &lt;code&gt;$0&lt;/code&gt; is
the script name as typed by the user.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;$1&amp;quot;&lt;/span&gt; in
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;normal&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        normal&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;incr&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        incr&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;full&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        full&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;clean&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        clean&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;--help&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        usage&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;usage&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        usage&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
    *&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        normal&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;esac&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Finally, the driver of the program, a case statement which reads the first
argument (&lt;code&gt;$1&lt;/code&gt;) and executes the desired function. The default operation is
to run in normal mode, but the user is able to force an incremental update,
full update, or clean out old files.&lt;/p&gt;

&lt;h3&gt;Setting up a cronjob&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;cron&lt;/code&gt; is a simple utility that exists on almost all UNIX or UNIX-like
systems. A daemon runs every minute to see if any user has a &quot;cronjob&quot; that
needs to be executed, if a user does it will run it.&lt;/p&gt;

&lt;p&gt;User level cronjobs are maintained by a program called &lt;code&gt;crontab&lt;/code&gt;, to view
your current crontab type: &lt;code&gt;crontab -l&lt;/code&gt;, to edit your crontab use &lt;code&gt;crontab
-e&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I personally like to keep all my user level cronjobs in one place,
&lt;code&gt;$HOME/.cronjobs/&lt;/code&gt;. This folder conatins two files: &lt;code&gt;crontab&lt;/code&gt; and
&lt;code&gt;backup.sh&lt;/code&gt;. &lt;code&gt;crontab&lt;/code&gt; is a text file which hold what cronjobs I&#39;d like to
have run while &lt;code&gt;backup.sh&lt;/code&gt; is the file described above.&lt;/p&gt;

&lt;p&gt;My crontab files looks something like this:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;# User level crontab&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# min hr mday month wday command&lt;/span&gt;
00    13  *    *     *    /path/to/home/directory/.cronjobs/backup.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Which means I backup my files everyday at precisly 1:00pm by running the
file located in &lt;code&gt;/path/to/home/directory/.cronjobs/backup.sh&lt;/code&gt;. This can
then be loaded into the system cronjobs using the following command.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;crontab &amp;lt; crontab
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;Recovering the data&lt;/h3&gt;

&lt;p&gt;If you ever need to restore your backed up data all you need to do is find
the most recent full backup (we&#39;ll say &lt;code&gt;full-20071001.tar.gz&lt;/code&gt;) and all the
incremental backups since then (in our example &lt;code&gt;incr-2007100[2-5].tar.gz&lt;/code&gt;).
You&#39;ll start by extracting the full backup to the correct folder via &lt;code&gt;tar
xzf full-20071001.tar.gz&lt;/code&gt;, followed by the incremental backups oldest to
newest. Effectively you are restoring your entire home folder from n-days
ago and applying the differences from each succeeding day. The commands
should go as below (where &lt;code&gt;$&lt;/code&gt; is the shell prompt).&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;tar xzf full-20071001.tar.gz
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;tar xzf incr-20071002.tar.gz
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;tar xzf incr-20071003.tar.gz
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;tar xzf incr-20071004.tar.gz
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;tar xzf incr-20071005.tar.gz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;After which you should have you home directory restored exactly as it
appeared at 1:00pm on October 10, 2007.&lt;/p&gt;
</content>
	</entry>
	
</feed>
