[Spice-devel] [PATCH vd_agent V3 4/4] file-xfer: Add file-xfer support for linux agent

Dunrong Huang riegamaths at gmail.com
Fri Jan 18 02:39:29 PST 2013


Hi,
thank you for your review

On Wed, Jan 16, 2013 at 9:29 PM, Marc-André Lureau
<marcandre.lureau at gmail.com> wrote:
> Hi
>
> On Wed, Dec 5, 2012 at 5:27 AM, Dunrong Huang <riegamaths at gmail.com> wrote:
>> The patch makes linux agent support file-xfer feature.
>>
>> Signed-off-by: Dunrong Huang <riegamaths at gmail.com>
>> ---
>>  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.
>
Sound great, I will try it.
>>  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.
>
Ok.
>> +/* 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.
>
will be fixed in next version
>> +    }
>> +    task = g_malloc0(sizeof(*task));
>
> nitpick: the more elegant g_new0(AgentFileXferTask, 1); is generally prefered.
>
ok
>> +    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.
>
will be fixed in next version.
>> +
>> +    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()
>
Agree, the code here was a bit ugly, will be fixed in next version.
>> +    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.
>
Ok.
>> +    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.
>
you are right, will be fixed in next version.
>> +    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? :)
>
you are right, there is no special purpose, write() should be enough.
>> +    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.
>
Ok.
>> +        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
>>
>> _______________________________________________


-- 
Best Regards,

Dunrong Huang


More information about the Spice-devel mailing list