I waste way too much time on the web. Most people I know do—it's the TV of the new generation. Sure, the web is more interactive, more informative, and more social than TV ever was, but most of my web-surfing is still just time-wasting. And it's addictive. I had real trouble keeping myself away from the web. Then I figured, “I'm a programmer. I bend computers to my will, not the other way around. I can fix this.”

Like so many programming tasks, this started out simply, with a cronjob run by root that looked like this:

*/3 * * * * /sbin/ifconfig eth0 down

That just shuts down my internet connection every three minutes (try man 5 crontab for help understanding the format). When I really wanted to surf the web, I could remove this. Otherwise, it prevented me from wasting too much time, but I could still do an ifup and pop onto the web for emergencies.

Then I realized that my real problem was the web, and shutting off my whole internet connection was an overly broad solution. There were things I really needed the internet for, like email and apt-get. So I came up with a new cronjob.

*/3 * * * * if (ps ax | grep -v grep | grep -q firefox) || (ps ax | grep -v grep | grep -q lynx); then /sbin/ifconfig eth0 down ; fi

Now, I would only have my internet connection shut down if the cronjob caught me browsing the web. If I was a good boy, I could use email as much as I wanted. For a time, life was good, or at least better: I was once again able to plan lunches successfully.

But after a while, I noticed that there are actually lots of useful non-time-wasting things I do on the web. Like buying books, getting directions, or checking on my bills. I wanted to be able to do these things without fear of interruption, and I didn't want to completely switch off my anti-web cronjob every time I needed driving directions. My first solution was to hack up a way to turn off my anti-web cronjob for a limited time.

set -o errexit set -o nounset NETLOG="$HOME/.net-use.log" read -e -p"Number of minutes to use internet: " minutes=$REPLY read -e -p"Purpose of internet use: " purpose=$REPLY date +"%Y-%m-%dT%k:%M Using internet for $minutes minutes for $purpose" >> "$NETLOG" # Start by emptying out root's crontab LOAD_CRONTAB='crontab -u root /tmp/root.crontab' cat > /tmp/root.crontab <<EOF # Nothing EOF $LOAD_CRONTAB ifconfig eth0 up && dhclient eth0 # After the specified number of minutes, set the crontab up so that it # shuts down the internet regularly cat > /tmp/root.crontab <<EOF */3 * * * * if (ps ax | grep -v grep | grep -q firefox) || (ps ax | grep -v grep | grep -q lynx); then /sbin/ifconfig eth0 down ; fi EOF echo $LOAD_CRONTAB | at now + $minutes minutes

Unfortunately, this was too permissive. I could easily tell my script to give me twenty minutes, and if I hit the end of my twenty minutes and still had some interesting stuff to read (which I almost invariably did), I could grab another twenty minutes, or even more. So the script wasn't much of a deterrent to time-wasting web browsing. To combat this, I limited myself to using the web only once a day, by adding this snippet near the top of the script.

if [ -e "$NETLOG" ] then if grep -E -q $(date +"^%Y-%m-%d") "$NETLOG" then echo "No more Web for you!" sleep 3 exit 1 fi fi

But this swung too far the other way. Now if I'd looked at Reddit or Facebook in the morning, I couldn't order a pizza for dinner. Or I could, but I'd have to use a phone—what is this, the 90s? If my daughter asks me how many moons Jupiter has (answer: a lot), I might forget about it before I get access to Wikipedia again.

So back to the drawing board. It turns out that it's still overly broad to say that “the web” is a problem: relatively few sites give me trouble. Instead of shutting down my whole internet connection, I can avoid wasting time on the web by blocking just a handful of sites. That way, I can have easy access to Google, Wikipedia, and StackOverflow without accidentally losing an hour to clever parodies of motivational posters. I just needed to mess with my /etc/hosts file.

