I am going to present how to make a cron-daemon like program in C++. But a difference is that it can run more frequently than what "cron" can do. For example, if you want to have a cron task that should run every seconds, you cannot do that, because the minimum time resolution of cron is "minute"; not "second".
I also tried "dynamic library" feature in Linux to load C++ binary file at runtime dynamically. This means the base program doesn't need to be recompiled with modified CPP files. Instead, it can load/unload a Position Independent Code, or PIC, at runtime. You can easily find more information from the manual page: "man dlopen".
I made five files for the base program:
I named it "cron_native" but I am not sure if it is good enough. "plugins" is a directory that contains plugins. And the "plugin" programs are Position Independent Code that can be loaded/unloaded dynamically; as I will cover later, they need special compiler options to be PIC.
The main logic of the program is in the file, "cron_native.cpp":
The function, "main()", calls "daemon_init()" to setup the process as a daemon. It looks for plugin files under the given or default directory. It executes "dl_init()" functions on each plugin and repeatedly executes "dl_main" function on each plugin. I put 1 second sleep on each loop to cool it down little; otherwise it may consume CPU too much. As a daemon process, it can reload the plugins when a signal, "SIGHUP", occurs. During the reloading step, it calls "dl_shutdown" function on each plugin to give a chance to properly shutdown the program.
Note that it is a good practice to have explicit "init/shutdown" calls on any code that can be dynamically loaded and shared. I am not going to explain the details of the aspect but you can easily find topics like "DLL hell" or something.
The Makefile looks like this:
Note that it includes "dl" library.
The precompile header file, "stdafx.h", looks like this:
The file, daemon.hpp, is to setup the process as a daemon process. The characteristics of daemon process is well described here.
One of the characteristics of daemon is that it assumes stdout is not available. All outputs go to syslog which then gets stored in /var/log/syslog. You can monitor the message output with a command like "tail -f /var/log/syslog".
The long program, "dynamic_library.hpp", looks like this:
The "Dynamic library" mainly consists of three functions: "dlopen", "dlclose" and "dlsym". As the name implies, "dlopen" opens a PIC program and increase the reference counter by one. "dlclose" reduces the reference count by one and when the reference counter hits zero, it will unload the program. In other words, if the reference counter is still above zero, it will not unload nor load a new PIC. "dlsym" searches the PIC with the name of function we want to use and returns a function pointer. You can find more explanation from Wiki pages: DynamicLoading and PIC.
For the function, dynamic_library_t::call(), I was thinking of using thread. But I couldn't find a good use of it. The latest RaspberryPi 2 has four cores so I would like to utilize threads as much as possible. But I don't think the added complexity can be justified in this case. Non-thread style is much more straight-forward and less error-prone.
I noticed that when one of PIC crashes or causes "Segment fault" error, the base program crashes as well, which makes the cron unstable as a daemon process. One of ways to prevent it is to "fork" a child process and isolate the problem in the process. But then it may affect the performance and the source code will suffer from complexity. Another way is to have another monitoring process or cron job that checks if the program is still running; if it doesn't it can simply restart a new one. I think when a crash happens, it should look obvious rather than make it automatically recover without noticing.
As a simple example of PIC, I have a "plugin.hello.cpp". In order to compile the plugin, I made three files:
On PIC side, the program must be compiled with a compiler option, "-fPIC". I am not sure on "-shared"; probably both are needed. And the functions that will be found by "dlsym" must have a keyword "extern "C"" at the beginning of the function.
The file, "plugin.hello.cpp", looks like this:
The program simply prints out syslog message, "Hello". You will see the message printed every second in the file, "/var/log/syslog". Note that it stops after 10 times; it is to show how to control the frequency or longevity of each plugin. As I mentioned earlier, three functions have keyword, "extern "C"". This is a common way to export the mangled function name.
The Makefile shows how the program is compiled.
Note the option, "-shared" and "-fPIC".
Not important but for completeness, I like to show the file, "stdafx.h", as well:
Enjoy the high resolution cron.