Skip to content

Commit

Permalink
VNC: Implement raw encoding handling for builtin screenshot support
Browse files Browse the repository at this point in the history
This patch implements the builtin screenshot support even for the
case the gvnc-tools package (that contains gvnccapture utility) is
not installed.

Signed-off-by: Michal Novotny <minovotn@redhat.com>
  • Loading branch information
Michal Novotny committed Dec 12, 2011
1 parent b9fdcb1 commit e59b691
Show file tree
Hide file tree
Showing 4 changed files with 331 additions and 55 deletions.
86 changes: 59 additions & 27 deletions src/libvirt-php.c
Expand Up @@ -456,6 +456,27 @@ char *get_feature_binary(char *name)
return NULL;
}

/*
Private function name: has_builtin
Since version: 0.4.5
Description: Function to get the information whether feature could be used as a built-in feature or not
Arguments: @name [string]: name of the feature to check against
Returns: 1 if feature has a builtin fallback to be used or 0 otherwise
*/
int has_builtin(char *name)
{
int i, max;

max = (ARRAY_CARDINALITY(features) < ARRAY_CARDINALITY(features_binaries)) ?
ARRAY_CARDINALITY(features) : ARRAY_CARDINALITY(features_binaries);

for (i = 0; i < max; i++)
if ((features[i] != NULL) && (strcmp(features[i], name) == 0))
return 1;

return 0;
}

