必威体育Betway必威体育官网
当前位置:首页 > IT技术

vdagent与vdserver

时间:2019-09-26 17:12:14来源:IT技术作者:seo实验室小编阅读:83次「手机版」
 

ppmsg

        Spice VDAgentmessage 协议及使用

  I.概要

   Spice 包含三块:绑定在qemu/kvm上的Server(模块), 运行在(远程)主机上的Client(程序),和运行在Guest(虚拟机)上Agent(程序),(虚拟机运行在qemu­spice上)。Server将运行在qemu­spice上的虚拟机的数据通过channel(socket)传给client 显示输出,并和后者交互;agent 会辅助server/client在虚机上执行一些任务。

   Win32中,Spice Agent是服务程序,由vdservice.exe和vdagent.exe组成,前者负责与系统和VDI端口通信,后者负责具体任务。二者之间建立pipe通信。

II.VDAgentMessage结构和操作

   1.VDAgentMessage结构示例

   这三方关于agent任务的所有数据通信,都基于struct VDAgentMessage

   typedef struct SPICE_ATTR_PACKED VDAgentMessage { 

   uint32_t protocol;    //协议名长为32字节的unsigned int

   uint32_t type; //类别:剪贴板,鼠标事件...

   uint64_t opaque;     //掩码:消息发往何处

   uint32_t size; //大小=data的长度+某子类型长度

   uint8_t data[0];     //附加数据,实际是uint8_t*,所有msg本质上都存在uint8_t[]内。

   } VDAgentMessage; 

   VDAgentMessage存于长度可变的uint8_t[]内,可对.type和.data处理,生成各种子类型。

   具体为例,spice实现了agent所在的虚机和client所在的主机共享剪贴板,剪贴板消息的数据结构是

   struct VDAgentMessage clip_msg = 

new uint8_t[_out_msg_size=sizeof(VDAgentMessage)+sizeof(VDAgentClipboard)+clip_data_len] ={

   .protocol =   VD_AGENT_PROTOCOL;

   .type =    VD_AGENT_CLIPBOARD;

   .opaque=    ...方向

   .size=    sizeof(VDAgentClipboard)+clip_data_len;

   .data=    new uin8_t[.size];

};

此时clip_msg.data 即为(VDAgentClipboard*)clip_msg.data:

struct VDAgentClipboard//即把clip_msg.data[]内的数据进行细分当结构体看待:

{

   .uint32_t type, //剪贴数据类型

   .uint8_t* data = new uint8_t[clip_data_len],//这才是剪贴的数据clip_data

};

2.VDAgentMessage处理过程(以VDAgentClipboard 为例)

   1)从虚机复制单词“Massacre”,被agent捕获,同时向client发出请求锁定client所在主机的剪贴板

   Agent­­­(VDAgentMessage clip_grab_msg)­­­>Server­­­>Client

   2)此时若在在client主机内执行粘贴则被client 捕获,向agent 发出数据请求

   Client­­­(VDAgentMessage clip_data_request_msg)­­­>Server­­­>Agent

   3)受到请求后,agent将从虚机剪贴板上取出的“Massacre”加工成VDAgentMessage包装好发出

   Agent­­­(VDAgentMessage clip_data_msg)­­­>Server­­­>Client

   4)client 将其解包取出“Massacre”,加入主机的剪贴板内­­>“Massacre”贴到client所在的主机上。其中Agent内从vdagent发出的VDAgentMessage _out_msg 先包装成VDPipeMessage _pipe_msg ={

   u32t .type =  VD_AGENT_commaND;//亦即发往VDIPort而非止步于vdservice

   u32t .opaque=VDP_CLIENT_PORT;//最终发往client

   u32t .size=   _out_msg_size

   u8t* .data=new uint8_t[.size] = (VDAgentMessage*) _out_msg

}

_pipe_msg通过pipe发往vdservice, 然后if(_pipe_msg.type ==VD_AGENT_COMMAND),则vdservice先向vdiport write_ring发出VDAChunkHeader chunk =

{

   u32t .port=   pipe_msg.opaque;

   u32t .size=   pipe_msg.size= _out_msg_size;

}

再发出pipe_msg.data =_out_msg.

3.具体过程(函数路线)(待细化)

1)Agent:

   从2.3)开始

   vdagent.cpp: VDAgent::handle_clipboard_request() 

­­­­{生成.data.data=clip_data的VDAgentMessage _out_msg;}

­­­>vdagent.cpp: VDAgent::write_clipboard()

­­­­{生成.data=_out_msg 的VDPipeMessage pipe_msg;}

­­­>vdagent.cpp: VDAgent::write_completion(){将pipe_msg写入pipe}

   vdservice读取pipe(略),

   vservice.cpp: VDService::handle_pipe_data()

­­­­{经判断,将取得的pipe_msg发往VDI设备输入环:

先发出VDAChunkHeader chunk,再发pipe_msg.data=_out_msg}

   vdservice 写入vdiport(略)

2)Server:

   reds.c: read_from_vdi_port()

{

   从vdiport中读出信息,spice service 的所有管理信息都在

   struct RedsState* reds = 

int listen_socket; 

 ...

VDIPortState agent_state; 

InputsState *inputs_state; 

Incominghandler in_handler; 

RedsOutgoingData outgoing; 

Channel *channels;  ...

}; 内,其中

    struct VDIPortState

 ...

Ring external_bufs; 

Ring internal_bufs; 

