Launchd Daemon to block Social Media on macOS

Years ago I had an anacron job that would automatically block the forums I tended to want to read during my work day, and then automatically unblock them when the workday was over. By “block”, here, I mean redirect to local – so that when I went to those websites by typing them into my browser, I’d get just a DNS error saying the page couldn’t be found. I used anacron (instead of cron) because the tasks would “run” even if no user was logged in, even if the computer was asleep, or off… etc. Anyway – I did this as a little productivity hack.

After a week off twitter recently, I decided I wanted to bring this hack back – except updated for social media, not Something Awful. But it turns out … macOS has deprecated cron / anacron and launchd has adopted their functionality.

What follows is a tutorial on how to schedule a launchd daemon to block, and unblock, social media on your computer from 11am to 2pm and then 3pm to 8pm.

1. Hosts File

The heart of this daemon is the hosts file – a system configuration file that tells your computer how to DNS. One thing the hosts file lets you do is redirect websites and what we’re going to do is redirect everything that encourages procrastination to your local server, which I’m gonna assume is not configured but if it is … I hope it’s not, like, a flash game or anything.

Pop open terminal and type

cd /etc/

followed by enter, and then

sudo vi hosts

sudo is “superuser do” – its running a command at the highest permission your computer has. This should not be done lightly and if you’ve never done it before, your computer will warn you about as much and ask if you’re sure. Say yes, but don’t go sudoing things left and right after this.

vi is a text editor. If you need a quick tutorial on using vi, this one is great. A quick crash course: when you first enter vi, you’re in COMMAND mode. In order to edit the file you’ve opened you must enter INSERT mode, which you do by pressing “i”. After you’ve made all your edits, you save by typing “:” and then “w” followed by pressing enter. You can quit vi by entering “:q” and if you want to save and quit vi all in one go – you enter “:wq”

Your hosts file probably looks like this

##
# Host Database
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##

127.0.0.1 localhost

255.255.255.255 broadcasthost
::1 localhost
fe80::1%lo0 localhost


Mine looks like this

##
# Host Database
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##

127.0.0.1 localhost

127.0.0.1 huffingtonpost.com
127.0.0.1 www.huffingtonpost.com
127.0.0.1 www.nypost.com
127.0.0.1 nypost.com
127.0.0.1 thoughtcatalog.com
127.0.0.1 www.thoughtcatalog.com
127.0.0.1 buzzfeed.com
127.0.0.1 www.buzzfeed.com

#BUSY DAY
127.0.0.1 facebook
127.0.0.1 www.facebook.com
127.0.0.1 api.facebook.com
127.0.0.1 *.tumblr.com
127.0.0.1 tumblr.com
127.0.0.1 www.tumblr.com
127.0.0.1 twitter.com
127.0.0.1 www.twitter.com
127.0.0.1 www.tumblr.com/dashboard
127.0.0.1 userstream.twitter.com
127.0.0.1 api.twitter.com
127.0.0.1 reddit.com
127.0.0.1 www.reddit.com
127.0.0.1 instagram.com
127.0.0.1 www.instagram.com

255.255.255.255 broadcasthost
::1 localhost
fe80::1%lo0 localhost

Everything under #BUSYDAY is what gets blocked, and unblocked, when the daemon does it’s job throughout the day (everything above #BUSYDAY is always blocked – websites I never want to look at, even accidentally, ever).

Edit your hosts now to look like mine, adding #BUSYDAY and then one per line: 127.0.0.1, which is the IP of your local server, space, and one website you want to redirect there. Some websites have multiple URLs; I’m sure there’s a more efficient way to do what I’ve done above.

After you’ve done that, save your newly edited hosts file in vi, and open a browser (you may need to close and then open a browser). Upon going to, say, twitter.com … you should be met with an error. If you’re not… something went wrong and you should go back and check your work.

2. FILE MANAGEMENT

The way this daemon works is that it manages a couple different versions of your hosts file – moving them around so the right hosts file is “in effect” at the right time. The next two steps in this process involve creating the different hosts files that need managing, and then writing two very short programs that help do the managing.

If you haven’t already, quit vi and you’ll be dumped back to the terminal prompt. Into terminal, enter

sudo cp hosts hosts-busyday

You’ve just made a copy of hosts called hosts-busyday – which we’re gonna leave as is but, actually, we are gonna edit hosts again. So:

sudo vi hosts

Now – put a “#” in front of every entry you just made. # is a comment marker – it tells your computer to ignore what comes after it on that line. So we’re gonna comment out all your new redirects (that you want to schedule – if you want permanent blocks like my huffpo block, don’t comment those out). Your #BUSYDAY section should like this:

#BUSY DAY
#127.0.0.1 facebook
#127.0.0.1 www.facebook.com
#127.0.0.1 api.facebook.com
#127.0.0.1 *.tumblr.com
#127.0.0.1 tumblr.com
#127.0.0.1 www.tumblr.com
#127.0.0.1 twitter.com
#127.0.0.1 www.twitter.com
#127.0.0.1 www.tumblr.com/dashboard
#127.0.0.1 userstream.twitter.com
#127.0.0.1 api.twitter.com
#127.0.0.1 reddit.com
#127.0.0.1 www.reddit.com
#127.0.0.1 instagram.com
#127.0.0.1 www.instagram.com

Save, exit vi, and then test. Is twitter reachable again? If so: good. If not: go back and check your work.

3. BASH SCRIPTS

If you’ve never programmed before, guess what – you’re about to write your first (and second!). These are very simple programs that your computer is going to run on a schedule. All they do is move files around – those hosts files you were just working on, in fact.