/* Information function for phpinfo() */
PHP_MINFO_FUNCTION(libvirt)
{
Expand Down Expand Up @@ -2826,14 +2847,13 @@ PHP_FUNCTION(libvirt_domain_get_screenshot)
int scancode = 10;
char *path;
char name[1024] = { 0 };
int use_builtin = 0;

path = get_feature_binary("screenshot");
DPRINTF("%s: get_feature_binary('screenshot') returned %s\n", PHPFUNC, path);

if (access(path, X_OK) != 0) {
set_error("Cannot find gvnccapture binary");
RETURN_FALSE;
}
if ((path != NULL) && (access(path, X_OK) != 0))
use_builtin = 1;

GET_DOMAIN_FROM_ARGS("rs|l",&zdomain, &hostname, &hostname_len, &scancode);

Expand All @@ -2851,8 +2871,6 @@ PHP_FUNCTION(libvirt_domain_get_screenshot)

vnc_refresh_screen(hostname, tmp, scancode);

port = atoi(tmp)-5900;

if (mkstemp(file) == 0)
RETURN_FALSE;

Expand All @@ -2861,30 +2879,44 @@ PHP_FUNCTION(libvirt_domain_get_screenshot)
if (strcmp(name, hostname) == 0)
hostname = strdup("localhost");

DPRINTF("%s: Getting screenshot of %s:%d to temporary file %s\n", PHPFUNC, hostname, port, file);
if (use_builtin == 1) {
DPRINTF("%s: Binary not found, using builtin approach to %s:%s, tmp file = %s\n", PHPFUNC, hostname, tmp, file);

childpid = fork();
if (childpid == -1)
RETURN_FALSE;

if (childpid == 0) {
char tmpp[64] = { 0 };

snprintf(tmpp, sizeof(tmpp), "%s:%d", hostname, port);
retval = execlp(path, basename(path), tmpp, file, NULL);
_exit( retval );
if (vnc_get_bitmap(hostname, tmp, file) != 0) {
set_error("Cannot use builtin approach to get VNC window contents");
RETURN_FALSE;
}

// TODO: FIX!
}
else {
do {
w = waitpid(childpid, &retval, 0);
if (w == -1)
RETURN_FALSE;
} while (!WIFEXITED(retval) && !WIFSIGNALED(retval));
}
port = atoi(tmp)-5900;

DPRINTF("%s: Getting screenshot of %s:%d to temporary file %s\n", PHPFUNC, hostname, port, file);

childpid = fork();
if (childpid == -1)
RETURN_FALSE;

if (childpid == 0) {
char tmpp[64] = { 0 };

if (WEXITSTATUS(retval) != 0) {
set_error("Cannot spawn utility to get screenshot");
RETURN_FALSE;
snprintf(tmpp, sizeof(tmpp), "%s:%d", hostname, port);
retval = execlp(path, basename(path), tmpp, file, NULL);
_exit( retval );
}
else {
do {
w = waitpid(childpid, &retval, 0);
if (w == -1)
RETURN_FALSE;
} while (!WIFEXITED(retval) && !WIFSIGNALED(retval));
}

if (WEXITSTATUS(retval) != 0) {
set_error("Cannot spawn utility to get screenshot");
RETURN_FALSE;
}
}

fd = open(file, O_RDONLY);
Expand Down Expand Up @@ -7261,7 +7293,7 @@ PHP_FUNCTION(libvirt_has_feature)
}

binary = get_feature_binary(name);
ret = (binary != NULL);
ret = ((binary != NULL) || (has_builtin(name)));
free(binary);

if (ret)
Expand Down
38 changes: 34 additions & 4 deletions src/libvirt-php.h
Expand Up @@ -83,13 +83,24 @@ typedef uint64_t arch_uint;
#define UINTx PRIx64
#endif

int connect_socket(char *server, char *port, int keepalive, int nodelay, int allow_server_override);
int socket_has_data(int sfd, long maxtime, int ignoremsg);
void socket_read(int sfd, long length);
int connect_socket(char *server, char *port, int keepalive, int nodelay, int allow_server_override);
int socket_has_data(int sfd, long maxtime, int ignoremsg);
void socket_read(int sfd, long length);
int socket_read_and_save(int sfd, char *fn, long length);
int vnc_get_bitmap(char *server, char *port, char *fn);

int _is_bigendian;

#define SWAP2_BY_ENDIAN(le, v1, v2) (((le && _is_bigendian) || (!le && !_is_bigendian)) ? ((v2 << 8) + v1) : ((v1 << 8) + v2))
#define PUT2_BYTE_ENDIAN(le, val, v1, v2) { if ((le && _is_bigendian) || (!le && !_is_bigendian)) { v2 = val >> 8; v1 = val % 256; } else { v1 = val >> 8; v2 = val % 256; } }
#define SWAP2_BYTES_ENDIAN(le, a, b) { if ((le && _is_bigendian) || (!le && !_is_bigendian)) { uint8_t _tmpval; _tmpval = a; a = b; b = _tmpval; } }

#define UINT32STR(var, val) \
var[0] = (val >> 24) & 0xff; \
var[1] = (val >> 16) & 0xff; \
var[2] = (val >> 8) & 0xff; \
var[3] = (val ) & 0xff;

#define GETUINT32(var) (uint32_t)(((uint32_t)var[0] << 24) + ((uint32_t)var[1] << 16) + ((uint32_t)var[2] << 8) + ((uint32_t)var[3]))

typedef struct _resource_info {
int type;
Expand Down Expand Up @@ -158,6 +169,25 @@ typedef struct tVMNetwork {
char *model;
} tVMNetwork;

typedef struct tBMPFile {
uint32_t filesz;
uint16_t creator1;
uint16_t creator2;
uint32_t bmp_offset;

uint32_t header_sz;
int32_t height;
int32_t width;
uint16_t nplanes;
uint16_t bitspp;
uint32_t compress_type;
uint32_t bmp_bytesz;
int32_t hres;
int32_t vres;
uint32_t ncolors;
uint32_t nimpcolors;
} tBMPFile;

/* Libvirt-php types */
typedef struct _php_libvirt_connection {
virConnectPtr conn;
Expand Down
72 changes: 69 additions & 3 deletions src/sockets.c
Expand Up @@ -117,16 +117,22 @@ int socket_has_data(int sfd, long maxtime, int ignoremsg)
struct timeval timeout;
int rc;

timeout.tv_sec = maxtime / 1000000;
timeout.tv_usec = (maxtime % 1000000);
if (maxtime > 0) {
timeout.tv_sec = maxtime / 1000000;
timeout.tv_usec = (maxtime % 1000000);
}

if (!ignoremsg)
DPRINTF("%s: Checking data on socket %d, timeout = { %ld, %ld }\n", PHPFUNC, sfd,
(long)timeout.tv_sec, (long)timeout.tv_usec);

FD_ZERO(&fds);
FD_SET(sfd, &fds);
rc = select( sizeof(fds), &fds, NULL, NULL, &timeout);
if (maxtime > 0)
rc = select( sizeof(fds), &fds, NULL, NULL, &timeout);
else
rc = select( sizeof(fds), &fds, NULL, NULL, NULL);

if (rc==-1) {
DPRINTF("%s: Select with error %d (%s)\n", PHPFUNC, errno, strerror(-errno));
return -errno;
Expand Down Expand Up @@ -179,3 +185,63 @@ void socket_read(int sfd, long length)
DPRINTF("%s: All bytes read\n", PHPFUNC);
}

/*
Private function name: socket_read_and_save
Since version: 0.4.5
Description: Function to read the data from socket and save them into a file identified by fn
Arguments: @sfd [int]: socket descriptor for existing VNC client socket
@fn [string]: filename to save data to
@length [bool]: length of the data to be read or -1 for all the data
Returns: 0 on success, -errno otherwise
*/
int socket_read_and_save(int sfd, char *fn, long length)
{
int fd, i;
long len = 0;
long orig_len = length;
unsigned char bigbuf[1048576];

if (fn == NULL)
return -ENOENT;

orig_len = length;
fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1)
return -EPERM;

if (socket_has_data(sfd, 50000, 0) != 1) {
DPRINTF("%s: No data appears to be available\n", PHPFUNC);
return -ENOENT;
}

DPRINTF("%s: Reading %ld bytes\n", PHPFUNC, length);
while (length > 0) {
len = read(sfd, bigbuf, sizeof(bigbuf));

for (i = 0; i < len; i += 4)
SWAP2_BYTES_ENDIAN(1, bigbuf[i+1], bigbuf[i+2]);

write(fd, bigbuf, len);

length -= len;
if (length < 0)
length = 0;
}

if (length) {
len = read(sfd, bigbuf, length);

for (i = 0; i < len; i += 4)
SWAP2_BYTES_ENDIAN(1, bigbuf[i+1], bigbuf[i+2]);

write(fd, bigbuf, len);
}

ftruncate(fd, orig_len);

close(fd);

DPRINTF("%s: All bytes read\n", PHPFUNC);
return 0;
}

0 comments on commit e59b691

Please sign in to comment.