In today’s programming landscape, it is quite common to encounter programs that allow us to add a functionality via a system of plugins (also known as hooks or extensions). This is often done because the very nature of the program lends itself to extensibility, as it is the case with Emacs, or because authors can’t just foresee every potential use for their application, and therefore, let the users augment the functionality.
Pitfalls of Plugins
However, is this a sensible approach? While most people have naturalized this behavior, there are a few issues we should take into account before we dive into writing a plugin:
- In the vast majority of cases, these extensions run in the same address space as the application they augment. Indeed, it is typical of plugins to be compiled as a shared library, and then, to be loaded during runtime via ‘dlopen’ (or whatever equivalent exists in your platform). While this may seem to make sense, it is important to point out that any crash caused by the extensions will bring down the whole application. This has been a very serious problem in Web Browsers – any fatal error caused in one of the open tabs could make the browser crash. For instance, Google Chrome has finally decided to make tabs separate processes to work around this issue.
- If we execute plugins inside the context of another application, they frequently encounter strict limitations. For example, they typically have to be built in the same way as the parent application in order to make them binary-compatible. They are also generally forbidden from changing certain parameters, such as the signal mask and disposition, because they may alter the parent application’s behavior.
- There are additional security concerns. Suppose the application needs to be run by the super-user. This means that the plugins will also gain elevated privileges while running, allowing them to open low numbered ports and manipulate the whole filesystem. This opens up the possibility of serious damage by malicious extensions.
- Debugging becomes needlessly complicated as the application execution is intertwined with the plugin execution, making it much harder to spot where exactly the problems arise.
All this being said, the advantages of extensibility cannot be overstated: The simple fact that it allows us to reuse a tremendous amount of code, it makes development cycles much shorter, and by using a known-to-be-good application, it makes it unnecessary to reinvent the wheel.
A Practical Solution
So, the question is: How can we overcome the limitations of the basic plugin model? There really isn’t too much information on this subject, so when conjuring up a solution, we appeal to common sense and practicality. In this case, in order to avoid the pitfalls previously mentioned, we propose Remote Procedure Calls (RPC) as a mechanism to enjoy the advantages of plugins while mitigating the risks.
At first glance, RPC might seem like an overkill when we simply want to extend an application functionality, but as a hook complexity increases, it becomes a very attractive option. By controlling the application context, virtually every problem is solved: Any crashes won’t affect the parent application, and they can be easily debugged; we are free of any coding restrictions (even the source language), the server program can run without privileges, and so on.
Of course programming in RPC mode implies coming up with a serialization protocol and a suitable synchronization mechanism (for example, Inter-process synchronization which is arguably much harder to get right than its multithreaded counterpart is), but there exists a plethora of libraries available to prevent any possible risk. A clean separation and decoupling of the plugin functionality and the actual process that makes the effort more than make up the hassle associated with it. Choosing the right Inter-Process Communication (IPC) mechanism is a subject best left for another post.
Do not forget to post your comments in the section below. If you need additional information, do not hesitate to contact me at firstname.lastname@example.org.