The patch makes linux agent support drag-n-drop feature. Signed-off-by: Dunrong Huang <riegamaths@xxxxxxxxx> --- 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 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel