vdagent与vdserver

                     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);

    }

    }

}

 

你可能感兴趣的:(虚拟机,server,function,token,任务,jobs)