About PM...
Copyright © 2001-2004, arm0nia project
- Version
- 0.1, sometime in 2002
- Authors
- Nicholas Christopoulos
What is PM
The PM is a simple manager for shared-libraries. An application ask for a module, and PM loads it for it.
A module is a shared library. A set of routines and nothing more.
PM controls their modules by a key. There is one unique key for each module.
When an application wants to load a module, it calls the pm_getmid() and pass
the key. The PM loads the module and returns a structure with pointers to the
module's routines. Now, the application can execute the routines.
That is simple and easy but that is not all.
- PM can load their modules localy or throught the tcp/ip network (or unix-domain).
To do that, it uses a transparent (to application and to the modules) communication
way to talk with the daemon. The daemon can load the module instead of the application
and can return a set of fake-function pointers to the application. So, the application
does not know what happens and still it is working with the functions pointer that
was returned by pm_getmid(). But that fake function-pointers are belongs to PML and not
to the module itself. The PML now, ask from the daemon to run the functions (throught sockets)
and sends the results back to the application.
- PM require from their modules to have as specified set of functions.
It has categirized their sets to:
- VFS (virtual file system)
- GFX (graphics)
- SND (sound)
- PTD (pointing devices)
- KBD (keyboard)
- IDP (idepented)
- WND (windows (controls))
- DOC (document windows)
That gives the oppunity to an application to use the same set of routines for many kind of
jobs. For example, an application can use the same code to use the serial-port or a
telnet session (by just passing a different key on pm_getmid()).
That has the great effect to provides a common API to all of its apllications for various jobs.
For example, we use the VFS API to provide info about pm-modules, system's users, etc.
More about that on the next section.
- But the basic job of PM is keep up the system stability and the "speed".
PM it keeps a list with the loads of every pid.
Also, it is designed to keep info about the use of the modules (cache manager).
Results
All of us we have worked with graphics libraries (like SDL, SVGALIB, etc), with keyboard and
the 'hell' of termcap, different OSes, etc.
The PM promises to provide one API (per category) for all that. So, if you write a code for the SDL
it will works on SVGALIB too (by changing the module-key). Keyboard codes will the same on all
environments, terminals, etc. The same things for the rest like pointing-devices (mouse), sound, etc.
There is also a full described data style. The graphics-related modules must use/return always
data similar (almost the same) to PC's video-ram. The sound-related modules must use/return always
raw-data similar to /dev/dsp. There are libs on PM to help the module-developers to do that
work. So, all the graphics modules and all the vfs modules (which are used to encode/decode
graphics files) are returns or using a virtual video-ram structure that is compatible with the
PC's hardware's one (in almost all cases).
Additional, since, it can load modules as root or as a common user, throught the tcp/ip or locally
(daemon) or into applications space, its flexibility is beyond of our imagination. Of course, that
has its cost, the security. So, will let that to others (system, modules, window-manager, etc).
You maybe think that all these things will reduce the code's speed.
I can say that, if the module will be loaded in application's space (that is common), there
is no loss of speed because it is almost the same as to link the module as shared library!.
That can be easy understood if you think that the PM, in that case, returns a structure with
function-pointers which are points directly to module's functions. Transformation of data normally
will cost nothing to speed because its format is based on PC's hardware.
For example, the screen-data of a virtual-video-ram structure can be placed in the video-ram
directly by using a simple memcpy()!
What are the modules
As we said, the modules are simple shared-libraries.
There are three main differences between common shared-libraries and PM's modules
More about modules
As we said, modules are categorized by theirs set of routines.
The routines of that must supported by the modules ...
TODO....
* (routines typoi)
* required routines
* optional routines
* extentions
* and the bastards, optionals that can replaced by PM if their are missing from the module
* (standard routines)
* (APIs)
PM applications
(TODO: repeat how apps works)
(TODO: what is the returned pointer, how-to call module's routines)
A VFS application example
The following example is a typical vfs application. It does nothing more than
load a module and display one of its files.
The 'module-key' and the 'file' are the command-line parameters.
// vfstest.c
#include "../pm_app.h"
int main(int argc, char *argv[])
{
int handle;
pm_vfs_module *m;
pm_init(); // initialize PML
if ( argc != 3 ) // wrong args
panic("usage: vfstest module-key open-file");
// load the module
if ( (m = pm_vfs_load(argv[1])) == NULL )
panic("pm_vfs_load(\"%s\"): failed", argv[1]);
// open the file
if ( (handle = m->open(m->mid, argv[2], 0)) == -1 )
panic("m->open(m->mid,\"%s\"): failed", argv[2]);
// display theirs contents
while ( m->gets(m->mid, handle, buf, 256) )
printf("%s", buf);
m->close(m->mid, handle);
pm_vfs_release(m); // close the module
return 0;
}
The power of PM can be easily showed here. You'll need to use the 'root' user
to run these examples. Actually, that is no required (or it will no rq)
but for now it is good.
Example 1: Use vfstest to get the time from a local or internet server
# vfstest vfs/telnet localhost:13
if the vfstest fail, that means there is no configured the timegen service in
your machine. Edit /etc/inetd.conf and remove the # from timegen lines.
restart your inetd (on SuSE: rcinetd restart) and try again.
Example 2: Use vfstest to connect to other device (like my Palm) through serial port,
for a chat :)
# vfstest vfs/serial /dev/ttyS1:57600
In my Palm, I run the SB with the sertest.bas and ... voila... I do a chat
through the cardle.
Example 3: Use vfstest to get info about a username
# vfstest vfs/users users/root
A few information about the user 'root' will be displayed in your console.
A VFS module example
The following code demonstrates how to use the PM to build a VFS module that shows every username as a file.
Uid, gid, real-name, home-directory and the shell are part of the file's contents.
Note that is quite different than default PM's users module because the default-module uses directories to help
the applications to find the users by the username or the uid.
#include "../pm_km.h"
#include "../vfslib.h"
static vfs_tree_t *tree; // our vfs-tree
static int userfs_init = 0; // thread protection
// the PM has just now, load the module
int pm_module_init(mid_t mid)
{
userfs_init ++;
if ( userfs_init == 1 ) {
FILE *fp;
tree = vfs_build(1,1,0); // build vfs
if ( (fp = fopen("/etc/passwd", "rt")) != NULL ) {
char buf[1024];
// for each line of /etc/passwd
while ( fgets(buf, 1024, fp) ) {
if ( strlen(buf) > 4 && buf[0] != '#' ) {
char fdata[4096]; // our file's contents
strlist_t *list;
// building our virtual-file
list = slsplit(buf, ":", NULL, 0);
sprintf(fdata, "%s\n%s\n%s\n%s\n%s\n%s\n",
list->str[0], list->str[2], list->str[3],
list->str[4], list->str[5], list->str[6] );
// insert file to vfs-tree
vfs_build_file(tree, list->str[0], fdata, strlen(fdata));
// clean-up
strlist_destroy(list);
}
}
fclose(fp);
return 1; // everything ok
}
}
return 0; // error
}
// PM will close the module
void pm_module_close(mid_t mid)
{
userfs_init --;
if ( userfs_init == 0 )
vfs_destroy(tree); // free vfs-tree
}
// An application wants info about our vfs
void pm_vfs_sysinfo(mid_t mid, pm_vfs_sysinfo_t *info)
{
memset(info, 0, sizeof(pm_vfs_sysinfo_t));
info->version = PM_VFS_SYSINFO_T_VER;
info->readonly = 1; // this is a read-only file-system
info->dirsup = 1; // directory-routines are supported
}
// PM wants to known what routines are supported by the module
void pm_module_getapi(mid_t mid, pm_module_api_t *api)
{
api->version = PM_MODULE_API_T_VER; // version of this structure
api->api_type = pm_api_vfs; // what kind of interface is supported
api->api_version = 1; // what interface-version is supported by the module
}
// redirect everything else to vfslib
int pm_vfs_open(mid_t mid, const char *filename, int flags)
{ return vfs_open(tree, filename, flags); }
...
That's all folks :)