Qt浅谈之四十六QemuQuestAgent的应用

一、简介

        qemu-ga是在虚拟机中安装的一个agent,宿主机host通过通道(unix socket)与虚拟机vm内部的agent进行通信,这样宿主机就有了一种从外部控制/获取虚拟机的手段。比如:host可以向vm下发执行修改hostname的指令,或者获取vm内所有进程信息的指令。
        qemu-ga时刻监听这个unix socket,一旦发现有指令发送来,分析该指令,并执行,通过unix socket返回执行结果,传输的是json字符串。

二、详解

1、centos6下使用qemu-ga

(1)虚拟机连接本机及网络,然后在虚拟机中安装qemu-ga(windows是qemu-ga.exe)
yum install qemu-guest-agent
(2)修改安装后的qemu-ga配置文件
#修改/etc/sysconfig/qemu-ga文件
将 
# Enable fsfreeze hook. See the --fsfreeze-hook option in "qemu-ga --help".
FSFREEZE_HOOK_ENABLE=0
改为
# Enable fsfreeze hook. See the --fsfreeze-hook option in "qemu-ga --help".
FSFREEZE_HOOK_ENABLE=1
#修改/etc/sysconfig/qemu-ga,注释掉BLACKLIST_RPC这一行,将所有功能开放
将
BLACKLIST_RPC="guest-file-open,guest-file-close,guest-file-read,guest-file-write,guest-file-seek,guest-file-flush"
改为
#BLACKLIST_RPC="guest-file-open,guest-file-close,guest-file-read,guest-file-write,guest-file-seek,guest-file-flush"
(3)将虚拟机关机,在虚拟机配置文件libvirt.xml中的<devices>下面添加下述配置,并重新启动虚拟机
<channel type='unix'>
   <source mode='bind' path='/var/lib/libvirt/qemu/f16x86_64.agent'/>
   <target type='virtio' name='org.qemu.guest_agent.0'/>
</channel>
(4)测试是否正常
#得到虚拟机对应的domain id
[root@node-12 ~]# virsh list
 Id    名称                         状态
----------------------------------------------------
 90    instance-0000209f              running
 