set -o errexit set -o nounset NETLOG="$HOME/.net-use.log" BLOCKED_HOSTS="$HOME/my_configs/blocked_hosts" # Don't go web surfing more than once a day if [ -e "$NETLOG" ] then if grep -E -q $(date +"^%Y-%m-%d") "$NETLOG" then echo "No more Web for you!" sleep 3 exit 1 fi fi read -e -p"Number of minutes to use internet: " minutes=$REPLY read -e -p"Purpose of internet use: " purpose=$REPLY date +"%Y-%m-%dT%k:%M Using internet for $minutes minutes for $purpose" >> "$NETLOG" # Start by emptying out root's crontab cat "$BLOCKED_HOSTS" | grep -E -v '^!' > /etc/hosts LOAD_CRONTAB='crontab -u root /tmp/root.crontab' cat > /tmp/root.crontab <<EOF # Nothing EOF $LOAD_CRONTAB ifconfig eth0 up && dhclient eth0 # After the specified number of minutes, set the crontab up so that it # regularly blocks the sites specified in $BLOCKED_HOSTS cat > /tmp/root.crontab <<EOF */2 * * * * cat "$BLOCKED_HOSTS" | sed -e's/^!/127.0.0.1\t/' > /etc/hosts EOF echo $LOAD_CRONTAB | at now + $minutes minutes

Now I just need to maintain a copy of my /etc/hosts file under $HOME/my_configs/blocked_hosts[1] with blocked sites listed on lines starting with !, like this (alphabetized for my convenience):

127.0.0.1 localhost !cnn.com !digg.com !facebook.com ...

And the cronjob keeps those files blocked in my /etc/hosts file by redirecting them to localhost, where I don't generally have anything listening on port 80. Once a day, I can give myself access to those sites for some period of time, but the script ensures that I don't go to my time-wasting sites more than once a day.

Once I got to this point, it was fairly obvious that I no longer needed to specify a time or reason for my web surfing. Now it's always for fun, and twenty minutes is plenty. In fact, if this is strictly time-wasting fun browsing, I should be able to limit it to once every two days. So I can simplify my code a little and end up with this:

set -o errexit set -o nounset NETLOG="$HOME/.net-use.log" BLOCKED_HOSTS="$HOME/my_configs/blocked_hosts" DATECMD='date +"%Y-%m-%d"' # Don't go web surfing more than once every two days if [ -e "$NETLOG" ] then today=$($DATECMD) yesterday=$($DATECMD --date='now - 1 day') if grep -E -q "$today" "$NETLOG" || grep -E -q "$yesterday" "$NETLOG" then echo "No more Web for you!" sleep 3 exit 1 fi fi $DATECMD >> "$NETLOG" # Start by emptying out root's crontab cat "$BLOCKED_HOSTS" | grep -E -v '^!' > /etc/hosts LOAD_CRONTAB='crontab -u root /tmp/root.crontab' cat > /tmp/root.crontab <<EOF # Nothing EOF $LOAD_CRONTAB ifconfig eth0 up && dhclient eth0 # After the specified number of minutes, set the crontab up so that it # regularly blocks the sites specified in $BLOCKED_HOSTS cat > /tmp/root.crontab <<EOF */2 * * * * cat "$BLOCKED_HOSTS" | sed -e's/^!/127.0.0.1\t/' > /etc/hosts EOF echo $LOAD_CRONTAB | at now + 20 minutes

This works pretty well. Now that I've effectively broken the addiction, I find I don't even use this script every two days. It's more like twice a week.

There are a few points to take away from this. First, you'd be amazed what sorts of behavioral changes you can make just by forcing your desired behavior to be the default[2]. Second, always look for automation opportunities—even your personal habits are not exempt. Third, keep iterating until you solve your problem the right way: I was reasonably satisfied with several of the solutions above, but I'm glad I kept going until I hit on my final version.

Now if you'll excuse me, I've got to go write a quick script to prevent me from playing video games unless it's Saturday.