1 /* vdagent file xfers code
3 Copyright 2013 Red Hat, Inc.
6 Hans de Goede <hdegoede@redhat.com>
8 This program is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
35 #include <sys/types.h>
36 #include <spice/vd_agent.h>
39 #include "vdagentd-proto.h"
40 #include "vdagent-file-xfers.h"
41 #include "glib-compat.h"
43 struct vdagent_file_xfers {
45 struct udscs_connection *vdagentd;
51 typedef struct AgentFileXferTask {
62 static void vdagent_file_xfer_task_free(gpointer data)
64 AgentFileXferTask *task = data;
66 g_return_if_fail(task != NULL);
68 if (task->file_fd > 0) {
69 syslog(LOG_ERR, "file-xfer: Removing task %u and file %s due to error",
70 task->id, task->file_name);
72 unlink(task->file_name);
73 } else if (task->debug)
74 syslog(LOG_DEBUG, "file-xfer: Removing task %u %s",
75 task->id, task->file_name);
77 g_free(task->file_name);
81 struct vdagent_file_xfers *vdagent_file_xfers_create(
82 struct udscs_connection *vdagentd, const char *save_dir,
83 int open_save_dir, int debug)
85 struct vdagent_file_xfers *xfers;
87 xfers = g_malloc(sizeof(*xfers));
88 xfers->xfers = g_hash_table_new_full(g_direct_hash, g_direct_equal,
89 NULL, vdagent_file_xfer_task_free);
90 xfers->vdagentd = vdagentd;
91 xfers->save_dir = g_strdup(save_dir);
92 xfers->open_save_dir = open_save_dir;
98 void vdagent_file_xfers_destroy(struct vdagent_file_xfers *xfers)
100 g_hash_table_destroy(xfers->xfers);
101 g_free(xfers->save_dir);
105 AgentFileXferTask *vdagent_file_xfers_get_task(
106 struct vdagent_file_xfers *xfers, uint32_t id)
108 AgentFileXferTask *task;
110 task = g_hash_table_lookup(xfers->xfers, GUINT_TO_POINTER(id));
112 syslog(LOG_ERR, "file-xfer: error can not find task %u", id);
117 /* Parse start message then create a new file xfer task */
118 static AgentFileXferTask *vdagent_parse_start_msg(
119 VDAgentFileXferStartMessage *msg)
121 GKeyFile *keyfile = NULL;
122 AgentFileXferTask *task = NULL;
123 GError *error = NULL;
125 keyfile = g_key_file_new();
126 if (g_key_file_load_from_data(keyfile,
127 (const gchar *)msg->data,
129 G_KEY_FILE_NONE, &error) == FALSE) {
130 syslog(LOG_ERR, "file-xfer: failed to load keyfile: %s",
134 task = g_new0(AgentFileXferTask, 1);
136 task->file_name = g_key_file_get_string(
137 keyfile, "vdagent-file-xfer", "name", &error);
139 syslog(LOG_ERR, "file-xfer: failed to parse filename: %s",
143 task->file_size = g_key_file_get_uint64(
144 keyfile, "vdagent-file-xfer", "size", &error);
146 syslog(LOG_ERR, "file-xfer: failed to parse filesize: %s",
150 /* These are set for xfers which are part of a multi-file xfer */
151 task->file_xfer_nr = g_key_file_get_integer(
152 keyfile, "vdagent-file-xfer", "file-xfer-nr", NULL);
153 task->file_xfer_total = g_key_file_get_integer(
154 keyfile, "vdagent-file-xfer", "file-xfer-total", NULL);
156 g_key_file_free(keyfile);
160 g_clear_error(&error);
162 vdagent_file_xfer_task_free(task);
164 g_key_file_free(keyfile);
168 void vdagent_file_xfers_start(struct vdagent_file_xfers *xfers,
169 VDAgentFileXferStartMessage *msg)
171 AgentFileXferTask *task;
172 char *dir = NULL, *path = NULL, *file_path = NULL;
176 if (g_hash_table_lookup(xfers->xfers, GUINT_TO_POINTER(msg->id))) {
177 syslog(LOG_ERR, "file-xfer: error id %u already exists, ignoring!",
182 task = vdagent_parse_start_msg(msg);
187 task->debug = xfers->debug;
189 file_path = g_build_filename(xfers->save_dir, task->file_name, NULL);
191 dir = g_path_get_dirname(file_path);
192 if (g_mkdir_with_parents(dir, S_IRWXU) == -1) {
193 syslog(LOG_ERR, "file-xfer: Failed to create dir %s", dir);
197 path = g_strdup(file_path);
198 for (i = 0; i < 64 && (stat(path, &st) == 0 || errno != ENOENT); i++) {
200 path = g_strdup_printf("%s (%d)", file_path, i + 1);
202 g_free(task->file_name);
203 task->file_name = path;
205 syslog(LOG_ERR, "file-xfer: more then 63 copies of %s exist?",
210 task->file_fd = open(path, O_CREAT | O_WRONLY, 0644);
211 if (task->file_fd == -1) {
212 syslog(LOG_ERR, "file-xfer: failed to create file %s: %s",
213 path, strerror(errno));
217 if (ftruncate(task->file_fd, task->file_size) < 0) {
218 syslog(LOG_ERR, "file-xfer: err reserving %"PRIu64" bytes for %s: %s",
219 task->file_size, path, strerror(errno));
223 g_hash_table_insert(xfers->xfers, GUINT_TO_POINTER(msg->id), task);
226 syslog(LOG_DEBUG, "file-xfer: Adding task %u %s %"PRIu64" bytes",
227 task->id, path, task->file_size);
229 udscs_write(xfers->vdagentd, VDAGENTD_FILE_XFER_STATUS,
230 msg->id, VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA, NULL, 0);
236 udscs_write(xfers->vdagentd, VDAGENTD_FILE_XFER_STATUS,
237 msg->id, VD_AGENT_FILE_XFER_STATUS_ERROR, NULL, 0);
239 vdagent_file_xfer_task_free(task);
244 void vdagent_file_xfers_status(struct vdagent_file_xfers *xfers,
245 VDAgentFileXferStatusMessage *msg)
247 AgentFileXferTask *task;
249 task = vdagent_file_xfers_get_task(xfers, msg->id);
253 switch (msg->result) {
254 case VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA:
255 syslog(LOG_ERR, "file-xfer: task %u %s received unexpected 0 response",
256 task->id, task->file_name);
259 /* Cancel or Error, remove this task */
260 g_hash_table_remove(xfers->xfers, GUINT_TO_POINTER(msg->id));
264 void vdagent_file_xfers_data(struct vdagent_file_xfers *xfers,
265 VDAgentFileXferDataMessage *msg)
267 AgentFileXferTask *task;
268 int len, status = -1;
270 task = vdagent_file_xfers_get_task(xfers, msg->id);
274 len = write(task->file_fd, msg->data, msg->size);
275 if (len == msg->size) {
276 task->read_bytes += msg->size;
277 if (task->read_bytes >= task->file_size) {
278 if (task->read_bytes == task->file_size) {
280 syslog(LOG_DEBUG, "file-xfer: task %u %s has completed",
281 task->id, task->file_name);
282 close(task->file_fd);
284 if (xfers->open_save_dir &&
285 task->file_xfer_nr == task->file_xfer_total) {
287 snprintf(buf, PATH_MAX, "xdg-open '%s'&", xfers->save_dir);
288 status = system(buf);
290 status = VD_AGENT_FILE_XFER_STATUS_SUCCESS;
292 syslog(LOG_ERR, "file-xfer: error received too much data");
293 status = VD_AGENT_FILE_XFER_STATUS_ERROR;
297 syslog(LOG_ERR, "file-xfer: error writing %s: %s", task->file_name,
299 status = VD_AGENT_FILE_XFER_STATUS_ERROR;
303 udscs_write(xfers->vdagentd, VDAGENTD_FILE_XFER_STATUS,
304 msg->id, status, NULL, 0);
305 g_hash_table_remove(xfers->xfers, GUINT_TO_POINTER(msg->id));