#使用命令进行测试
[root@node-12 ~]# virsh qemu-agent-command 90 '{"execute":"guest-info"}'
{"return":{"version":"0.12.1","supported_commands":[{"enabled":true,"name":"guest-set-vcpus"},{"enabled":true}...
(5)freeze文件系统的方法
#直接用virsh命令,freeze文件系统
[root@node-12 ~]# virsh qemu-agent-command 90 '{"execute":"guest-fsfreeze-freeze"}'
{"return":1}
 
#freeze后,可以查询当前虚拟机文件系统的状态,表明是frozen
[root@node-12 ~]# virsh qemu-agent-command 90 '{"execute":"guest-fsfreeze-status"}'
{"return":"frozen"}
 
#thaw(解封)文件系统
[root@node-12 ~]# virsh qemu-agent-command 90 '{"execute":"guest-fsfreeze-thaw"}'
{"return":1}
 
#thaw后,文件系统为解封状态
[root@node-12 ~]# virsh qemu-agent-command 90 '{"execute":"guest-fsfreeze-status"}'
{"return":"thawed"}

2、python使用qemu-ga(类实现)

from commands import getstatusoutput
import logger
import os, sys, stat
import json  
import base64  
import crypt  
import string
import random  
import re
import socket

########################Configure############################
FILE_OPEN_READ="""{"execute":"guest-file-open", "arguments":{"path":"%s","mode":"r"}}"""  
FILE_OPEN_WRITE="""{"execute":"guest-file-open", "arguments":{"path":"%s","mode":"%s"}}"""  
FILE_READ="""{"execute":"guest-file-read", "arguments":{"handle":%s,"count":%d}}"""
FILE_WRITE="""{"execute":"guest-file-write", "arguments":{"handle":%s,"buf-b64":"%s"}}"""   
FILE_CLOSE="""{"execute":"guest-file-close", "arguments":{"handle":%s}}""" 
class QemuQuestAgent:
    def __init__(self):
        self.__socketFileName = ""
        self.__totalSize = 0
        self.__currentSize = 0

    def setSocketFile(self, filename):
        self.socketFileName = filename
        if not os.path.exists(self.socketFileName):
            logger.error("%s do not exist!", self.socketFileName)
            return False
        return True
        
    def resetPassWord(self, newPassword):
        passwordFile = "/etc/shadow"
        content = self._QemuQuestAgent__guestFileRead(passwordFile)
        if not content.strip():
            return False 
        content = base64.standard_b64decode(content)
        user_array = re.split("\n",content)  
        for iter,line in enumerate(user_array):  
            info = line.split(":")  
            if info[0] == "root":  
                info[1] = self._QemuQuestAgent__generationPwd(newPassword)  
                user_array[iter] = ":".join(info)  
                break
        content = base64.standard_b64encode("\n".join(user_array))
        write_count = self._QemuQuestAgent__guestFileWrite(passwordFile, content, "w+")
        if write_count > 0:
            logger.info("change password successfully!")
            return True
        else:
            return False
          
    def putFileToVM(self, fileName, vmFilePathName):
        if not os.path.exists(fileName):
            logger.error("%s do not exist" % (fileName))
            return False
        if vmFilePathName[-1] == "/":
            vmFilePathName += fileName.split("/")[-1]
        filestats = os.stat(fileName)
        self.__totalSize = filestats[stat.ST_SIZE]
        if self.__totalSize <= 0:
            logger.error("%s is Empty!" % (fileName))
            return False
        fd = open(fileName, "r")
        self.__currentSize = 0
        total = 0
        while True:
            content = fd.read(4096)
            total += 4096;
            self.__currentSize += len(content)   #<=4096
            content = base64.standard_b64encode(content)      
            write_count = self._QemuQuestAgent__guestFileWrite(vmFilePathName, content, "a+")
            if write_count <= 0:
                fd.close()
                return False
            if total >= self.__totalSize:
                break
        fd.close()
        return True
    
    def processWrite(self):
        return (self.__currentSize, self.__totalSize)
        
    def __getResult(self, command, fileName = ""):
        resultStr = ""
        if fileName.strip():
            self.socketFileName = fileName
        if not os.path.exists(self.socketFileName):
            return None;
       
        sockfd = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        server_address = self.socketFileName
        try:
            sockfd.connect(server_address)
        except socket.error, msg:
            logger.error("guestagent:%s,%s", socket.error, msg)
            return None
        try:
            sockfd.send(command)
            resultStr = sockfd.recv(10240)
        finally:
            sockfd.close()
        return None if not resultStr else json.loads(resultStr);
    
        
    def __generationPwd(self, pwd):
        salt=''.join(random.choice(string.ascii_letters + string.digits + "./") for _ in range(16))  
        return crypt.crypt(pwd, "$6$%s" % salt)
    
    def __guestFileRead(self, path):
        file_handle = -1
        content = self._QemuQuestAgent__getResult(FILE_OPEN_READ % (path))
        if not content:
           return ""
        file_handle = content["return"]
        if file_handle == -1:
            return ""
        file_content = self._QemuQuestAgent__getResult(FILE_READ % (file_handle,102400))["return"]["buf-b64"]
        self._QemuQuestAgent__getResult(FILE_CLOSE % file_handle) 
        if not file_content:
            return ""
        else:
            return file_content

    def __guestFileWrite(self, path, content, mode):  
        file_handle = -1  
        content = self._QemuQuestAgent__getResult(FILE_OPEN_WRITE % (path, mode))
        if not content:
           return -1
        file_handle = content["return"]
        if file_handle == -1:
            return -2
        write_count = self._QemuQuestAgent__getResult(FILE_WRITE % (file_handle,content))["return"]["count"]
        self._QemuQuestAgent__getResult(FILE_CLOSE % file_handle)  
        return write_count 

########################test############################
def testagent():
    instance = QemuQuestAgent()
    if instance.setSocketFile("/var/lib/libvirt/qemu/test.agent"):
        if instance.resetPassWord("abc123"):
            return True
        if instance.putFileToVM("test.py", "/root/test.py"):
            return True
    return False

if __name__ == '__main__':  
    testagent()

3、Qt使用qemu-ga

(1)qemuquestagent.h
#ifndef QEMUQUESTAGENT_H
#define QEMUQUESTAGENT_H
#include <QString>
#include <QDebug>
#include <QStringList>
#include <QDebug>

/***********************
 *<channel type='unix'>
 *  <source mode='bind' path='/var/lib/libvirt/qemu/test.agent'/>
 *  <target type='virtio' name='com.163.spice.0'/>
 *</channel>
 *
 * qemu-ga --daemonize -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0
 *
 **********************/

/*************QemuQuestAgent*****************/
class HostConnectVM;
class ResetPassWord;
class FileManager;
class QemuQuestAgent
{
public:
    QemuQuestAgent();
    ~QemuQuestAgent();
    void getQGACommandAll();
    void resetPassWord();
    void fileOperation();
private:
    HostConnectVM *hostVM;
    ResetPassWord *passWord;
    FileManager *fileManager;
    QString socketFileName;
};

/*************ResetPassWord*****************/
class ResetPassWord
{
public:
    ResetPassWord(HostConnectVM *host);
    ~ResetPassWord();
    int checkOldPassWord(const QString &password);
    int setNewPassWord(const QString &password);

private:
    QString passwdFileRead();
    bool passwdFileWrite(const QString &password);
    bool openFile(QString mode);
    void closeFile();
    QString generationNewPassWord(const QString &password);

private:
    int fileHandle;
    QString filePasswd;
    HostConnectVM *pwHost;
    QStringList lineContent;
};
/*************FileManager*****************/
class FileManager
{
public:
    FileManager(HostConnectVM *host);
    ~FileManager();
    int getFile(const QString &filePathName, const QString &localPath = "");
    int putFile(const QString &localPathName, const QString &clientPathName);

    bool getFileContent(const QString &filename, const QString &localPathName);
    bool putFileContent(const QString &localName, const QString &clientName);

private:
    int openFile(const QString &filename, const QString &mode);

private:
    HostConnectVM *fmHost;
};
#endif // QEMUQUESTAGENT_H
(2)qemuquestagent.cpp
#include <shadow.h>
#include <unistd.h>
#include "qemuquestagent.h"
#include "questagentcomponent.h"
#include "defineconstants.h"
#include "jsonparse.h"
#include "base64.h"

/*************QemuQuestAgent*****************/
QemuQuestAgent::QemuQuestAgent()
{
    socketFileName = "/var/lib/libvirt/qemu/test.agent";
    hostVM = new HostConnectVM(socketFileName);
    passWord = new ResetPassWord(hostVM);
    fileManager = new FileManager(hostVM);
}

QemuQuestAgent::~QemuQuestAgent()
{
    if (hostVM) {
        delete hostVM;
        hostVM = NULL;
    }
    if (passWord) {
        delete passWord;
        passWord = NULL;
    }
    if (fileManager) {
        delete fileManager;
        fileManager = NULL;
    }
}

void QemuQuestAgent::getQGACommandAll()
{
    QString info = hostVM->getResult(GUEST_INFO);
    qDebug() << __FUNCTION__<<__LINE__ << info;
    if (info == "error2") {
        qDebug() << "can not conncet to the client of VM";
    }
}

void QemuQuestAgent::resetPassWord()
{
    QString oldPassWord = "abc123";
    QString newPassWord = "abc124";
    int flag = passWord->checkOldPassWord(oldPassWord);
    if (flag == 1) {
        qDebug() << "old password is wrong";
    }
    else if (flag == 0){
        qDebug() << "password is right";
        if (passWord->setNewPassWord(newPassWord) == 0) {
            qDebug() << "set newpassword succeed";
        }
        else {
            qDebug() << "set newpassword failed";
        }
    }
    else {
        //qDebug() << __LINE__ << "the client of VM is off";
    }
}

void QemuQuestAgent::fileOperation()
{
    qDebug() << "---fileOperation---";
    QString file = "/tmp/test1K.iso";
    fileManager->getFile(file, "/tmp/abc/");
    //fileManager->putFile("/tmp/abc/test1M.iso", "/tmp/");
}

/*************ResetPassWord*****************/
ResetPassWord::ResetPassWord(HostConnectVM *host)
    : fileHandle(-1)
{
    filePasswd = "/etc/shadow";
    pwHost = host;
}

ResetPassWord::~ResetPassWord()
{

}

int ResetPassWord::checkOldPassWord(const QString &password)
{
    if (openFile("r") == false) {
        return -1;
    }
    QString fileContent = passwdFileRead();
    closeFile();
    lineContent.clear();
    lineContent = fileContent.split("\n");
    for (int index = 0; index < lineContent.size(); ++index) {
        QStringList fields = lineContent.at(index).split(":");
        if (fields.at(0) == "root") {
            QStringList passwdList = fields.at(1).trimmed().split("$");
            QString saltBuff = QString("$%1$%2").arg(passwdList[1]).arg(passwdList[2]);
            char *shadowPwd = crypt(password.toStdString().data(), saltBuff.toStdString().data());
            if (!strcmp(shadowPwd, fields.at(1).trimmed().toStdString().data())) {   //compare old and new password
                return 0;
            }
            else {
                return 1;
            }
        }
    }
    return false;
}

int ResetPassWord::setNewPassWord(const QString &password)
{
    for (int index = 0; index < lineContent.size(); ++index) {
        QStringList fields = lineContent.at(index).split(":");
        if (fields.at(0) == "root") {
            fields[1] = generationNewPassWord(password);
            lineContent[index] = fields.join(":");
            break;
        }
    }
    QString newFileContent = "";
    if (lineContent.size() > 0) {
        newFileContent = lineContent.join("\n");
    }
    if (openFile("r+") == false && newFileContent.isEmpty()) {
        return -1;
    }
    if (passwdFileWrite(newFileContent) == false) {
        return 1;
    }
    lineContent.clear();
    closeFile();
    return 0;
}

QString ResetPassWord::passwdFileRead()
{
    QString resultStr = pwHost->getResult(FILE_READ.arg(fileHandle).arg(1400));
    //qDebug() << "**********" << resultStr<<"****";
    QMap<QString, QString>resultMap = JsonParse::jsonParseEnter(resultStr);
    // {"return": {"buf-b64": "aGVsbG8gd29ybGQhCg==", "count": 13, "eof": true}}
    if (resultMap.count() != 3) {
        qDebug() << "JsonParse failed!" <<endl;
        return false;
    }
    else {
        if (resultMap["eof"] != "true") {
            qDebug() << "qga readfile failed!" <<endl;
            return false;
        }
        string outResult = "";
        Base64TransCode::Base64Decode(resultMap["buf-b64"].toStdString(), &outResult);
        return QString::fromStdString(outResult);
    }
}

bool ResetPassWord::passwdFileWrite(const QString &password)
{
    string outResult = "";
    Base64TransCode::Base64Encode(password.toStdString(), &outResult);
    QString resultStr = pwHost->getResult(FILE_WRITE.arg(fileHandle).arg(QString::fromStdString(outResult)));
    QMap<QString, QString>resultMap = JsonParse::jsonParseEnter(resultStr);
    // {"return": {"count": 13, "eof": false}}
    qDebug() << "&&&" << resultMap.count() << resultMap["eof"] << resultMap["count"] << QString::fromStdString(outResult) << outResult.size();
    if (resultMap.count() == 2 && resultMap["eof"] == "false") {
        qDebug() << "change password successed";
        return true;
    }
    else {
        return false;
    }
}

bool ResetPassWord::openFile(QString mode)
{
    QString resultStr = pwHost->getResult(FILE_OPEN.arg(filePasswd).arg(mode));
    if (resultStr == "error2") {
        qDebug() << "can not conncet to the client of VM";
        return false;
    }
    //qDebug() << "aoyang---------------" << resultStr;
    QMap<QString, QString> resultMap = JsonParse::jsonParseEnter(resultStr);
    fileHandle = resultMap.begin().value().toInt();
    if (fileHandle < 0) {
        return false;
    }
    return true;
}

void ResetPassWord::closeFile()
{
    pwHost->getResult(FILE_FLUSH.arg(fileHandle));
    pwHost->getResult(FILE_CLOSE.arg(fileHandle));
}

QString ResetPassWord::generationNewPassWord(const QString &password)
{
    char seqCode[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
    QString verifyCode = "$6$";
    QTime time= QTime::currentTime();
    qsrand(time.msec()+time.second()*1000);
    for(int i = 0; i < 16; i++) {
        int num = qrand() % strlen(seqCode);
        verifyCode += seqCode[num];
    }
    QString shadowPwd = crypt(password.toStdString().data(), verifyCode.toStdString().data());
    return shadowPwd;
}

/*************FileManager*****************/
FileManager::FileManager(HostConnectVM *host)
{
    fmHost = host;
}

FileManager::~FileManager()
{

}

int FileManager::getFile(const QString &filePathName, const QString &localPath)
{
    if (filePathName.isEmpty()) {
        return -1;
    }
    QFileInfo info(filePathName);
    QString locaFilename = localPath;
    if (localPath.isEmpty()) {
        locaFilename = QDir::currentPath();
        locaFilename += "/";
        locaFilename += info.fileName();
    }
    else if (localPath.right(1) == "/"){      //path
         locaFilename += info.fileName();
    }
    QFileInfo loaclInfo(locaFilename);
    QString dirName = loaclInfo.absolutePath();
    QDir dir(dirName);
    if (!dir.exists()) {
        dir.mkpath(dirName);
    }
    //write content to file
    //qDebug() << "locaFilename=" << locaFilename << "filePathName=" <<filePathName;
    getFileContent(filePathName, locaFilename);

    return 0;
}

bool FileManager::getFileContent(const QString &filename, const QString &localPathName)
{
    int handle = openFile(filename, "r");
    if (handle < 0) {
        return false;
    }
    QFile file(localPathName);
    file.open(QIODevice::WriteOnly | QIODevice::Text);
    QTextStream out(&file);

    while(true) {
        QString resultStr = fmHost->getResult(FILE_READ.arg(handle).arg(1400));
        //qDebug() << "--------" << resultStr<<"---------";
        QMap<QString, QString>resultMap = JsonParse::jsonParseEnter(resultStr);
        // {"return": {"buf-b64": "aGVsbG8gd29ybGQhCg==", "count": 13, "eof": false}}
        if (resultMap.count() != 3) {
            qDebug() << "JsonParse failed!" <<endl;
            return false;
        }
        else {
            //qDebug() << "++++++++++++++++++++" << resultMap["count"];
            if (resultMap["count"].toInt() < 0) {
                qDebug() << "qga readfile failed!" <<endl;
                return false;
            }
            string outResult = "";
            Base64TransCode::Base64Decode(resultMap["buf-b64"].toStdString(), &outResult);
            //qDebug() << "out-------put====" << QString::fromStdString(outResult);
            out << QString::fromStdString(outResult);
            file.flush();
            if (resultMap["eof"] == "true") {
                file.close();
                fmHost->getResult(FILE_CLOSE.arg(handle));
                return true;
            }
        }
    }
    return false;
}

int FileManager::putFile(const QString &localPathName, const QString &clientPathName)
{
    QFileInfo localFile(localPathName);
    if (!localFile.isFile()) {
        qDebug() << "file is not existed";
        return -1;
    }
    //long fileSize = localFile.size();

    QString clientFileName = clientPathName;

    if (clientPathName.right(1) == "/") {
        clientFileName += localFile.fileName();
    }
    putFileContent(localPathName, clientFileName);
}

bool FileManager::putFileContent(const QString &localName, const QString &clientName)
{
    QFile file(localName);
    long fileSize = file.size();
    if (fileSize <= 0) {
        return false;
    }
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        return false;
    }
    int handle = openFile(clientName, "w");
    if (handle < 0) {
        return false;
    }
    int total = 0;
    do {
        string fileContent = file.read(10240).data();
        total += 10240;
        string outResult = "";
        Base64TransCode::Base64Encode(fileContent, &outResult);
        QString resultStr = fmHost->getResult(FILE_WRITE.arg(handle).arg(QString::fromStdString(outResult)));
        fmHost->getResult(FILE_FLUSH.arg(handle));

        QMap<QString, QString>resultMap = JsonParse::jsonParseEnter(resultStr);
        // {"return": {"count": 13, "eof": false}}
        //qDebug() << "&&&" << resultMap.count() << resultMap["eof"] << resultMap["count"]<< outResult.size();

        if (resultMap.count() != 2 || resultMap["count"].toInt() <= 0) {
            qDebug() << "write file error";
            fmHost->getResult(FILE_CLOSE.arg(handle));
            return false;
        }
    }while(total < fileSize);
    fmHost->getResult(FILE_CLOSE.arg(handle));
}

int FileManager::openFile(const QString &filename, const QString &mode)
{
    QString resultStr = fmHost->getResult(FILE_OPEN.arg(filename).arg(mode));
    qDebug() << FILE_OPEN.arg(filename).arg(mode);
    if (resultStr == "error2") {
        qDebug() << "can not conncet to the client of VM";
        return -2;
    }
    QMap<QString, QString> resultMap = JsonParse::jsonParseEnter(resultStr);
    int fileHandle = resultMap.begin().value().toInt();
    if (fileHandle <= 0) {
        qDebug() << "open file failed!";
        return -1;
    }
    return fileHandle;
}
(3)questagentcomponent.cpp
socket通讯,发送命令返回结果
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <QDebug>
#include "questagentcomponent.h"

HostConnectVM::HostConnectVM(const QString &file)
{
    socketFile = file;
}

HostConnectVM::~HostConnectVM()
{

}

QString HostConnectVM::getResult(QString command, QString file)
{
    if (!file.isEmpty()) {
        socketFile = file;
    }
    QString resultStr = "";
    /* create a socket */
    struct sockaddr_un address;
    address.sun_family = AF_UNIX;
    strcpy(address.sun_path, socketFile.toStdString().data());
    sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sockfd == -1) {
        qDebug() << "create socket failed";
        resultStr = "error1";
        return resultStr;
    }
    /* connect to the server */
    int result = connect(sockfd, (struct sockaddr *)&address, sizeof(address));
    if(result == -1)  {
         qDebug() << "connect socket failed";
         resultStr = "error2";
         return resultStr;
    }

    if((result = write(sockfd, command.toStdString().data(), command.length())) != command.length()) {
        qDebug() << "write socket failed";
        resultStr = "error3";
        return resultStr;
    }
    char buff[10240] = {0};
    if ((result = read(sockfd, buff, 10240)) == -1) {
        qDebug() << "read socket failed";
        resultStr = "error4";
        return resultStr;
    }
    resultStr = buff;
    resultStr = resultStr.trimmed();
    close(sockfd);
    return resultStr;
}
(4)jsonparse.cpp
传输数据格式json的封装和解析
#include "jsonparse.h"

JsonParse::JsonParse()
{

}

JsonParse::~JsonParse()
{

}

QMap<QString, QString> JsonParse::jsonParseEnter(const QString &str)
{
    QMap<QString, QString> map;
    map.clear();
    if (str.trimmed().isEmpty()) {
        return map;
    }
    QScriptEngine engine;
    QScriptValue source = engine.evaluate("value="+str);
    QScriptValue valueReturn = source.property("return");
    if (valueReturn.isObject()) {
        QScriptValueIterator it(valueReturn);
        while(it.hasNext()) {
            it.next();
            map.insert(it.name(), it.value().toString());
        }
    }
    else {
        map.insert("return", valueReturn.toString());
    }
#ifndef PRINT_DEBUG
    QMap<QString, QString>::const_iterator iter = map.constBegin();
    for(;iter != map.constEnd(); iter++) {
        qDebug() << "---key=" << iter.key() << ", value=" << iter.value();
    }
#endif
}
(5)base64.cpp
        base64将字符串以MIME BASE64编码,此种编码是为了使二进制数据可以通过非纯8-bit的传输层传输,可以让中文字或者图片也能在网络上顺利传输,例如电子邮件的主体。在BASE64编码后的字符串只包含英文字母大小写、阿拉伯数字、加号与反斜线,共64个基本字符,不包含其它特殊的字符,因而才取名BASE64。编码后的字符串比原来的字符串长度再加1/3左右。
#include <string.h>
#include "base64.h"

bool Base64TransCode::Base64Encode(const string& input, string* output)
{
  string temp;
  temp.resize(modp_b64_encode_len(input.size()));    // makes room for null byte

  // null terminates result since result is base64 text!
  int input_size = static_cast<int>(input.size());
  int output_size= modp_b64_encode(&(temp[0]), input.data(), input_size);

  if (output_size < 0)  return false;

  temp.resize(output_size);  // strips off null byte
  output->swap(temp);
  return true;
}

bool Base64TransCode::Base64Decode(const string& input, string* output)
{
  string temp;
  temp.resize(modp_b64_decode_len(input.size()));

  // does not null terminate result since result is binary data!
  int input_size = static_cast<int>(input.size());
  int output_size = modp_b64_decode(&(temp[0]), input.data(), input_size);

  if (output_size < 0) return false;

  temp.resize(output_size);
  output->swap(temp);
  return true;
}
(6)main.cpp
#include <QCoreApplication>
#include "qemuquestagent.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QemuQuestAgent w;
    //w.resetPassWord();
    w.getQGACommandAll();
    //w.fileOperation();
    return a.exec();
}

三、总结

(1)参考:http://www.zoues.com/index.php/2015/10/13/qemu-guest-agent/http://www.csdn123.com/html/itweb/20130729/27101_27081_27076.htmhttp://blog.csdn.net/taiyang1987912/article/details/45191153
(2)若有问题或建议,请留言,在此感谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乌托邦2号

博文不易,支持的请给予小小打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值