This is fourth part of the series. You can find outline of the whole series here. We already have OMI server up, running and accessible from outside and we have test class implemented on it. Time to create something useful. We start with processes because they are objects very convenient for demo purposes. Normally we would create class that is based on existing one, e.g. CIM_UnixProcess. But before we do it right, we will try to create something that at least works and does what we need. Why? Well, if you look at CIM_UnixProcess schema you will find plenty of keys there, not to mention other properties that would require values at certain point. We will come back to this later, when we know what those values should be.
Bare minimum
What is the property of a process that we simply can not miss? There are a few, but for me – it has to be ID. We will start defining our class in a schema.mof with just this single property that will also be a key property for this class:
class Lin_Process
{
[Key] Uint32 PID;
};
To implement it we need to look for a source of information first. If we do, we will soon discover that all processes store their information in /proc. So the process with ID X will have information stored in /proc/X folder, try this:
ps -A | grep -v -E 'PID|ps|grep|awk' | awk '{print "/proc/"$1"/stat"}' | xargs ls
As you can see – everything is there. No we only need a c++ code that will do more or less the same. To stay in focus we will implement EnumerateInstances method only, we can add implementation of GetInstance later, once we know where (and how) we can find other properties of the process. We need to open /proc folder first and enumerate subfolders there. We also need to check if the folder name is all-numeric (we will use regular expressions for that):
DIR *pdir;
struct dirent *pent;
regex_t reg;
const char *regex = "^[0-9]+$";
regmatch_t matches[2];
regcomp(®, regex, REG_EXTENDED);
pdir = opendir("/proc");
if (!pdir)
{
context.Post(MI_RESULT_NOT_FOUND);
}
else
{
while ((pent = readdir(pdir)))
{
if (regexec(®, pent->d_name, 2, matches, 0) == 0)
{
// Folder is numeric, we can proceed...
}
}
closedir(pdir);
}
We also need to add a few additional headers to the original list, otherwise we won’t be able to compile our provider. The code that we will use to create instances of our class is very straightforward:
Process_Class proc;
proc.PID_value(atoi(pent->d_name));
flag = 1;
context.Post(proc);
Additional variable (that we will define as int with value 0) will be used later to decide what return code send to the user:
if (flag == 1)
{
context.Post(MI_RESULT_OK);
}
else
{
context.Post(MI_RESULT_NOT_FOUND);
}
Because we change it value to 1 for any folder that is processed – result will be “NOT FOUND” only if no process is found. We need to compile (make) our provider next, and than register it (make reg). Once this is done – we can use our CIM session to retrieve PIDs:
What’s your name?
Now that we have our key covered, it’s time to add something more to our class. In my original design next thing I did was reading content of /proc/PID/cmdline file to add next property (named the same as file: cmdline). First of all – I updated the schema:
class Lin_Process
{
[Key] Uint32 PID;
String cmdline;
};
That I tried to read /proc/PID/cmdline file. It took me some time to make it happen. Why? Well, if you look for solution to read file in c++ you may find something like that:
FILE* f = fopen(filename, "r");
// Determine file size
fseek(f, 0, SEEK_END);
size_t size = ftell(f);
char* where = new char[size];
rewind(f);
fread(where, sizeof(char), size, f);
Surprisingly (for me) it didn’t work at all here. I tried with another file in my home directory just to make sure it’s not typo in my code and it worked as expected. Eventually I decided that this has to be related to dynamic nature of /proc, and more searches confirmed that was the case. There are many ways to skin this cat, I decided to use the one that was easiest to me (but probably least efficient) and just stored data in buffer with static, predefined size. Also: EnumerateInstances has keysOnly argument that fits perfectly here: if users asks for keys, we won’t process anything else:
// define new variables
FILE *file;
char buffer[4096];
char Path[20];
int Length;
// Reading PID, setting proc.PID as previously...
if (keysOnly)
{
context.Post(proc);
}
else
{
(void)sprintf(Path,"/proc/%d/cmdline", proc.PID_value());
file = fopen(Path,"r");
Length = fread(buffer, 1, 4096, file);
if (Length > 0)
{
buffer[Length] = '';
proc.cmdline_value(buffer);
}
else
{
proc.cmdline_value("");
}
fclose(file);
context.Post(proc);
}
That was probably the hardest part: make this first step. Adding other properties was easier, because I already knew limitations of /proc. Anyways, after this first part I was able to do something like:
Enough for today. In the next part I will cover adding other properties and one, very spectacular method. Stay tuned!
Pingback: Managing Linux via OMI: Roadmap | IT Pro PowerShell experience