Hi,
I am trying to develop PHP binding for Libvirt API. As discussed
previously on the list the best way is to create a ZEND extension. There
is a great tutorial on the net (
http://devzone.zend.com/tag/Extension )
and PHP provides a lot of macros and functions to make extension writing
easier.
I've implemented functions for connecting, listing domains, getting info
about domains and node, dumping domain XML, some basic domain life-cycle
actions and very basic error reporting. Before moving to other functions
I need to solve a few issues :-)
The biggest issue is that PHP used from webserver can (and usually does)
run multithread. One thread can process more than one request and the
threads can run in parallel. PHP/ZEND provides "tools" to solve this. It
provides "request-global" variables, resources (containers for objects
like file handles or connection pointers) and much more. In single
threaded environment (like calling from command line) all these fallback
to usual behavior. In addition ZEND has its own memory manager to be
able to better manage the memory on per request basis (functions are
called emalloc(), efree(),...). One can still use malloc() and free()
but this is bypassing the ZEND memory manager. Free()ing emalloced
memory or vice versa leads to crash.
So one thing is that every memory allocated by libvirt (using malloc())
I need to copy to memory allocated using emalloc(). Also every memory,
that would be free()ed by libvirt I need to malloc() and copy from
emmaloc()ed memory. The first one is done by simple macro:
#define RECREATE_STRING_WITH_E(str_out, str_in) \
str_out = estrndup(str_in, strlen(str_in)); \
free(str_in);
The second case I encountered only on authentication callback and I use
simple strndup() to copy it for libvirt.
When running from command line everything seems to work fine. This is of
course singlethreaded and potential resource leaks need not to cause a
problem. However when running from webserver, the result is much more
interesting. When connecting to qemu:///system (via local socket) using
<?
libvirt_connect($uri,true);
?>
I can crash the libvirt daemon after cca 10 page reloads - sometimes
with message
Apr 13 15:32:44 kvmtest kernel: libvirtd[8263]: segfault at 4 ip
00000039d7223fc0 sp 00007fa6fbc29a88 error 6 in
libdbus-1.so.3.4.0[39d7200000+3c000]
in system log, sometimes without any message.
(When running the same script from command line it worked for 1000
executions without noticing a problem).
When connecting to qemu+tcp:///system using credentials (explained later):
<?
libvirt_connect($uri,true,Array(VIR_CRED_AUTHNAME=>"fred",VIR_CRED_PASSPHRASE=>"fred"));
?>
It works but httpd processes open a lot pipes and after a few hours with
page refreshing every 10 sec I even ran into error:
[Mon Apr 13 02:40:26 2009] [error] [client 10.38.25.152] PHP Warning:
libvirt_connect() unable to make pipe: Too many open files
Next issue I am not sure about is callbacks. I need them mainly for
authentication. As PHP is intended to run the whole script
non-interactively at most times, I've created this solution. When
calling the libvirt_connect() PHP function you can provide list of
credentials in form of an array
Array(VIR_CRED_AUTHNAME=>"fred",VIR_CRED_PASSPHRASE=>"fred").
This PHP
array is parsed to C array and this prepopulated array is passed to my
authentication callback function. That function receives the requested
credential, looks it up in the provided array and returns it. I think
this suits more the PHP nature but in future I may provide real callback
solution.
Little trouble is that libvirt free()s the values returned by callback
so I need to copy them to malloc()ed memory.
I am not sure about multithread safety of callback functions but I think
that if the function only obtains the parameters via cbdata and operates
only with them then it should be safe. However sometimes I get error:
[Mon Apr 13 14:19:48 2009] [error] [client 10.38.25.152] PHP Warning:
libvirt_connect(): Failed to collect auth credentials
and I need to restart http daemon for a few times to make it work again.
The last trouble is with error callback, where I am not sure whether it
is thread safe. I need to call PHP function for reporting error, storing
the error in "global" variable for next use and the PHP may even
terminate the whole processing of the request...
And if you are still reading then I can point you to
http://phplibvirt.cybersales.cz/ where you can download the source code
and browse the documentation - you can find there list of implemented
functions and brief instructions how to install the extension. But be
warned it really can crash your libvirt and maybe apache! For
completeness: I use Fedora 10 with some rawhide updates:
httpd-2.2.11-6.x86_64
php-5.2.9-1.fc11.x86_64
php-devel-5.2.9-1.fc11.x86_64
libvirt-0.6.0-2.fc11.x86_64
qemu-0.9.1-12.fc11.x86_64
libvirt-devel-0.6.0-2.fc11.x86_64
php-cli-5.2.9-1.fc11.x86_64
kvm-83-5.fc10.x86_64
php-common-5.2.9-1.fc11.x86_64
Radek