First things first, find or create a directory that you’re not gonna mess with. My stable of important scripts is kept in /Users/rugnetta/bin, but you can keep yours wherever. I recommend against the desktop. If you wanna be fancy you can make the folder in terminal by typing

mkdir /Users/[your username]/bin

then navigate to that directory and create two new files by using the “touch” command

cd /Users/[your username]/bin
touch busyday-start.sh
touch busyday-end.sh

open busyday-start in vi and enter the following:

#!/bin/bash

cp /etc/hosts /etc/hosts-placeholder
cp /etc/hosts-busyday /etc/hosts

This code will copy hosts (not blocking twitter atm) to a placeholder file, and copy hosts-busyday (which does block twitter) into its place. Save and exit vi. Back at the command prompt, open busyday-end in vi and enter the following:

#!/bin/bash

mv /etc/hosts-placeholder /etc/hosts

This does the opposite: it moves hosts-placeholder (not blocking) back to hosts, overwriting the current hosts file (blocking) and deleting hosts-placeholder in the process. Save and exit vi.

Back at the command prompt, we have to do one tricky thing: make it so these scripts are executable by the system, not just you, the user who created them. We do this with the chmod command. In terminal, type

chmod 700 busyday-start.sh

and

chmod 700 busyday-end.sh

After you’ve done this, its probably not a bad idea to run each program IN ORDER to make sure they work. If you’re still in the directory you’ve created your scripts in, type

sudo ./busyday-start.sh

into terminal and press enter. Is twitter blocked? It better be. And then type

sudo ./busyday-end.sh

Everything connect again? If not – go and check all your work. Everything in those scripts has to be exactly right or else it wont work.

4. SCHEDULING macOS

So now that we’ve made a couple hosts files and written a couple programs to move them around, we have to set up a little task on your computer to run those programs that manage those files at convenient times. We wanna make it so that your computer, with no further input from you, will magically block and unblock social media (or whatever) as the day progresses. We do this with launchd

There are a couple places you can put launchd daemons – we’re going to put ours in /Library/LaunchDaemons/ for a specific reason: we want them to run no matter what state the computer is in. On, off, user logged in, no user logged in, asleep. No matter what, we want these scripts to run on a system level. So make sure that happens, we place our daemon in /Library/LaunchDaemons/.

In terminal type

cd /Library/LaunchDaemons/
sudo touch com.[your username].busydaystart.plist
sudo touch com.[your username].busydayend.plist
sudo vi com.[your username].busydaystart.plist

Paste the following into busydaystart.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.[your username].busydaystart</string>

  <key>ProgramArguments</key>
  <array>
    <string>/path/to/your/busyday-start.sh</string>
  </array>

  <key>StartCalendarInterval</key>
  <dict>
  <key>Hour</key>
  <integer>11</integer>
  <key>Minute</key>
  <integer>0</integer>
  </dict>

  <key>StandardErrorPath</key>
  <string>/tmp/BD1.err</string>

  <key>StandardOutPath</key>
  <string>/tmp/BD1.out</string>
</dict>
</plist>

First thing to note: make sure your label reflects the particulars of your system, and make sure the path to your busyday-start script is correct.

The StartCalendarInterval key is where you schedule the script to be run. Time is 24-hour style (1pm is “13”) and synced to the computers local time. I have my social media blocking begin at 11am.

Save and exit vi, then sudo open the busystay-end.plist file in vi. Into it, paste the following:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.[your username].busydayend</string>

  <key>ProgramArguments</key>
  <array>
    <string>/path/to/your/busyday-end.sh</string>
  </array>

  <key>StartCalendarInterval</key>
  <dict>
  <key>Hour</key>
  <integer>20</integer>
  <key>Minute</key>
  <integer>0</integer>
  </dict>

  <key>StandardErrorPath</key>
  <string>/tmp/BD1.err</string>

  <key>StandardOutPath</key>
  <string>/tmp/BD1.out</string>
</dict>
</plist>

Same things to note here: make sure your label is correct, and also that the path to your end of the day script is correct. Schedule the same way. I have everything unblock at 8pm. If you want to do more fancy stuff, involving months or days of the week, you can find a handy little guide here. Save and exit vi. All thats left to do now is tell the system about these two daemons you’ve just made! That’s pretty simple. Into terminal, just type

sudo launchctl load com.[your username].busydaystart.plist
sudo launchctl load com.[your username].busydayend.plist

and you’re done! I would recommend experimenting a little – set the times to something relatively soon so you can see it in action and make sure it works, and once you’ve verified set it back to whatever you want to happen daily.

If you want to create a little window of social media access – mine is from 2pm to 3pm – its as easy as making some copies of your begin and end of day plists

sudo cp com.[your username].busydaystart.plist com.[your username].lunchbreakstart.plist
sudo cp com.[your username].busydayend.plist com.[your username].lunchbreakend.plist

and then opening them up in vi to change the times. After you’ve done that, load them into launchctl the exact same way. For simplicity: I am imagining that you are doing this work OUTSIDE the time you’d like social media blocked – but if you’re not, make sure you run the appropriate script so that your system is in the correct configuration. Voilá!

A few more notes:

Whatever programs you’re using that access internet services (chrome, tweetbot, etc) may need to be closed for the blocking takes effect. I know this is not ideal. Sorry. FWIW, I use these blocks in order to defeat a nearly reflexive habit I have of opening tweetbot, tumblr, whatever, before even realizing I’ve done it and then losing 5-10 minutes or more.

I’ve also heard that if you use FireFox, you may need to restart your machine. Also not ideal. For Chrome, which is what I use, I find I usually just have to quit and restart my browser.

Tags: , , , ,

speak up: