Hi On Wed, Dec 5, 2012 at 5:27 AM, Dunrong Huang <riegamaths@xxxxxxxxx> wrote: > The patch makes linux agent support file-xfer feature. > > Signed-off-by: Dunrong Huang <riegamaths@xxxxxxxxx> > --- > src/vdagent.c | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > src/vdagentd.c | 43 +++++++++++++++ > 2 files changed, 214 insertions(+) > > diff --git a/src/vdagent.c b/src/vdagent.c > index 0af82b1..b6507ec 100644 > --- a/src/vdagent.c > +++ b/src/vdagent.c > @@ -34,12 +34,24 @@ > #include <sys/select.h> > #include <sys/stat.h> > #include <spice/vd_agent.h> > +#include <glib.h> > > #include "udscs.h" > #include "vdagentd-proto.h" > #include "vdagentd-proto-strings.h" > #include "vdagent-x11.h" > > +typedef struct AgentFileXferTask { > + uint32_t id; > + int file_fd; > + uint64_t read_bytes; > + char *file_name; > + uint64_t file_size; > + struct AgentFileXferTask *next; > +} AgentFileXferTask; > + > +GList *agent_file_xfer_task_list = NULL; Tbh, I wonder why not using a GHashTable instead, the code might get even simpler. > static const char *portdev = "/dev/virtio-ports/com.redhat.spice.0"; > static int debug = 0; > static struct vdagent_x11 *x11 = NULL; > @@ -47,6 +59,153 @@ static struct udscs_connection *client = NULL; > static int quit = 0; > static int version_mismatch = 0; > > +static gint file_xfer_task_find(gconstpointer a, gconstpointer b) > +{ > + AgentFileXferTask *task = (AgentFileXferTask *)a; > + uint32_t id = *(uint32_t *)b; > + > + if (task->id == id) { > + return 0; > + } > + > + return 1; > +} That would go away for example. > +static const char *get_home_dir() > +{ > + const char *homedir = g_getenv("HOME"); > + if (!homedir) { > + homedir = g_get_home_dir(); > + } > + return homedir; > +} Right, this is needed for glib < 2.36. Please add a comment, I might not be the first one surprised. > +/* Parse start messag then create a new file xfer task */ > +static AgentFileXferTask *vdagent_parse_start_msg( > + VDAgentFileXferStartMessage *msg) > +{ > + GKeyFile *keyfile = NULL; > + AgentFileXferTask *task = NULL; > + > + keyfile = g_key_file_new(); > + if (g_key_file_load_from_data(keyfile, > + (const gchar *)msg->data, > + -1, > + G_KEY_FILE_NONE, NULL) == FALSE) { > + goto error; Please add a GError and print it's content in error. > + } > + task = g_malloc0(sizeof(*task)); nitpick: the more elegant g_new0(AgentFileXferTask, 1); is generally prefered. > + task->id = msg->id; > + task->file_name = g_key_file_get_string( > + keyfile, "vdagent-file-xfer", "name", NULL); > + task->file_size = g_key_file_get_uint64( > + keyfile, "vdagent-file-xfer", "size", NULL); Please use a GError and go to error if it is set. > + > + g_key_file_free(keyfile); > + return task; > + > +error: > + if (task) { > + g_free(task->file_name); > + g_free(task); > + } It is nicer and more future proof to add a agent_file_xfer_task_free() > + if (keyfile) { > + g_key_file_free(keyfile); > + } > + return NULL; > +} > + > +static void vdagent_file_xfer_start(VDAgentFileXferStartMessage *msg) > +{ > + AgentFileXferTask *new; > + char file_path[1024]; /* file path, limit to 1024 chars */ There is a define PATHMAX or something, but I don't think you need to care about it here, you can just allocate the string. > + new = vdagent_parse_start_msg(msg); > + if (new == NULL) { > + goto error; > + } > + > + snprintf(file_path, sizeof(file_path), "%s/Desktop/%s", > + get_home_dir(), new->file_name); Please use g_build_filename () and g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP). You might not need the get_home_dir() function anymore. > + 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, new->file_size) < 0) { > + close(new->file_fd); > + goto error; > + } > + > + agent_file_xfer_task_list = g_list_append(agent_file_xfer_task_list, new); > + > + udscs_write(client, VDAGENTD_FILE_XFER_STATUS, > + msg->id, VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA, NULL, 0); > + return ; > + > +error: > + udscs_write(client, VDAGENTD_FILE_XFER_STATUS, > + msg->id, VD_AGENT_FILE_XFER_STATUS_ERROR, NULL, 0); > + if (new) { > + free(new->file_name); > + free(new); > + } > +} > + > +static void vdagent_file_xfer_status(VDAgentFileXferStatusMessage *msg) > +{ > + syslog(LOG_INFO, "task %d received response %d", msg->id, msg->result); > + > + if (msg->result == VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA) { > + /* Do nothing */ > + } else { > + /* Error, remove this task */ > + GList *l; > + l = g_list_find_custom(agent_file_xfer_task_list, &msg->id, > + file_xfer_task_find); > + if (l) { > + AgentFileXferTask *task = l->data; > + syslog(LOG_DEBUG, "user removed task %d, result: %d", msg->id, msg->result); > + agent_file_xfer_task_list = g_list_remove(agent_file_xfer_task_list, task); > + close(task->file_fd); > + free(task->file_name); > + free(task); If you use a GHashTable, you should be able to call only g_hash_table_remove (GINT_TO_POINTER(msg->id)), use hash_table_new_full() with agent_file_xfer_task_free(). > +static void vdagent_file_xfer_data(VDAgentFileXferDataMessage *msg) > +{ > + AgentFileXferTask *task; > + int len; > + GList *l; > + > + l = g_list_find_custom(agent_file_xfer_task_list, &msg->id, > + file_xfer_task_find); > + if (l == NULL) { > + syslog(LOG_INFO, "Can not find task:%d", msg->id); > + return ; > + } > + > + task = l->data; > + 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 ; > + } Why not just call write() since it's written sequentially? I rarely use pwrite(), but perhaps it's better in some other way? :) > + task->read_bytes += msg->size; > + if (task->read_bytes >= task->file_size) { It could be worthwhile to check the > condition and warn in this case before finishing. > + syslog(LOG_DEBUG, "task %d have been finished", task->id); > + agent_file_xfer_task_list = g_list_remove(agent_file_xfer_task_list, task); > + close(task->file_fd); > + free(task->file_name); > + free(task); > + } > +} > + > void daemon_read_complete(struct udscs_connection **connp, > struct udscs_message_header *header, uint8_t *data) > { > @@ -82,6 +241,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 bd5e0d4..7006d80 100644 > --- a/src/vdagentd.c > +++ b/src/vdagentd.c > @@ -215,6 +215,34 @@ static void do_client_clipboard(struct vdagent_virtio_port *vport, > data, size); > } > > +static void do_client_file_xfer(struct vdagent_virtio_port *vport, > + 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, > @@ -284,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, message_header, data); > + break; > default: > syslog(LOG_WARNING, "unknown message type %d, ignoring", > message_header->type); > @@ -613,6 +646,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 Thanks! -- Marc-André Lureau _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel