[Spice-devel] [PATCH vd_agent V2] drag-n-drop: Add drag-and-drop support for linux agent

Dunrong Huang riegamaths at gmail.com
Fri Nov 23 03:17:45 PST 2012


The patch makes linux agent support drag-n-drop feature.

Signed-off-by: Dunrong Huang <riegamaths at gmail.com>
---
V1 -> V2:
   * New transfer protocol
   
 src/vdagent.c  | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/vdagentd.c |  44 +++++++++++++++++
 2 files changed, 197 insertions(+)

diff --git a/src/vdagent.c b/src/vdagent.c
index 0af82b1..23fa31c 100644
--- a/src/vdagent.c
+++ b/src/vdagent.c
@@ -33,6 +33,8 @@
 #include <signal.h>
 #include <sys/select.h>
 #include <sys/stat.h>
+#include <sys/types.h>
+#include <pwd.h>
 #include <spice/vd_agent.h>
 
 #include "udscs.h"
@@ -40,6 +42,27 @@
 #include "vdagentd-proto-strings.h"
 #include "vdagent-x11.h"
 
+typedef struct AgentData {
+    int dnd_fd;
+    int file_size;
+    int read_bypes;
+} AgentData;
+
+typedef struct AgentFileXferTask {
+    uint32_t                       id;
+    int                            file_fd;
+    uint64_t                       read_bytes;
+    VDAgentFileXferStartMessage    *start_msg;
+    struct AgentFileXferTask       *next;
+} AgentFileXferTask;
+static AgentFileXferTask agent_file_xfer_task_list = {
+    -1,
+    -1,
+    0,
+    NULL,
+    NULL
+};
+
 static const char *portdev = "/dev/virtio-ports/com.redhat.spice.0";
 static int debug = 0;
 static struct vdagent_x11 *x11 = NULL;
@@ -47,6 +70,124 @@ static struct udscs_connection *client = NULL;
 static int quit = 0;
 static int version_mismatch = 0;
 
+/* Remove task from task list */
+static void file_xfer_task_remove(uint32_t id)
+{
+    AgentFileXferTask *list, *prev;
+
+    prev = &agent_file_xfer_task_list;
+    list = agent_file_xfer_task_list.next;
+
+    while (list) {
+        if (list->id == id) {
+            prev->next = list->next;
+            break;
+        }
+        prev = list;
+        list = list->next;
+    }
+}
+
+static const char *get_home_dir()
+{
+    static const char *home = NULL;
+    struct passwd *p;
+
+    if (home)
+        return home;
+
+    home = getenv ("HOME");
+    if (!home) {
+        p = getpwuid(getuid());
+        home = p->pw_dir;
+    }
+
+    return home ? home : "";
+}
+
+static void vdagent_file_xfer_start(VDAgentFileXferStartMessage *msg)
+{
+    AgentFileXferTask *new, *list;
+    char file_path[1024];      /* file path, limit to 1024 chars */
+
+    list = &agent_file_xfer_task_list;
+
+    new = malloc(sizeof(*new));
+    memset(new, 0, sizeof(*new));
+    new->id = msg->id;
+    new->start_msg = malloc(sizeof(VDAgentFileXferStartMessage));
+    memcpy(new->start_msg, msg, sizeof(VDAgentFileXferStartMessage));
+
+    /* TODO: use file mode passed by client */
+    snprintf(file_path, sizeof(file_path), "%s/Desktop/%s",
+             get_home_dir(), (char *)msg->file_name);
+    new->file_fd = open(file_path, O_CREAT | O_WRONLY, 0644);
+    if (new->file_fd == -1) {
+        syslog(LOG_ERR, "Create file error:%s\n", strerror(errno));
+        goto error;
+    }
+
+    if (ftruncate(new->file_fd, msg->file_size) < 0) {
+        close(new->file_fd);
+        goto error;
+    }
+
+    while (list->next) {
+        list = list->next;
+    }
+    list->next = new;
+
+    udscs_write(client, VDAGENTD_FILE_XFER_STATUS,
+                msg->id, VD_AGENT_FILE_XFER_RESULT_SUCCESS, NULL, 0);
+    return ;
+
+error:
+    udscs_write(client, VDAGENTD_FILE_XFER_STATUS,
+                msg->id, VD_AGENT_FILE_XFER_RESULT_ERROR, NULL, 0);
+    free(new->start_msg);
+    free(new);
+}
+
+static void vdagent_file_xfer_status(VDAgentFileXferStatusMessage *msg)
+{
+
+}
+
+static void vdagent_file_xfer_data(VDAgentFileXferDataMessage *msg)
+{
+    AgentFileXferTask *task = NULL, *list;
+    int len;
+
+    list = &agent_file_xfer_task_list;
+    while (list) {
+        if (list->id == msg->id) {
+            task = list;
+            break;
+        }
+        list = list->next;
+    }
+    if (task == NULL) {
+        syslog(LOG_INFO, "Can not find task:%d", msg->id);
+        return ;
+    }
+
+    len = pwrite(task->file_fd, msg->data, msg->size, task->read_bytes);
+    if (len == -1) {
+        syslog(LOG_ERR, "write file error:%s\n", strerror(errno));
+        /* TODO: close, cancel dnd */
+        return ;
+    }
+
+    task->read_bytes += msg->size;
+    if (task->read_bytes >= task->start_msg->file_size) {
+        syslog(LOG_DEBUG, "task %d have been finished", task->id);
+        file_xfer_task_remove(task->id);
+        close(task->file_fd);
+        free(task->start_msg);
+        free(task);
+    }
+}
+
 void daemon_read_complete(struct udscs_connection **connp,
     struct udscs_message_header *header, uint8_t *data)
 {
@@ -82,6 +223,18 @@ void daemon_read_complete(struct udscs_connection **connp,
             version_mismatch = 1;
         }
         break;
+    case VDAGENTD_FILE_XFER_START:
+        vdagent_file_xfer_start((VDAgentFileXferStartMessage *)data);
+        free(data);
+        break;
+    case VDAGENTD_FILE_XFER_STATUS:
+        vdagent_file_xfer_status((VDAgentFileXferStatusMessage *)data);
+        free(data);
+        break;
+    case VDAGENTD_FILE_XFER_DATA:
+        vdagent_file_xfer_data((VDAgentFileXferDataMessage *)data);
+        free(data);
+        break;
     default:
         syslog(LOG_ERR, "Unknown message from vdagentd type: %d, ignoring",
                header->type);
diff --git a/src/vdagentd.c b/src/vdagentd.c
index a4db935..7cda887 100644
--- a/src/vdagentd.c
+++ b/src/vdagentd.c
@@ -214,6 +214,35 @@ static void do_client_clipboard(struct vdagent_virtio_port *vport,
                 data, size);
 }
 
