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.