Posted on | October 1, 2008 | 2 Comments
Sometimes we’re fortunate enough to really enjoy what we’re being paid to do. That’s been the case for me this week while working on a replacement for the ‘lvm’ command for control panels and other underprivileged things to use.
Most people who write programs that must operate on logical volumes do it in a very ugly way, via the system() or execv() family of calls. Sure it works, but its clunky, forky and (if your not careful) downright dangerous. Surprisingly, most people don’t realize that a C library (liblvm2cmd) exists to make this process safer and more elegant. While the library is very basic, it does provide a simple reusable interface to lvm2 and a semi-intelligent callback.
The library is more or less just a compilation of everything in lvm2 tools/ sources, with a header exposing enough functions to register a callback and reach the various command entry points with some arguments. This is cool, because whatever you change in lvm2 itself (i.e. output print format) is automatically inherited by the library and can be statically linked. This means being able to get done what you need to get done without touching the default system lvm.
The result is neat, you can write your own ‘lvm’ exposing only the parts of lvm that you absolutely must. For instance, making ‘lvm’ setuid 0 just so some web control panel can talk to it is just insane.
Phase 1 of my plan is done, I now have a ‘safe to setuid’ lvm executable with quite a bit of logging and auditing support. It uses a deny/allow ACL list to see what uid’s are permitted to talk to it .. and what those users are allowed to do. Not done yet, I need to enhance the ACLs but the concept is working. Its actually quite easy to make your own CLI to your new lvm as well, write your own help displays .. all without touching lvm itself.
The next step is to make lvm2 a service….. establish a local listening socket and wait for connections. This depends on my emerging grant and IPC daemon to be complete, but the final result will look something like this (from the vantage point of a PHP control panel talking to it):
$node = gridnix_get_node();
$task = gridnix_register_app("WebCP", $cfg['key'], posix_getpid(), $node);
$perm = gridnix_request_grant($task, LVM);
$res = array();
gridnix_call(LVM, $task, $res, $perm, "create -L 10G -n foo vg1");
$err = gridnix_get_errno($task);
…. then later ….
The last line really isn’t needed .. as the controller will check on its clients to ensure they are alive. The name “WebCP” might have a ttl of 30 seconds or so, since its never expected to run any longer than that. Yeah, racy, working on it.
In this instance, we connected to the grid service, obtained permission to talk to LVM, called LVM and sent it a request, store the results in an array then immediately dropped our permission to talk to LVM.
Behind the scenes, the grid controller called the listening LVM service and said “Allow connections from pid xxxx use acl list WebCP”. At that point, our PHP process was able to establish a call and issue its instructions.
This is not nearly done, there is still alot to consider and complete .. however its the direction that I’m moving in. Also, this will extend so that PHP on node 4 can request to talk to LVM on node 5 .. the controller on node5 will simply proxy the requests.
In a perfect world, everyone used central storage with fiber and had all of their machines in a single data center with the luxury of a private interconnect.
In the real world, that’s just not the case .. and what I’m working at addressing. Again, the examples above are just meta examples .. just to illustrate the point.