Ring write_queue

Ring read_bufs; 

uint32_t read_state; 

uint32_t message_recive_len; 

uint8_t *recive_pos; 

uint32_t recive_len; 

VDIReadBuf *current_read_buf; 

VDIChunkHeader vdi_chunk_header; 

int client_agent_started; 

};

   struct VDIReadBuf 

RingItem link; 

int len; 

uint8_t data[SPICE_AGENT_MAX_DATA_SIZE]; 

}; 

   VDIPortState* state= (VDIPortState*) &reds­>agent_state;

   VDIReadBuf* dispatch_buf;

   在state的管理及辅助下(略),Agent发来的chunk信息读入state内,pipe_msg.data =_out_msg保存于dispatch_buf­>data内。

}

­­­­>reds.c: dispatch_vdi_port_data(state­>vdi_chunk_header.port,dispatch_buf)

{

   用dispatch_buf信息生成RedsOutItem item,再reds_push_pipe_item(item),

   通过channel发往client.

}

3.)Client (略)

III.新的VDAgentMessage子类型添加实例

1.添加类型

   在vdagent.h中为VDAgentMessage.type添加新的种类:enum{

   VD_AGENT_MOUSE_STATE=1,

...    VD_AGENT_ADMIN,//虚机管理消息

...};

   新的结构体(将位于msg.data内):

typedef struct SPICE_ATTR_PACKED VDAgentAdmin

{

   .uint32_t type, //管理消息类型

   .uint8_t* data,//附带信息

}VDAgentAdmin;

enum{//管理消息类型

   VD_AGENT_ADMIN_SHUTDOWN=1;

    VD_AGENT_ADMIN_reboot,...

};

2.使用过程(server 发出shutdown命令给agent):

1)

即server执行

vdagent_guest_admin(SHUTDOWN);

void vdagent_guest_admin(int vd_admin)

{

RingItem *ring_item;

VDAgentExtBuf *buf;

if (!reds­>agent_state.num_client_tokens) {

   red_printf("token violation");

   return;

}

­­reds­>agent_state.num_client_tokens;

if (!vdagent) {

   add_token();

   return;

}

if (!reds­>agent_state.client_agent_started) {

   red_printf("SPICE_MSGC_MaiN_AGENT_DATA race");

   add_token();

   return;

}

if (!(ring_item = ring_get_head(&reds­>agent_state.external_bufs))) {

   red_printf("no agent free bufs");

   return;

}

VDAgentMessage* message;

uint32_t size;

vdagent_admin_msg(vd_admin,&message,&size); //server 生成msg,定义在下面    ring_remove(ring_item);

buf = (VDAgentExtBuf *)ring_item;

buf­>base.now = (uint8_t *)&buf­>base.chunk_header.port;

buf­>base.write_len = size + sizeof(VDIChunkHeader);

buf­>base.chunk_header.size = size;

memcpy(buf­>buf, message, size);

ring_add(&reds­>agent_state.write_queue, ring_item);

write_to_vdi_port();//将msg写入vdiport

free(message);

red_printf("Guest is being shutdown,for disaster is looming...");

}

void vdagent_admin_msg(int vd_admin,VDAgentMessage** ppmsg,uint32_t* msg_size){

uint8_t* data;

uint32_t size;

switch(vd_admin)

{

   case VD_AGENT_ADMIN_SHUTDOWN:

       size=10;

       data=(uint8_t*)malloc(size);

       snprintf(data,size,"%s",(uint8_t*)"Apocalypse");//目前无用

       break;

   default:

       break;

}

*msg_size = sizeof(VDAgentMessage) + sizeof(VDAgentAdmin) + size;

(*ppmsg) = (VDAgentMessage*) (uint8_t*)malloc(*msg_size);

(*ppmsg)­>protocol = VD_AGENT_PROTOCOL;

(*ppmsg)­>type = VD_AGENT_ADMIN;//虚机管理消息

(*ppmsg)­>opaque = 0;//方向:agent

(*ppmsg)­>size = sizeof(VDAgentAdmin) + size;

VDAgentAdmin* admin = (VDAgentAdmin*)((*ppmsg)­>data);

admin­>type = vd_admin;//=SHUTDOWN

memcpy(admin­>data, data, size);//无用

free(data);

}

2. agent

   vdservice会一如往常,从vdiport读出包含这个VDAgentMessage的数据包,加工成pipe_msg发往pipe­­­>vdagent,后者会从pipe内读出pipe_msg:

VDAgent::read_completion()

{

   VDPipeMessage pipe_msg;

...

   do_admin_jobs((VDAgentMessage*)pipe_msg­>data);//监视有无虚机管理消息

...

}

void VDAgent::do_admin_jobs(VDAgentMessage* msg)

{

   if(msg­>type == VD_AGENT_ADMIN)

   {

   FILE* f=fopen("C:\\aho.txt","w");

   fprintf(f,"%s:I'm searching demons...\n",__FUNCTION__);

   VDAgentAdmin* admin = (VDAgentAdmin*)msg­>data;

   if(admin­>type == VD_AGENT_ADMIN_SHUTDOWN)

   {

   system("shutdown ­s ­t 0");//关闭虚机

   fprintf(f,"%s:Hoorrrorrr! Apocalypse has come!!!.Shutdownnow...\n",

   __FUNCTION__);

   fclose(f);

   }

   }

}

相关阅读

分享到:

栏目导航

推荐阅读

热门阅读