Venturing back into C
For the past two weeks or so I have been diving back into C programming. I've found it to be a very fun and refreshing experience coming off of a slog of Java 11 updates at work. I've found comfort in its simplicity and frustrations in my "I can do this without an IDE" mindset.
I started C programming in College during a 8 AM course of which all I
can remember is that it was at 8 AM. I loved programming in C, dealing
with memory, pointers, no strings, structs, no strings, linking, no
strings. It was a really interesting difference from the web and Java
programming I had done previously. Obviously the lack of the
"string" type made things interesting and initially a challenge
for me back then. In my most recent endevour I found char *
to be perfectly suitable for every case I came
across. It was usually a separate library that was failing me, not a
fixed char array. This was mostly due to the types of programs I was
writting in college were text adventures where all of what I did was
using strings. And my lack of understanding of what was actually
happening in C was really what was causing all the issues.
The Project
I started working on an application I had been meaning to develop called reminder.d. This daemon would monitor for reminder notifications I would send via a CLI. It queue them up based on some time set to send the notification. I ended up writing both the CLI and the daemon in this past week, both in C.
The Beginning
This project started with an outline (as a README) which I think was the reason this ended up as an actually successful project. I had been thinking about this for a long time, and had begun using a calendar to keep track of long term reminders/dates etc. First, I outlined the architecture "how would I actually do want to send myself remidners". Since half my day is spent infront of a computer, with a terminal open or at least two keystrokes away, a CLI would do the trick. Then how do I actually send myself notifications... writing them down. So I can use the CLI to write to a file and have a daemon pick up the changes and notify me once it hits the desired time posted.
The CLI
The CLI remindme took in messages and appened them to a file. This file would be monitored by the daemon later on. Each reminder consisted of three parts:
- Message - The body of the notification.
- Time - This is either a datetime or a period for when the notification should send.
- Flag - The Flag was set by the CLI when written to the file, this marks the status of the reminder
The Daemon
The Daemon reminder-daemon opened and tailed a file at
/usr/local/etc/reminder.d/$USER.list. It would tail the file
monitoring any incoming lines parsing them into reminders. The syntax of
the reminder is FLAG EPOCHSEC MESSAGE
.
Tokenizing on spaces it was then added to a linked-list sorted by time.
Every second it checks the file for any new lines, adding reminders as
they come in, then check the head of the list. If the reminder at the
head is ready to be notified the daemon pops it off the list and sends
the notification. After a notification is sent successfully the daemon
modifies that line in file updating its FLAG
to 'd'. This is so when the daemon starts back up it skips the
reminder. Notifications are sent via libnotify: Reminder - $DATETIME
with the message body. They are also
set to last until dismissed manually, this way if were to walk away, once
I sat down I'd see the stale reminder waiting.
Future Plans for Reminder.d
Having a system to create and send myself notifications is incredibly useful but having them limit to just the computer I sent them on makes them a very limited. I have been using them at work for the last few days and its nice to be able to tell myself to remeber to email a person after lunch. But I would like to be able to tell myself things later in the day. I have planned since the beginning to have a remote server I can sync the reminders through. In addition having an application running on my phone that also gets and sets reminders.
Remote syncing would change entirely how I deal with reminders in the file.
struct remnode {
long fileptr;
struct reminder* reminder;
struct remnode* next;
};
Is currently the struct I use to keep track of the reminders.
fileptr
is the line of the file where the
reminder is, so I can fseek
back to the
location and overwrite its flag. I cannot currently think of a way to
keep the files perfectly identical without introducing countless
edgecases. What I do think might work is providing some form of UUID.
When a remote pull tells the systems daemon that a notification has been
cleared it can mark it by ID. Right now the fileptr is effectively its
ID, but that will not work anymore. A composite key of the daemons own id
(generated at install?) with a new ID of each incoming message would help
ensure uniqueness across ID generations across multiple systems.
What I've learned
First off, I probably could've done this in bash. With
date notify-send git awk cron
and a few other
useful commands I could very easily keep track of file changes and push
notifications at a certain time. But seeing as I scrap together bash
scripts all the time I though C would make things more fun.
Writing manpages was the probably the most fun I had working on the project. They have a simple elegance to them, similar to C. That being said you could FEEL the age of the language. Every single decision is there to make things simple to parse. Even compared to modern markup the explicit direct nature of the language made it so easy to learn. Every tag served a specific purpose and each objective I had had a flag to do it.
.TH REMINDME 1
.SH NAME
remindme \- Send yourself reminders at a specific time on one or more devices
.SH SYNOPSIS
.B remindme
[\fB\-t\fR \fITIME\fR]
[\fB\-\-at \fITIME\fR]
[\fB\-i\fR \fIPERIOD\fR]
[\fB\-\-in\fR \fIPERIOD\fR]
Libnotify was insanely easy to work with, from a programming perspective.
NotifyNotification *notif = notify_notification_new(title, rem->message, "info");
notify_notification_set_app_name(notif, APP_NAME);
notify_notification_set_timeout(notif, NOTIFY_EXPIRES_NEVER);
GError* error = NULL;
gboolean shown = notify_notification_show(notif, &error);
In closing
Overall, this was an extremely fun first week of engineering. I look forward to what I am able to do syncing and sending notifications on android.
For the zero people reading, grab a beer and outline your project. Full through. Think about the how, then write it down. Don't worry about getting in the weeds of how to write a manfile, thats what is fun about programming. I thought I botched my debian/sid environment uninstalling and reinstalling a notification daemon. Infact I think its caused me to take a stance on the whole systemd thing. Either way, start a private repo (they're free now) write a README and a LICENSE file and iterate on the README until you realize "oh shit this is something I can do". Then do it. This project still needs some work, but for an MVP, its actually done. And now I can dive in the deep end of trying to actually make it easy to setup on a fresh PC. Or dive into modern android development and server syncing...