+static void do_client_file_xfer(struct vdagent_virtio_port *vport,
+                                int port_nr,
+                                VDAgentMessage *message_header,
+                                uint8_t *data)
+{
+    uint32_t msg_type;
+
+    if (!active_session_conn) {
+        syslog(LOG_WARNING,
+               "Could not find an agent connnection belonging to the "
+               "active session, ignoring client clipboard request");
+        return;
+    }
+
+    switch (message_header->type) {
+    case VD_AGENT_FILE_XFER_START:
+        msg_type = VDAGENTD_FILE_XFER_START;
+        break;
+    case VD_AGENT_FILE_XFER_STATUS:
+        msg_type = VDAGENTD_FILE_XFER_STATUS;
+        break;
+    case VD_AGENT_FILE_XFER_DATA:
+        msg_type = VDAGENTD_FILE_XFER_DATA;
+        break;
+    }
+
+    udscs_write(active_session_conn, msg_type, 0, 0, data, message_header->size);
+}
+
 int virtio_port_read_complete(
         struct vdagent_virtio_port *vport,
         int port_nr,
@@ -283,6 +312,11 @@ int virtio_port_read_complete(
         }
         do_client_clipboard(vport, message_header, data);
         break;
+    case VD_AGENT_FILE_XFER_START:
+    case VD_AGENT_FILE_XFER_STATUS:
+    case VD_AGENT_FILE_XFER_DATA:
+        do_client_file_xfer(vport, port_nr, message_header, data);
+        break;
     default:
         syslog(LOG_WARNING, "unknown message type %d, ignoring",
                message_header->type);
@@ -614,6 +648,16 @@ void agent_read_complete(struct udscs_connection **connp,
             return;
         }
         break;
+    case VDAGENTD_FILE_XFER_STATUS:{
+        VDAgentFileXferStatusMessage status;
+        status.id = header->arg1;
+        status.result = header->arg2;
+        vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
+                                  VD_AGENT_FILE_XFER_STATUS, 0,
+                                  (uint8_t *)&status, sizeof(status));
+        break;
+    }
+
     default:
         syslog(LOG_ERR, "unknown message from vdagent: %u, ignoring",
                header->type);
-- 
1.8.0



More information about the Spice-devel mailing list