HostBlocker: Current functionality and Daemonization

The HostBlocker Utility was a project I started a long time ago with a certain functionality in mind. Being a software engineer, I spend a lot of time on the internet, and doing so I end up wasting a lot of time on sites that are not that productive. I wanted to build a simple utility that I could use to blackhole sites with a quick CLI command. In addition, this utility was to be written in C.

Back then I started it with the idea that I would get better at C for my upcoming job at Kickback Rewards Systems. Even though it could be trivially written in a dynamic language such as Python, C was on my horizon so I felt I needed to try.

Currently, the project is a very simple command line tool used to blackhole sites by opening and editing the /etc/hosts file as root. It supports the showing, editing, and adding of hosts in the /etc/hosts file. While this was initially enough to suit my needs, I felt like the project could be further improved with a certain level of automation. I decided that the project would need to run as a daemon, that would periodically check a configuration file for when certain hosts should be blocked and edit/update the hosts file accordingly.

The Plan

I will have the process start manually still, versus using something like SystemD to kick it off. To do this, I will utilize forking. The parent process will fork a child which will run in a loop, while the parent kills itself. The child will sleep at some configurable interval, and upon waking up check the config file for changes, edit the hosts file, then go back to sleep. This forking, is super simple and looks like below.

void daemonize() {
    pid_t mypid = fork();
    if (mypid < 0) {
        fprintf(stderr, "Error: %s\n", strerror(errno));
        exit(1);
    }

    if (mypid != 0) {
        // I am the parent, kill myself
        fprintf(stderr, "Parent exiting");
        exit(0);
    }
    else {
        // I am the child - DO THE THING.
        while(1) {
            // read config
            read_config_file();
            break;
            // make adjustments

            // profit??
            // JK, sleep.
        }
    }
}

Data Structures

To hold information from the config file in memory, I will use a basic linked list where each node represents a host, and its associated time windows of when to block and when to unblock. The linked list implementation is simple in C and allows me to be able to handle a dynamic amount of hosts in the file.

Lessons (Re)learned So Far

C is a pass by value type language. So when I wrote a routine that added to the linked list using a pointer to the head of the list, things were not working as intended. I did some research, and used my debugger, to figure out that the head pointer was being copied into the argument literally. So for instance, in the case that the list was null, the head pointer was null. I passed the head pointer by value to the linked list add routine, which detected that the head was null and created a new node, but by setting the head I passed in as the new node nothing was happening. The new node only existed locally, never outside of the scope of the function. When I switched to using a doubly indirect pointer for any function that had to modify the linked list, like below, things returned to working as intended.

/**
 * Add a char * to our linked list and set up a new head.
 * @param head The first node in the list.
 * @param data Our data to add to the list.
 */
void linkedlist_add(LinkedList **head, char *data) {
    // Create a pointer to the head element.
    LinkedList *tmp = *head;

    if (tmp == NULL) {
        *head = _linkedlist_new_with_data(data);
    }
    else {
        // Iterate until we don't have a next node.
        while (tmp->next != NULL)
            tmp = tmp->next;

        tmp->next = _linkedlist_new_with_data(data);
        tmp->next->next = NULL;
    }
}

 Share!

 
I run WindleWare! Feel free to reach out!

Subscribe for Exclusive Updates

* indicates required