<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-12429911</id><updated>2012-01-26T21:30:53.301-08:00</updated><category term='gwt'/><category term='java'/><category term='spring'/><title type='text'>GavinTech</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://gavintech.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://gavintech.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Gavin Andresen</name><uri>http://www.blogger.com/profile/10105284501947275111</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_yD2UuJRJTmU/ScVJh-9pldI/AAAAAAAABv4/D0iIhf-e_XQ/S220/GavinPic.png'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>16</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-12429911.post-2932531434408582265</id><published>2011-11-07T06:32:00.000-08:00</published><updated>2011-11-07T06:32:22.201-08:00</updated><title type='text'>Deploying Bitcoin-Qt on OSX...</title><content type='html'>My goal: create a .dmg file that anybody running OSX version 10.5 or later on an Intel-based Mac can use to install and run Bitcoin-Qt (aka Bitcoin version 0.5).&lt;br /&gt;&lt;br /&gt;How hard could it be? There must be plenty of people who are using the QT cross-platform graphical user interface toolkit to create Mac applications... right?&lt;br /&gt;&lt;br /&gt;With some help and hints and a lot of googling I did finally succeed, but not before giving up at least three separate times. I'm writing this to, hopefully, save somebody else lots of frustration.&lt;br /&gt;&lt;br /&gt;Side note: Bitcoin-Qt doesn't run on PowerPC chips, doesn't use more than 4 gigabytes of memory, and doesn't contain any CPU-performance-critical code. So there is no reason to create and deploy a "universal" app bundle, and I saved myself a lot of recompiling time by compiling just 32-bit i386.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Problem 1&lt;/b&gt;: I've got a 64-bit Mac running Snow Leopard (10.6).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Solution 1&lt;/b&gt;:  compile &lt;b&gt;everything&lt;/b&gt; with these flags:&lt;br /&gt;  -mmacosx-version-min=10.5 -arch i386 -isysroot /Developer/SDKs/MacOSX10.5.sdk&lt;br /&gt;&lt;br /&gt;Compiling Bitcoin-Qt with those flags is easy when you know how; just add something like this to the Bitcoin-Qt.pro file:&lt;br /&gt;&lt;pre&gt;macx:QMAKE_CXXFLAGS += -mmacosx-version-min=10.5 -arch i386 -isysroot /Developer/SDKs/MacOSX10.5.sdk&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Problem 1a&lt;/b&gt;: I'm using macports for dependencies, so it wasn't obvious how to compile everything with those flags.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Solution 1a&lt;/b&gt;: Edit /opt/local/etc/macports/macports.conf and set macosx_deployment_target and the build_arch:&lt;br /&gt;&lt;pre&gt;# Building for lowest-common denominator OSX 10.5, 32-bit&lt;br /&gt;# (see build_arch i386 later in this file)&lt;br /&gt;macosx_deployment_target 10.5&lt;br /&gt;&lt;br /&gt;# CPU architecture to compile for. Defaults to i386 or ppc on Mac OS X 10.5&lt;br /&gt;# and earlier, depending on the CPU type detected at runtime. On Mac OS X 10.6&lt;br /&gt;# the default is x86_64 if the CPU supports it, i386 otherwise.&lt;br /&gt;# GAVIN: overrode to default to 32-bit i386 build&lt;br /&gt;build_arch                      i386&lt;br /&gt;&lt;/pre&gt;I then ran:&lt;br /&gt;&lt;pre&gt;port list installed | cut -f 1 -d' ' | uniq &gt; /tmp/portsinstalled&lt;br /&gt;sudo port remove installed&lt;br /&gt;sudo port install $(cat /tmp/portsinstalled)&lt;br /&gt;&lt;/pre&gt;... to remove and recompile/reinstall everything. Yes, that took several hours.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Problem 2&lt;/b&gt;: macdeployqt refuses to deploy one of our dependencies.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Solution 2&lt;/b&gt;: file a bug (&lt;a href="https://bugreports.qt.nokia.com/browse/QTBUG-21913"&gt;QT-21913&lt;/a&gt;), and patch my copy of macdeployqt.&lt;br /&gt;&lt;br /&gt;How'd I patch? I found the macdeployqt source inside /opt/local/var/macports/build/ (find /opt/local/var/macports/build -name macdeployqt -print), copied it to $HOME/src/, and ran qmake/make to compile after editing its .cpp files.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Problem 3&lt;/b&gt;: macdeployqt created an App bundle that crashed on startup.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Solution 3&lt;/b&gt;: When you 'port install qt4-mac', it installs a shared-library version of Qt. macdeployqt seems to work with a shared-library version of Qt... but it really doesn't. It wants the 'Framework' version.  So the solution:  'port deactivate qt4-mac' then 'port install qt4-mac +framework'.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Problem 4&lt;/b&gt;: Images were not being displayed by the deployed App.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Solution 4&lt;/b&gt;: macdeployqt wasn't deploying plugins and the images are JPEG format which requires the JPEG plugin.&lt;br /&gt;&lt;br /&gt;It wasn't deploying them because it thinks the plugin path is "/Developer/Applications/Qt/plugins"... but since I'm using macports, the actual pluginPath is "/opt/local/share/qt4/plugins".&lt;br /&gt;&lt;br /&gt;I took the easy way out and changed the path in my patched version of macdeployqt.  I'll send the patch to the macports qt4-mac maintainer.&lt;br /&gt;&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;Did I make things hard for myself by following the &lt;a href="http://doc.trolltech.com/4.7/deployment-mac.html"&gt;Qt documentation for deploying on OSX&lt;/a&gt;-- is there some magical XCode or commercial application for creating App bundles that I don't know about that would have saved me days of frustration and work?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12429911-2932531434408582265?l=gavintech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gavintech.blogspot.com/feeds/2932531434408582265/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12429911&amp;postID=2932531434408582265' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/2932531434408582265'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/2932531434408582265'/><link rel='alternate' type='text/html' href='http://gavintech.blogspot.com/2011/11/deploying-bitcoin-qt-on-osx.html' title='Deploying Bitcoin-Qt on OSX...'/><author><name>Gavin Andresen</name><uri>http://www.blogger.com/profile/10105284501947275111</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_yD2UuJRJTmU/ScVJh-9pldI/AAAAAAAABv4/D0iIhf-e_XQ/S220/GavinPic.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12429911.post-965514600350145641</id><published>2008-08-14T07:16:00.000-07:00</published><updated>2008-08-14T07:28:45.179-07:00</updated><title type='text'>Yahoo Pipes Yay!</title><content type='html'>This morning I decided to take a little time and look at tools I could use to filter my RSS feeds; for example, I have some feeds written by multiple authors, and I might want to get entries posted by just one of the authors.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_yD2UuJRJTmU/SKQ_znoBzvI/AAAAAAAABoc/pkb8dSdC0gQ/s1600-h/Picture+1.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_yD2UuJRJTmU/SKQ_znoBzvI/AAAAAAAABoc/pkb8dSdC0gQ/s320/Picture+1.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5234378822954110706" /&gt;&lt;/a&gt;A little searching turned up some small companies that look like they're struggling (ZapTxt, Blastfeed, FeedRinse) and &lt;a href="http://pipes.yahoo.com/"&gt;Yahoo Pipes&lt;/a&gt;.  I've already got a Yahoo account, and Yahoo is the least likely of the services to disappear tomorrow, so that's what I used.&lt;br /&gt;&lt;br /&gt;And wow!  In less than five minutes I have a new rss feed that does exactly what I want!  Now I'm going to see if I can come up with a filtering rule that nukes posts that are nothing but recycled content from somewhere else... (and I may have to write a filter for any post by &lt;a href="http://delong.typepad.com/sdj/"&gt;Brad deLong&lt;/a&gt; that has the phrase "a better press corps" in it...).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12429911-965514600350145641?l=gavintech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gavintech.blogspot.com/feeds/965514600350145641/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12429911&amp;postID=965514600350145641' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/965514600350145641'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/965514600350145641'/><link rel='alternate' type='text/html' href='http://gavintech.blogspot.com/2008/08/yahoo-pipes-yay.html' title='Yahoo Pipes Yay!'/><author><name>Gavin Andresen</name><uri>http://www.blogger.com/profile/10105284501947275111</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_yD2UuJRJTmU/ScVJh-9pldI/AAAAAAAABv4/D0iIhf-e_XQ/S220/GavinPic.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_yD2UuJRJTmU/SKQ_znoBzvI/AAAAAAAABoc/pkb8dSdC0gQ/s72-c/Picture+1.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12429911.post-8290964038379565663</id><published>2008-07-16T08:30:00.000-07:00</published><updated>2008-07-16T08:49:31.193-07:00</updated><title type='text'>SoyLatte and GWT:  Retreat!</title><content type='html'>After much struggle and consternation trying to do development on my Mac with SoyLatte (version 1.0.2-- it's a JDK1.6 implementation for the Mac) and GWT (version 1.5), I've decided to retreat back to JDK1.5.&lt;br /&gt;&lt;br /&gt;So, I've recompiled lots of stuff (we had compiled our own and third-party JARs with JDK1.6), rewrote a couple of little bits of code (surprisingly little-- one use of Deque that was a straight-up replacement with LinkedList, and one use of java.text.Normalizer that was easy to replace with sun.text.Normalizer), and I'm much happier.&lt;br /&gt;&lt;br /&gt;Just in case it helps somebody else, my dead ends were:&lt;br /&gt;&lt;br /&gt;SoyLatte wouldn't load the libgwt-ll.jnilib .  Turns out simply creating a copy named "libgwt-ll.dylib" makes SoyLatte happy...&lt;br /&gt;&lt;br /&gt;... but trying to run the gwt hosted mode browser results in an error about not being able to open the X11 Display :0.0.   No problem, I've got X on my mac, so I startx and run from an xterm window...&lt;br /&gt;&lt;br /&gt;... and I get some weird exception deep in the SoyLatte code.  That's the point where I decided it would be less painful to go back to JDK1.5.&lt;br /&gt;&lt;br /&gt;Oh, also, another bizarre-ness:&lt;br /&gt;&lt;br /&gt;I'm using ant's os.name built-in property to read a operating-system-specific .antproperties file (so the build can use the GWTCompiler in either the gwt-dev-mac or gwt-dev-linux jar files), like this:&lt;pre&gt;&amp;lt;property file="${os.name}.antproperties" /&amp;gt;&lt;/pre&gt;Depending on which version of ant I'm using, os.name is either "Darwin" or "Mac OS X".&lt;br /&gt;&lt;br /&gt;I don't like to check in filenames with spaces in them-- in my experience it's just not a good idea.  So my solution is to check in the Darwin.antproperties file, and just manually create a symbolic link from that file to "Mac OS x.antproperties"&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12429911-8290964038379565663?l=gavintech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gavintech.blogspot.com/feeds/8290964038379565663/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12429911&amp;postID=8290964038379565663' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/8290964038379565663'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/8290964038379565663'/><link rel='alternate' type='text/html' href='http://gavintech.blogspot.com/2008/07/soylatte-and-gwt-retreat.html' title='SoyLatte and GWT:  Retreat!'/><author><name>Gavin Andresen</name><uri>http://www.blogger.com/profile/10105284501947275111</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_yD2UuJRJTmU/ScVJh-9pldI/AAAAAAAABv4/D0iIhf-e_XQ/S220/GavinPic.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12429911.post-1970689612872796437</id><published>2008-06-18T11:23:00.000-07:00</published><updated>2008-06-18T12:15:06.828-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='gwt'/><category scheme='http://www.blogger.com/atom/ns#' term='spring'/><title type='text'>Spring Security+GWT Rant</title><content type='html'>One day, I may come to love and appreciate Spring (aka acegi) Security.&lt;br /&gt;&lt;br /&gt;I'll appreciate the elegance of being able to inject security into web pages and methods that know nothing authentication and authorization.  Maybe one day I'll embrace editing XML configuration files and restarting my web server as a Better Way than writing code in a scripting language like PHP or Python and poking the refresh button on my web browser.&lt;br /&gt;&lt;br /&gt;That day is not today.&lt;br /&gt;&lt;br /&gt;My task seems like it should be reasonably straightforward:&lt;br /&gt; we've got a web site that you can browse either logged-in (in which case you'll see Edit buttons, and can submit edit/updates) or not-logged-in (in which case you see "Login to Edit" links).&lt;br /&gt;&lt;br /&gt;We're re-implementing an old version of the site using GWT and Spring.  I'm rewriting the login/logout code to use Spring Security... and MAN!  It's been PAINFUL!&lt;br /&gt;&lt;br /&gt;It's been painful for a few reasons:&lt;br /&gt;&lt;br /&gt;1. I'm new to Spring, XML configuration files, and GWT.  Oh, and Java and Eclipse, too (but I feel like I've started to master those in the last couple of months).&lt;br /&gt;&lt;br /&gt;2. There's lots of documentation and examples available online, but it's not easy to figure out what's relevant and what's not.  A lot of the documentation that's out there is for the old, deprecated, previous version (the acegi security library).  A lot of the new documentation assumes that you were using the old, deprecated version-- so it contains all sorts of stuff that I don't need to know.  But I don't know that I don't need to know it...&lt;br /&gt;&lt;br /&gt;3. The edit/compile/debug turnaround time is pretty bad.  Editing the xml file with the security stuff in it, then patching it into the war.d/ directory is quick, but starting up Jetty and refreshing my test web page takes about 45 seconds.&lt;br /&gt;&lt;br /&gt;ANYWAY, in the hopes that describing the snags I encountered might help somebody else, here are a few random things that are probably obvious to somebody who knows a lot more about this stuff than I do:&lt;br /&gt;&lt;br /&gt;Here's the security-context.xml file I'm using:&lt;br /&gt;&lt;pre&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;ISO-8859-1&amp;quot;?&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;!-- Spring Security settings --&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;beans:beans xmlns=&amp;quot;http://www.springframework.org/schema/security&amp;quot;&lt;br /&gt;    xmlns:beans=&amp;quot;http://www.springframework.org/schema/beans&amp;quot;&lt;br /&gt;    xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot;&lt;br /&gt;    xsi:schemaLocation=&amp;quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd&lt;br /&gt;                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.2.xsd&amp;quot;&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;http auto-config='true'&amp;gt;&lt;br /&gt;    &amp;lt;intercept-url pattern=&amp;quot;/foo/**&amp;quot; access=&amp;quot;ROLE_USER&amp;quot; /&amp;gt;&lt;br /&gt;  &amp;lt;/http&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;beans:bean id=&amp;quot;myJdbcDAO&amp;quot;&lt;br /&gt;        class=&amp;quot;org.springframework.security.userdetails.jdbc.JdbcDaoImpl&amp;quot;&amp;gt;&lt;br /&gt;    &amp;lt;beans:property name=&amp;quot;dataSource&amp;quot;&amp;gt;&lt;br /&gt;      &amp;lt;beans:ref bean=&amp;quot;varDataSource&amp;quot;/&amp;gt;&lt;br /&gt;    &amp;lt;/beans:property&amp;gt;&lt;br /&gt;    &amp;lt;beans:property name=&amp;quot;usersByUsernameQuery&amp;quot;&amp;gt;&lt;br /&gt;      &amp;lt;beans:value&amp;gt;SELECT email AS username, LOWER(password),'true' AS enabled FROM users WHERE email=?&amp;lt;/beans:value&amp;gt;&lt;br /&gt;    &amp;lt;/beans:property&amp;gt;&lt;br /&gt;    &amp;lt;beans:property name=&amp;quot;authoritiesByUsernameQuery&amp;quot;&amp;gt;&lt;br /&gt;      &amp;lt;beans:value&amp;gt;SELECT email AS username, 'ROLE_USER' AS authority FROM users WHERE email=?&amp;lt;/beans:value&amp;gt;&lt;br /&gt;    &amp;lt;/beans:property&amp;gt;&lt;br /&gt;  &amp;lt;/beans:bean&amp;gt;&lt;br /&gt;  &amp;lt;authentication-provider user-service-ref=&amp;quot;myJdbcDAO&amp;quot;&amp;gt;&lt;br /&gt;    &amp;lt;password-encoder hash=&amp;quot;sha&amp;quot;/&amp;gt;&lt;br /&gt;  &amp;lt;/authentication-provider&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/beans:beans&amp;gt;&lt;br /&gt;&lt;/pre&gt;In no particular order, this is different from what the Spring Security tutorials describe in the following ways:&lt;ol&gt;&lt;li&gt;For initial testing, I'm protecting a non-existent URL ( /foo ).  I want to make sure that I get a login page if I go there.&lt;/li&gt;&lt;li&gt;Usernames and passwords are stored in an already-existing database (the varDataSource is a JdbcTemplate database doo-hickey that is defined in another xml configuration file).&lt;/li&gt;&lt;li&gt;I had to step through the code for org.springframework.security.providers.dao.DaoAuthenticationProvider to figure out why I was getting "Bad credentials" when trying to login.  It turns out the SHA password comparison code inside Spring is fussy about upper and lowercase (you've gotta give it a lowercase hex string, which explains the LOWER(password) in the usersByUsernameQuery).&lt;/li&gt;&lt;li&gt;I've hardcoded all users to have ROLE_USER; eventually, I'll teach our old code to handle multiple roles&lt;/li&gt;&lt;li&gt;I only have a vague idea of why I have to say &amp;lt;beans:bean&amp;gt; (instead of just &amp;lt;bean&amp;gt;); if I understood XML namespaces I'm sure it would be easy and obvious.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;There's still one big security hole here-- passwords are sent across the network in the clear (I know that's true because I saw my clear-text password as I was stepping through the DaoAuthenticationProvider code on the server).   One solution is to require logins go over an https connection, and Spring Security supports that, but I'm surprised that this hole wasn't mentioned in any of the Spring Security tutorials or documentation that I came across.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12429911-1970689612872796437?l=gavintech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gavintech.blogspot.com/feeds/1970689612872796437/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12429911&amp;postID=1970689612872796437' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/1970689612872796437'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/1970689612872796437'/><link rel='alternate' type='text/html' href='http://gavintech.blogspot.com/2008/06/spring-securitygwt-rant.html' title='Spring Security+GWT Rant'/><author><name>Gavin Andresen</name><uri>http://www.blogger.com/profile/10105284501947275111</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_yD2UuJRJTmU/ScVJh-9pldI/AAAAAAAABv4/D0iIhf-e_XQ/S220/GavinPic.png'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12429911.post-381405913202086779</id><published>2008-03-03T17:13:00.000-08:00</published><updated>2008-03-22T10:50:35.417-07:00</updated><title type='text'>CMS Made Simple: very nice!</title><content type='html'>I've volunteered to help the Amherst League of Women Voters with their web site (&lt;a href="http://www.lwvamherst.org/"&gt;here&lt;/a&gt;).  Just about the first thing they mentioned is that they want an easier way to update content.&lt;br /&gt;&lt;br /&gt;I happen to know quite a bit about that problem; I implemented much of &lt;a href="http://www.gravityswitch.com/"&gt;Gravity Switch's&lt;/a&gt; "content management system" (what an awful term for "software that lets you create a web site").  The Gravity Switch CMS would be overkill for what they need, and it had been a while since I'd looked at the open source solutions, so I decided to take another look at open source solutions.&lt;br /&gt;&lt;br /&gt;The usual suspects-- Drupal, Joomla, WordPress-- are all still around, and they all have more than enough functionality.  But they all fail the simplicity test; any software for creating a web site that doesn't have a "Create a new page" button is too complex.&lt;br /&gt;&lt;br /&gt;Then I found &lt;a href="http://www.cmsmadesimple.org/"&gt;CMS Made Simple&lt;/a&gt;, and played around with it.  Finally, an open source CMS with a reasonable user interface, where simple things are simple and complex things are possible!  One of these days I'm going to get around to converting my old, hand-coded HTML home page to a CMS Made Simple-powered site...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12429911-381405913202086779?l=gavintech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gavintech.blogspot.com/feeds/381405913202086779/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12429911&amp;postID=381405913202086779' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/381405913202086779'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/381405913202086779'/><link rel='alternate' type='text/html' href='http://gavintech.blogspot.com/2008/03/cms-made-simple-very-nice.html' title='CMS Made Simple: very nice!'/><author><name>Gavin Andresen</name><uri>http://www.blogger.com/profile/10105284501947275111</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_yD2UuJRJTmU/ScVJh-9pldI/AAAAAAAABv4/D0iIhf-e_XQ/S220/GavinPic.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12429911.post-3055382081545203680</id><published>2008-02-20T18:32:00.000-08:00</published><updated>2008-02-20T19:19:10.961-08:00</updated><title type='text'>Python SIGALRM oddness</title><content type='html'>I usually don't stop debugging my code until I've found the root cause of a problem.  And the cause just about always turns out to be me being an idiot and doing something boneheaded.&lt;br /&gt;&lt;br /&gt;Anyway, I ran into a hard-to-reproduce, hard-to-debug problem with some Python code, and &lt;i&gt;still&lt;/i&gt; haven't figured out what's going on.&lt;br /&gt;&lt;br /&gt;The code in question downloads PDF files from the Web, using Python's httplib2.  I was using signal.alarm() to setup a timeout-- if httplib took more than four minutes to download the file, I wanted to abort the transfer and move on to the next file (otherwise a very slow web server could make the process take, essentially, forever).&lt;br /&gt;&lt;br /&gt;The relevant code started out like this:&lt;pre&gt;# How long to wait?  Lets do a "typical worst case":&lt;br /&gt;#   5 megabyte paper, through a fairly busy DSL connection (20Kbytes/second upload)&lt;br /&gt;#   works out to 250 seconds (downloads have a little over 4 minutes before timing out)&lt;br /&gt;alarm_timeout = 250&lt;br /&gt;&lt;br /&gt;def _alarm_handler(signum, frame):&lt;br /&gt;    """Bail out of HTTP reads if server takes too long."""&lt;br /&gt;    raise IOError(60, "fetch timed out")&lt;br /&gt;&lt;br /&gt;def _start_alarm(timeout=alarm_timeout):&lt;br /&gt;    signal.signal(signal.SIGALRM, _alarm_handler)&lt;br /&gt;    signal.alarm(timeout)&lt;br /&gt;&lt;br /&gt;def _stop_alarm():&lt;br /&gt;    signal.alarm(0)&lt;br /&gt;&lt;br /&gt;def _remember_and_write(reader, db, save_to_directory):&lt;br /&gt;    _start_alarm()&lt;br /&gt;    data = reader.read()&lt;br /&gt;    _stop_alarm()&lt;/pre&gt;The problem was that every once in a while the alarm wouldn't go off and the code would hang.  I'm fetching tens of thousands of pdf files, and maybe one in a thousand would hang-- and, of course, if I tried fetching that particular file again everything would work just fine.  Python wasn't hung-- if I sent the process a SIGINT signal it would wake up and generate a KeyboardInterrupt, showing a stack trace somewhere deep in the httplib fetching code.&lt;br /&gt;&lt;br /&gt;That stack trace gave me a clue, and helped me fix bug number 1:  I was timing out only around the reading of the body of the HTTP request, but sometimes fetching (for example) the robots.txt file or the HTTP header could take a very long time.  So, confident that I'd figured out the problem, I re-arranged the code so that an alarm was always set to go off if &lt;i&gt;anything&lt;/i&gt; took too long.  I re-run a big fetch... and it still hangs after several hours.&lt;br /&gt;&lt;br /&gt;I realize my code has another bug just thinking about the problem during my morning shower.  What if something in httplib is catching (and silently dropping) the IOError that I raise in the alarm handler?  So I rewrite my code to raise a custom exception, and re-run a big fetch...   and no dice, it still hangs.  But I can still interrupt it by sending a KeyboardInterrupt.&lt;br /&gt;&lt;br /&gt;Maybe httplib2 is catching ALL exceptions somewhere deep in it's code, so the alarm goes off, throws the exception, which is caught by httplib insted of interrupting out of whatever operation is hanging... I briefly contemplate digging into it's source, and decide it would be easier to just change my entire approach.&lt;br /&gt;&lt;br /&gt;Which is what I ended up doing.  Now I do a fork() to create two processes-- one of which does the downloads (as before), updating the timestamp on a file every time it starts a file download.  And another which checks that file every four minutes, and if it hasn't changed in over 4 minutes, sends the main process a SIGINT (which Python turns into a KeyboardInterrupt exception) to tell it to proceed to the next file.&lt;br /&gt;&lt;br /&gt;Works like a charm.  I wrapped up the "fork and monitor" functionality into this nifty little routine:&lt;pre&gt;#!/usr/bin/env python&lt;br /&gt;"""&lt;br /&gt;Routine that monitors a log file, and, if it hasn't been modified in a certain amount of time,&lt;br /&gt;sends a signal to the process that called the routine.&lt;br /&gt;&lt;br /&gt;Gavin ran into problems using SIGALRM, so came up with this solution instead.&lt;br /&gt;"""&lt;br /&gt;&lt;br /&gt;import atexit&lt;br /&gt;import os&lt;br /&gt;import signal&lt;br /&gt;import sys&lt;br /&gt;import time&lt;br /&gt;&lt;br /&gt;def monitor_and_signal(filename, timeout, signal):&lt;br /&gt;    "If file doesn't change in timeout seconds, send this process {signal}.  Returns monitoring process pid."&lt;br /&gt;&lt;br /&gt;    parent_pid = os.getpid()&lt;br /&gt;    # Strategy is:  fork a child process, which mostly sleeps.&lt;br /&gt;    pid = os.fork()&lt;br /&gt;    if pid == 0:&lt;br /&gt;        # We're the child process; monitor the file:&lt;br /&gt;        while 1:&lt;br /&gt;            time.sleep(timeout)&lt;br /&gt;            try:&lt;br /&gt;                info = os.stat(filename)&lt;br /&gt;            except OSError:&lt;br /&gt;                sys.exit(0)  # Nothing to monitor any more, exit.&lt;br /&gt;            if (time.time() - info.st_mtime) &gt; timeout:&lt;br /&gt;                try:&lt;br /&gt;                    os.kill(parent_pid, signal)&lt;br /&gt;                except OSError:&lt;br /&gt;                    sys.exit(0)  # Parent process goes away: exit.&lt;br /&gt;    return pid&lt;br /&gt;        &lt;br /&gt;if __name__ == '__main__':&lt;br /&gt;    fname = "/tmp/pretendLog.log"&lt;br /&gt;    fp = open(fname, "w")&lt;br /&gt;    fp.write("Testing\n")&lt;br /&gt;&lt;br /&gt;    monitor_and_signal(fname, 3, signal.SIGINT)&lt;br /&gt;&lt;br /&gt;    for i in range(1,8):&lt;br /&gt;        print("%d second delay (interrupt at &gt;3)"%i)&lt;br /&gt;        try:&lt;br /&gt;            time.sleep(i)&lt;br /&gt;        except KeyboardInterrupt:&lt;br /&gt;            print("Interrupt, was sleeping %d seconds"%i)&lt;br /&gt;        fp.write("%d\n"%i)&lt;br /&gt;        fp.flush()&lt;br /&gt;    fp.close()&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12429911-3055382081545203680?l=gavintech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gavintech.blogspot.com/feeds/3055382081545203680/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12429911&amp;postID=3055382081545203680' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/3055382081545203680'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/3055382081545203680'/><link rel='alternate' type='text/html' href='http://gavintech.blogspot.com/2008/02/python-sigalrm-oddness.html' title='Python SIGALRM oddness'/><author><name>Gavin Andresen</name><uri>http://www.blogger.com/profile/10105284501947275111</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_yD2UuJRJTmU/ScVJh-9pldI/AAAAAAAABv4/D0iIhf-e_XQ/S220/GavinPic.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12429911.post-6610241645900883934</id><published>2008-02-04T13:05:00.000-08:00</published><updated>2008-02-04T13:10:46.871-08:00</updated><title type='text'>Mac, gwt, SoyLatte Java 1.6 == unhappiness</title><content type='html'>I'm setting up a development environment for GWT (the Google Web Toolkit) on my Mac... and ran into "issues."&lt;br /&gt;&lt;br /&gt;It seemed to be going well, until I actually tried to run a sample application, and got:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Warning: The Mac OS -XstartOnFirstThread: is currently unsupported&lt;br /&gt;Unable to load required native library 'gwt-ll'.  Detailed error:&lt;br /&gt;Can't load library: /usr/local/lib/gwt-mac-1.4.61/libgwt-ll.dylib)&lt;br /&gt;&lt;br /&gt;Your GWT installation may be corrupt&lt;br /&gt;Exception in thread "main" java.lang.UnsatisfiedLinkError: Unable to load required native library 'gwt-ll'.  Detailed error:&lt;br /&gt;Can't load library: /usr/local/lib/gwt-mac-1.4.61/libgwt-ll.dylib)&lt;br /&gt;&lt;br /&gt;Your GWT installation may be corrupt&lt;br /&gt;        at com.google.gwt.dev.shell.LowLevel.init(LowLevel.java:107)&lt;br /&gt;        at com.google.gwt.dev.shell.mac.LowLevelSaf.init(LowLevelSaf.java:262)&lt;br /&gt;        at com.google.gwt.dev.BootStrapPlatform.go(BootStrapPlatform.java:29)&lt;br /&gt;        at com.google.gwt.dev.GWTShell.main(GWTShell.java:318)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;A couple of hours of googling, searching, reading release notes, scratching my head...&lt;br /&gt;&lt;br /&gt;... and I found the cause, and a workaround.&lt;br /&gt;&lt;br /&gt;The cause:  I was running SoyLatte (a version of Java 1.6 for the Mac).&lt;br /&gt;The workaround:  unset JAVA_HOME, and reset my PATH so I run the java that came with my Mac (version 1.5).&lt;br /&gt;&lt;br /&gt;Grumble, grumble... why does it seem like EVERYTHING I try to do with Java is about three times as hard as it should be?  Maybe I've just been spoiled by PHP and Python...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12429911-6610241645900883934?l=gavintech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gavintech.blogspot.com/feeds/6610241645900883934/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12429911&amp;postID=6610241645900883934' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/6610241645900883934'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/6610241645900883934'/><link rel='alternate' type='text/html' href='http://gavintech.blogspot.com/2008/02/mac-gwt-soylatte-java-16-unhappiness.html' title='Mac, gwt, SoyLatte Java 1.6 == unhappiness'/><author><name>Gavin Andresen</name><uri>http://www.blogger.com/profile/10105284501947275111</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_yD2UuJRJTmU/ScVJh-9pldI/AAAAAAAABv4/D0iIhf-e_XQ/S220/GavinPic.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12429911.post-1146263790733011310</id><published>2007-09-27T10:56:00.000-07:00</published><updated>2007-09-27T11:11:58.654-07:00</updated><title type='text'>Yay, Kingston Technology!</title><content type='html'>My MacBook Pro suddenly started acting really flaky a few weeks ago-- random kernel panics started happening a couple of times a week, then became more and more frequent until they were happening once or twice a day.&lt;br /&gt;&lt;br /&gt;I suspected maybe some software I'd installed, but just to be sure, I ran Apple's Hardware Diagnostic software (reboot the Mac with system install disk in while pressing the 'D' key).&lt;br /&gt;&lt;br /&gt;Surprise!  I got a cryptic error message that I soon figured out meant one of my two 1-gigabyte memory SIMMs was bad.  I have no idea which of the two, so I pulled out the one on top and re-ran the Test.  Murphy musta been on vacation, because the second run showed no errors.&lt;br /&gt;&lt;br /&gt;I don't buy memory from Apple, because they charge too much for it.  But I also don't buy cheap memory.  The SIMM that went bad was Kingston-brand memory, purchased from some online retailer (I don't remember which one).  So, it was time to put Kingston's lifetime warranty to the test.&lt;br /&gt;&lt;br /&gt;I gotta give kudos to Kingston.  I filled out their online RMA form, explaining how I'd diagnosed the problem, and got a fairly quick (next day) reply with instructions on how to return the bad memory.  A quick trip to the Fedex shipping store down the street, and about a week later I get a replacement SIMM from the Fedex guy (which I'm testing now in the Apple Hardware Test tool-- the memory, not the Fedex guy).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12429911-1146263790733011310?l=gavintech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gavintech.blogspot.com/feeds/1146263790733011310/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12429911&amp;postID=1146263790733011310' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/1146263790733011310'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/1146263790733011310'/><link rel='alternate' type='text/html' href='http://gavintech.blogspot.com/2007/09/yay-kingston-technology.html' title='Yay, Kingston Technology!'/><author><name>Gavin Andresen</name><uri>http://www.blogger.com/profile/10105284501947275111</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_yD2UuJRJTmU/ScVJh-9pldI/AAAAAAAABv4/D0iIhf-e_XQ/S220/GavinPic.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12429911.post-4894714698600111159</id><published>2007-07-03T10:51:00.001-07:00</published><updated>2007-07-03T11:09:09.870-07:00</updated><title type='text'>Stable, so far...</title><content type='html'>uptime for gavinserver is 18 days, running without a hitch in my basement with just the one (power) wire, so I've moved over the nightly cron job that computes internal-rate-of-return for my &lt;a href="http://www.gavintools.com/fantasy/"&gt;Fantasy Lending&lt;/a&gt; web site.&lt;br /&gt;&lt;br /&gt;I ran into two MORE issues:&lt;br /&gt;&lt;br /&gt;1. resolv.conf was getting overwritten by something (dhclient, I think), so DNS lookups broke.  I think installing the "resolvconf" package will fix that... we'll see.&lt;br /&gt;&lt;br /&gt;2. I want the cron daemon to send me email if Something Bad Happens with the nightly cron jobs-- otherwise it might be days before I notice that something isn't getting updated.  'mail gavinandresen@...' didn't work out-of-the-box; but I did get this somewhat-helpful message:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;The program 'mail' can be found in the following packages:&lt;br /&gt; * mailx&lt;br /&gt; * mailutils&lt;br /&gt;Try: sudo apt-get install &lt;selected package&gt;&lt;br /&gt;Make sure you have the 'universe' component enabled&lt;br /&gt;-bash: mail: command not found&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;So, I install mailx.  I don't want to get mail on this system, and made things even more difficult for myself because I decided to configure gavinserver as a 'satellite' system, with all outgoing mail sent via the EasyDNS mail server.&lt;br /&gt;&lt;br /&gt;Sending mail via mailout.easydns.com requires authentication with a username and password.  Which made me scratch my head, because the spiffy-auto-setup for postfix didn't let me specify a username and password.&lt;br /&gt;&lt;br /&gt;More web searching turns up a &lt;a href="http://www.freelock.com/kb/Postfix_relayhost"&gt;helpful web page&lt;/a&gt; that describes exactly what I need to do (create a SASL password file and point postfix at it).  Works like a charm the first time!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12429911-4894714698600111159?l=gavintech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gavintech.blogspot.com/feeds/4894714698600111159/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12429911&amp;postID=4894714698600111159' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/4894714698600111159'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/4894714698600111159'/><link rel='alternate' type='text/html' href='http://gavintech.blogspot.com/2007/07/stable-so-far.html' title='Stable, so far...'/><author><name>Gavin Andresen</name><uri>http://www.blogger.com/profile/10105284501947275111</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_yD2UuJRJTmU/ScVJh-9pldI/AAAAAAAABv4/D0iIhf-e_XQ/S220/GavinPic.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12429911.post-1683236507789292479</id><published>2007-06-12T15:26:00.000-07:00</published><updated>2007-06-14T06:38:02.672-07:00</updated><title type='text'>Almost TOO easy...</title><content type='html'>Eventually, I hope to setup dynamic DNS, and will connect to the Ubuntu machine from the server at my ISP, with my DSL router port-forwarding requests.  But to do that, the PC will have to have a static IP address inside my network.  So time to edit /etc/network/interfaces again (I decide 192.168.2.88 is a good address; the FreeBeer network router is at 192.168.2.1, and assigns DHCP addresses in the range 192.168.2.100 through 192.168.2.254):&lt;br /&gt;&lt;pre&gt;# Wireless interface&lt;br /&gt;auto ra0&lt;br /&gt;iface ra0 inet static&lt;br /&gt;address 192.168.2.88&lt;br /&gt;netmask 255.255.255.0&lt;br /&gt;gateway 192.168.2.1&lt;br /&gt;wireless-essid FreeBeer&lt;br /&gt;wireless-channel 11&lt;br /&gt;wireless-mode managed&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Restart the wireless connection with "ifdown ra0" "ifup ra0", and it's mostly working.  Except DNS is busted-- I can't "ping www.google.com".&lt;br /&gt;&lt;br /&gt;Solution is easy, if you know how:  figure out your DNS name servers, then add their IP addresses to /etc/resolv.conf.  In my case, that means going to the router's admin screen (http://192.168.2.1/), looking at the Primary and Secondary DNS settings on the router's "Connection Summary" screen, then adding:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;nameserver 71.243.0.12&lt;br /&gt;nameserver 68.237.161.12&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;... to /etc/resolv.conf.&lt;br /&gt;&lt;br /&gt;Restart the ra0 network interface (ifup/ifdown), then make sure I can "ssh gavin@192.168.2.88" from my mac.  Success again!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12429911-1683236507789292479?l=gavintech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gavintech.blogspot.com/feeds/1683236507789292479/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12429911&amp;postID=1683236507789292479' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/1683236507789292479'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/1683236507789292479'/><link rel='alternate' type='text/html' href='http://gavintech.blogspot.com/2007/06/almost-too-easy.html' title='Almost TOO easy...'/><author><name>Gavin Andresen</name><uri>http://www.blogger.com/profile/10105284501947275111</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_yD2UuJRJTmU/ScVJh-9pldI/AAAAAAAABv4/D0iIhf-e_XQ/S220/GavinPic.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12429911.post-2398653186180135511</id><published>2007-06-12T14:13:00.000-07:00</published><updated>2007-06-12T15:26:19.020-07:00</updated><title type='text'>Wireless progress</title><content type='html'>A Linksys WMP54G wireless PCI card arrived on my doorstep today (Amazon Prime; they hooked me with a free trial, and I'm now addicted...), so this afternoon I've been figuring out how to get it working with Ubuntu server.&lt;br /&gt;&lt;br /&gt;Apparently, Ubuntu has a very nice network configuration GUI I could use.  Except I installed Ubuntu Server, which doesn't include all the nice GUI stuff.  So... either I install all the nice GUI stuff (I'd already installed X11R6 on my Mac, so theoretically I could run the GUI stuff over an ssh connection).  Or I need to figure out how to set everything up via the command line.&lt;br /&gt;&lt;br /&gt;Googling for "ubuntu wireless", I find the &lt;a href="https://help.ubuntu.com/community/WifiDocs/WiFiHowTo"&gt;Wifi HowTo&lt;/a&gt; page, which points me in all the right directions.  The iwconfig command tells me that the wireless card is named "ra0".  I add this to /etc/network/interfaces:&lt;br /&gt;&lt;pre&gt;# Wireless interface&lt;br /&gt;auto ra0&lt;br /&gt;iface ra0 inet dhcp&lt;br /&gt;wireless-essid FreeBeer&lt;br /&gt;wireless-channel 11&lt;br /&gt;wireless-mode managed&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;then "ifup ra0", and success! It connects!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12429911-2398653186180135511?l=gavintech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gavintech.blogspot.com/feeds/2398653186180135511/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12429911&amp;postID=2398653186180135511' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/2398653186180135511'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/2398653186180135511'/><link rel='alternate' type='text/html' href='http://gavintech.blogspot.com/2007/06/wireless-progress.html' title='Wireless progress'/><author><name>Gavin Andresen</name><uri>http://www.blogger.com/profile/10105284501947275111</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_yD2UuJRJTmU/ScVJh-9pldI/AAAAAAAABv4/D0iIhf-e_XQ/S220/GavinPic.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12429911.post-8808757859618191049</id><published>2007-06-08T20:19:00.000-07:00</published><updated>2007-06-08T20:43:10.830-07:00</updated><title type='text'>Easy when you know how</title><content type='html'>So the Ubuntu patient is now running and on the network (wired).  I ask my Router for it's IP address, and try to ssh in from my Mac.&lt;br /&gt;&lt;br /&gt;No dice.  Hmm... maybe sshd isn't running.  Nope.  Not even on the system.&lt;br /&gt;&lt;br /&gt;&lt;a href="https://help.ubuntu.com/7.04/server/"&gt;The Ubuntu documentation&lt;/a&gt; is actually pretty helpful, and after a quick "apt-get install ssh"  (after I'd sudo bash to get a root shell) I'm able to "ssh gavin@192.168.1.109" and get a Password: prompt, and login remotely.&lt;br /&gt;&lt;br /&gt;Which is good, because Michele wants to use the new PC to play Guild Wars, and I'd stolen it's power/keyboard/ethernet/etc.  So I move it across the room, and plug it in "headless" -- just power and ethernet attached.  Turn it on... and yay!  I can still ssh in.&lt;br /&gt;&lt;br /&gt;Setup continues; "apt-get install emacs" (gotta have emacs), and lynx, and curl, and subversion.  I need a few Perl packages, so I try:  perl -MCPAN -e 'install Bundle::CPAN', which fails miserably.  It's missing EVERYTHING (make, c compiler, ...).&lt;br /&gt;&lt;br /&gt;Hmm.  Browse documentation... Aha!  I need to  "apt-get install build-essential" to get make and g++ and all that Good Stuff.&lt;br /&gt;&lt;br /&gt;I forget how to get CPAN to re-run it's configuration script to figure out where make is.  And I don't want to configure it all by hand.  So I use the hammer approach-- I remove the ~/.cpan directory and /etc/perl/CPAN/Config.pm, then re-run perl -MCPAN -e ...  Success!&lt;br /&gt;&lt;br /&gt;I'm getting closer, I can feel it...  Ok, try running the PHP script that computes portfolio returns.  Oops, missing php, gotta apt-get install php5-cli.&lt;br /&gt;&lt;br /&gt;Run again.  Oops, missing php's Math/Finance.  That's from &lt;a href="http://pear.php.net"&gt;PEAR&lt;/a&gt;.  Ok, apt-get install php-pear, then pear install --alldeps Math_Finance.&lt;br /&gt;&lt;br /&gt;Man oh man, that's a lot of arcane stuff sitting around in my brain.  I'm starting to think maybe I should have installed the Desktop Ubuntu; maybe installing one of the graphical Linux IDE's from the graphical installer would have done most of this stuff for me.&lt;br /&gt;&lt;br /&gt;Let's see how many different ways of installing software I've used so far:&lt;br /&gt;  + Burn an installation CD and boot from it&lt;br /&gt;  + apt-get&lt;br /&gt;  + perl -MCPAN&lt;br /&gt;  + pear install&lt;br /&gt;&lt;br /&gt;At least I haven't had to download a .tar.gz file and then edit configuration files and compile from source.  Or use the rpm command.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12429911-8808757859618191049?l=gavintech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gavintech.blogspot.com/feeds/8808757859618191049/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12429911&amp;postID=8808757859618191049' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/8808757859618191049'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/8808757859618191049'/><link rel='alternate' type='text/html' href='http://gavintech.blogspot.com/2007/06/easy-when-you-know-how.html' title='Easy when you know how'/><author><name>Gavin Andresen</name><uri>http://www.blogger.com/profile/10105284501947275111</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_yD2UuJRJTmU/ScVJh-9pldI/AAAAAAAABv4/D0iIhf-e_XQ/S220/GavinPic.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12429911.post-3165994838593617778</id><published>2007-06-08T18:45:00.000-07:00</published><updated>2007-06-08T18:57:20.228-07:00</updated><title type='text'>No Wireless For You!</title><content type='html'>The first part of this project went pretty smoothly.  I downloaded the Ubuntu Server .iso CD image to my Mac.&lt;br /&gt;&lt;br /&gt;I already happened to know that I needed to open it in "Disk Utility" to burn it to CD-- the last time I needed to burn a .iso, I think I remember it taking me a bunch of head-scratching and googling before figuring out how to do it (launch Disk Utility, File...Open the .iso image, make sure it's selected, then poke the big Burn button).&lt;br /&gt;&lt;br /&gt;I purchased a Belkin Wireless G Desktop network card, and installed it in the PC (replacing the wired ethernet card).&lt;br /&gt;&lt;br /&gt;Then I had to figure out how to tell the PC to boot into it's BIOS (the PC is a Dell Dimension 4100).  Umm....  Escape?  Nope.  Ummm.... F1?   Nope.  Ok, google it... aha, press the F2 key while booting.  Theoretically I knew that, but it's been a year or three since doing any significant work on a PC.&lt;br /&gt;&lt;br /&gt;So, bring up the BIOS, tell it to boot from the CD first, put the CD in the drive, and reboot.&lt;br /&gt;&lt;br /&gt;Yay!  Ubuntu installer runs.  After answering a bunch of questions, things seemed to be going swimmingly... until I was asked which networking I wanted, "wifi0" or "aeth0".  Huh?  I dunno... I try wifi0, which fails.&lt;br /&gt;&lt;br /&gt;More googling.  As far as I can see, the Belkin wireless card isn't supported by Linux out-of-the-box.  Sigh.&lt;br /&gt;&lt;br /&gt;Ok, gonna return the Belkin, and buy a Linksys card, which (apparently) is better supported.  I do NOT want to spend hours twiddling network settings to try to get a brand-new wireless card to work.&lt;br /&gt;&lt;br /&gt;So:  sudo shutdown -h now   to shutdown.  Then remove the wireless card, put back in the wired card.  Reboot.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12429911-3165994838593617778?l=gavintech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gavintech.blogspot.com/feeds/3165994838593617778/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12429911&amp;postID=3165994838593617778' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/3165994838593617778'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/3165994838593617778'/><link rel='alternate' type='text/html' href='http://gavintech.blogspot.com/2007/06/no-wireless-for-you.html' title='No Wireless For You!'/><author><name>Gavin Andresen</name><uri>http://www.blogger.com/profile/10105284501947275111</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_yD2UuJRJTmU/ScVJh-9pldI/AAAAAAAABv4/D0iIhf-e_XQ/S220/GavinPic.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12429911.post-2906814802920892945</id><published>2007-06-08T18:34:00.000-07:00</published><updated>2007-06-08T18:45:10.945-07:00</updated><title type='text'>Ubuntu Server, baby...</title><content type='html'>I'm running a couple of web sites on an inexpensive (less than $50/month) "virtual private server" at SolarVPS.&lt;br /&gt;&lt;br /&gt;My development machine is a Mac Powerbook Pro-- I spend most of my time in emacs in a bash shell, because I'm a crotchety old Unix hacker.  I was running a bunch of CPU-intensive stuff (like calculating internal-rate-of-return on the Fantasy Prosper portfolios at gavintools.com/fantasy ) on my development machine, fired off from a cron job every day.  Which really isn't the right way of doing things-- Bad Things Happen when I take my laptop travelling and it's not hooked up to a network when the cron job wants to do it's thing.&lt;br /&gt;&lt;br /&gt;It would be a bad idea to run the really CPU-intensive stuff on the VPS; I'm sharing the CPU with other random folks.  Besides, I don't have a full development environment (compiler, etc) setup on that system.  So, I need a system that's always turned on, always connected to the network, and can do a bunch of CPU-intensive work.&lt;br /&gt;&lt;br /&gt;So I've decided to take an old (circa 2001) PC and turn it into my number-crunching server.  The plan:&lt;br /&gt;&lt;br /&gt;1. Install Ubuntu Server (Linux) on it, with all the extra stuff I need.&lt;br /&gt;2. Put a wireless networking card in it.&lt;br /&gt;3. Move it down into the basement.&lt;br /&gt;&lt;br /&gt;Should be easy for an old unix hacker...  except I'm not really a system administrator, I'm a software developer.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12429911-2906814802920892945?l=gavintech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gavintech.blogspot.com/feeds/2906814802920892945/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12429911&amp;postID=2906814802920892945' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/2906814802920892945'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/2906814802920892945'/><link rel='alternate' type='text/html' href='http://gavintech.blogspot.com/2007/06/ubuntu-server-baby.html' title='Ubuntu Server, baby...'/><author><name>Gavin Andresen</name><uri>http://www.blogger.com/profile/10105284501947275111</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_yD2UuJRJTmU/ScVJh-9pldI/AAAAAAAABv4/D0iIhf-e_XQ/S220/GavinPic.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12429911.post-6087962624530201679</id><published>2007-05-24T06:28:00.000-07:00</published><updated>2007-05-24T06:59:32.090-07:00</updated><title type='text'>Tragedy of the email commons</title><content type='html'>I was listening to &lt;a href="http://www.amazon.com/Ethics-Liberty-Murray-Newton-Rothbard/dp/0814775594/ref=pd_bbs_sr_1/102-7780632-4520129?ie=UTF8&amp;s=books&amp;qid=1180013510&amp;sr=1-1"&gt;"The Ethics of Liberty"&lt;/a&gt; yesterday, and  it got me thinking about email lists.&lt;br /&gt;&lt;br /&gt;Most email discussion lists seem to suffer from a Tragedy of the Commons-- there's not a whole lot of motivation for people posting their opinions to think twice about spouting off, or going on and on and on, or otherwise polluting the list with stuff that the subscribers to the list might consider junk.&lt;br /&gt;&lt;br /&gt;There are a couple of solutions to these kinds of problems.  You can try to set up some kind of policing system, with trusted moderators given the power to bless posts, ban people, etc.  You need really thick-skinned, generous-hearted, even-tempered moderators for that to work.&lt;br /&gt;&lt;br /&gt;A solution that works in the real world that hasn't been tried for the email mailing list problem (as far as I know) is some kind of economic system, where the common resource is given a value and anybody wanting to use the resource must pay rent (which is then used to maintain the resource).&lt;br /&gt;&lt;br /&gt;So, here's a thumbnail sketch of how this might work for an email mailing list:&lt;br /&gt;&lt;br /&gt;Create an artificial currency.  If you have enough of this currency, you're allowed to post messages to the list.  Longer posts cost more than shorter posts.&lt;br /&gt;&lt;br /&gt;Everybody starts with some amount of this currency.  Everybody is allowed to use it themselves, give it away, or sell it (for real money!) to people who want to use it to post messages.&lt;br /&gt;&lt;br /&gt;Every message posted includes ways of:&lt;br /&gt; - Tagging the message as "Worthwhile" --&gt; transfers currency from your account to the poster's account.&lt;br /&gt; - Tagging the message as "Waste of my Time" --&gt; transfers currency from poster's account to the common pool of currency.&lt;br /&gt;&lt;br /&gt;The amount of currency in the system is tied to the desired message traffic.  We all have a limited attention span; basically, the trick will be to create an artificial economy so that the people who contribute positively are encouraged to contribute more, and people who "pollute" are punished.&lt;br /&gt;&lt;br /&gt;Currency goes into the system every day (again, based on desired message traffic per day).  Some is assigned to new people signing up for the list, and the rest evenly distributed to everybody already on the list.&lt;br /&gt;&lt;br /&gt;"Lurkers" on the list could either hoard their currency, offer to sell it, or give it away to people from whom they want to hear more.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12429911-6087962624530201679?l=gavintech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gavintech.blogspot.com/feeds/6087962624530201679/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12429911&amp;postID=6087962624530201679' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/6087962624530201679'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/6087962624530201679'/><link rel='alternate' type='text/html' href='http://gavintech.blogspot.com/2007/05/tragedy-of-email-commons.html' title='Tragedy of the email commons'/><author><name>Gavin Andresen</name><uri>http://www.blogger.com/profile/10105284501947275111</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_yD2UuJRJTmU/ScVJh-9pldI/AAAAAAAABv4/D0iIhf-e_XQ/S220/GavinPic.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12429911.post-6141351501705692238</id><published>2007-03-23T10:39:00.000-07:00</published><updated>2007-03-23T10:46:06.507-07:00</updated><title type='text'>Coding web forms using PHP</title><content type='html'>I've made a bunch of fixes and upgrades to my code for &lt;a href="http://www.onlamp.com/pub/a/php/2006/03/16/autofill-forms.html"&gt;auto-filling PHP forms&lt;/a&gt;.  It's really a much, much nicer way of dealing with form re-display than the traditional way of mixing PHP code into your page's HTML.  And it's a lot simpler than using a form API to generate the form programatically from your PHP code.&lt;br /&gt;&lt;br /&gt;The biggest change: it now Does The Right Thing with arrays of values (form elements with name="foo[]", which automagically get turned into PHP arrays when they're submitted).&lt;br /&gt;&lt;br /&gt;Get it from http://www.skypaint.com/gavin/code/.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12429911-6141351501705692238?l=gavintech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gavintech.blogspot.com/feeds/6141351501705692238/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12429911&amp;postID=6141351501705692238' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/6141351501705692238'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12429911/posts/default/6141351501705692238'/><link rel='alternate' type='text/html' href='http://gavintech.blogspot.com/2007/03/coding-web-forms-using-php.html' title='Coding web forms using PHP'/><author><name>Gavin Andresen</name><uri>http://www.blogger.com/profile/10105284501947275111</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_yD2UuJRJTmU/ScVJh-9pldI/AAAAAAAABv4/D0iIhf-e_XQ/S220/GavinPic.png'/></author><thr:total>0</thr:total></entry></feed>
