remove trailing spaces
[vd_agent.git] / src / vdagent-file-xfers.c
blob48b3069a2ce4b16e92ee885a3cc5e4f1200c9fe3
1 /* vdagent file xfers code
3 Copyright 2013 Red Hat, Inc.
5 Red Hat Authors:
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/>.
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <inttypes.h>
29 #include <string.h>
30 #include <syslog.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <errno.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36 #include <spice/vd_agent.h>
37 #include <glib.h>
39 #include "vdagentd-proto.h"
40 #include "vdagent-file-xfers.h"
42 struct vdagent_file_xfers {
43 GHashTable *xfers;
44 struct udscs_connection *vdagentd;
45 char *save_dir;
46 int open_save_dir;
47 int debug;
50 typedef struct AgentFileXferTask {
51 uint32_t id;
52 int file_fd;
53 uint64_t read_bytes;
54 char *file_name;
55 uint64_t file_size;
56 int file_xfer_nr;
57 int file_xfer_total;
58 int debug;
59 } AgentFileXferTask;
61 static void vdagent_file_xfer_task_free(gpointer data)
63 AgentFileXferTask *task = data;
65 g_return_if_fail(task != NULL);
67 if (task->file_fd > 0) {
68 syslog(LOG_ERR, "file-xfer: Removing task %u and file %s due to error",
69 task->id, task->file_name);
70 close(task->file_fd);
71 unlink(task->file_name);
72 } else if (task->debug)
73 syslog(LOG_DEBUG, "file-xfer: Removing task %u %s",
74 task->id, task->file_name);
76 g_free(task->file_name);
77 g_free(task);
80 struct vdagent_file_xfers *vdagent_file_xfers_create(
81 struct udscs_connection *vdagentd, const char *save_dir,
82 int open_save_dir, int debug)
84 struct vdagent_file_xfers *xfers;
86 xfers = g_malloc(sizeof(*xfers));
87 xfers->xfers = g_hash_table_new_full(g_direct_hash, g_direct_equal,
88 NULL, vdagent_file_xfer_task_free);
89 xfers->vdagentd = vdagentd;
90 xfers->save_dir = g_strdup(save_dir);
91 xfers->open_save_dir = open_save_dir;
92 xfers->debug = debug;
94 return xfers;
97 void vdagent_file_xfers_destroy(struct vdagent_file_xfers *xfers)
99 g_return_if_fail(xfers != NULL);
101 g_hash_table_destroy(xfers->xfers);
102 g_free(xfers->save_dir);
103 g_free(xfers);
106 static AgentFileXferTask *vdagent_file_xfers_get_task(
107 struct vdagent_file_xfers *xfers, uint32_t id)
109 AgentFileXferTask *task;
111 g_return_val_if_fail(xfers != NULL, NULL);
113 task = g_hash_table_lookup(xfers->xfers, GUINT_TO_POINTER(id));
114 if (task == NULL)
115 syslog(LOG_ERR, "file-xfer: error can not find task %u", id);
117 return task;
120 /* Parse start message then create a new file xfer task */
121 static AgentFileXferTask *vdagent_parse_start_msg(
122 VDAgentFileXferStartMessage *msg)
124 GKeyFile *keyfile = NULL;
125 AgentFileXferTask *task = NULL;
126 GError *error = NULL;
128 keyfile = g_key_file_new();
129 if (g_key_file_load_from_data(keyfile,
130 (const gchar *)msg->data,
132 G_KEY_FILE_NONE, &error) == FALSE) {
133 syslog(LOG_ERR, "file-xfer: failed to load keyfile: %s",
134 error->message);
135 goto error;
137 task = g_new0(AgentFileXferTask, 1);
138 task->id = msg->id;
139 task->file_name = g_key_file_get_string(
140 keyfile, "vdagent-file-xfer", "name", &error);
141 if (error) {
142 syslog(LOG_ERR, "file-xfer: failed to parse filename: %s",
143 error->message);
144 goto error;
146 task->file_size = g_key_file_get_uint64(
147 keyfile, "vdagent-file-xfer", "size", &error);
148 if (error) {
149 syslog(LOG_ERR, "file-xfer: failed to parse filesize: %s",
150 error->message);
151 goto error;
153 /* These are set for xfers which are part of a multi-file xfer */
154 task->file_xfer_nr = g_key_file_get_integer(
155 keyfile, "vdagent-file-xfer", "file-xfer-nr", NULL);
156 task->file_xfer_total = g_key_file_get_integer(
157 keyfile, "vdagent-file-xfer", "file-xfer-total", NULL);
159 g_key_file_free(keyfile);
160 return task;
162 error:
163 g_clear_error(&error);
164 if (task)
165 vdagent_file_xfer_task_free(task);
166 if (keyfile)
167 g_key_file_free(keyfile);
168 return NULL;
171 void vdagent_file_xfers_start(struct vdagent_file_xfers *xfers,
172 VDAgentFileXferStartMessage *msg)
174 AgentFileXferTask *task;
175 char *dir = NULL, *path = NULL, *file_path = NULL;
176 struct stat st;
177 int i;
179 g_return_if_fail(xfers != NULL);
181 if (g_hash_table_lookup(xfers->xfers, GUINT_TO_POINTER(msg->id))) {
182 syslog(LOG_ERR, "file-xfer: error id %u already exists, ignoring!",
183 msg->id);
184 return;
187 task = vdagent_parse_start_msg(msg);
188 if (task == NULL) {
189 goto error;
192 task->debug = xfers->debug;
194 file_path = g_build_filename(xfers->save_dir, task->file_name, NULL);
196 dir = g_path_get_dirname(file_path);
197 if (g_mkdir_with_parents(dir, S_IRWXU) == -1) {
198 syslog(LOG_ERR, "file-xfer: Failed to create dir %s", dir);
199 goto error;
202 path = g_strdup(file_path);
203 for (i = 0; i < 64 && (stat(path, &st) == 0 || errno != ENOENT); i++) {
204 g_free(path);
205 path = g_strdup_printf("%s (%d)", file_path, i + 1);
207 g_free(task->file_name);
208 task->file_name = path;
209 if (i == 64) {
210 syslog(LOG_ERR, "file-xfer: more then 63 copies of %s exist?",
211 file_path);
212 goto error;
215 task->file_fd = open(path, O_CREAT | O_WRONLY, 0644);
216 if (task->file_fd == -1) {
217 syslog(LOG_ERR, "file-xfer: failed to create file %s: %s",
218 path, strerror(errno));
219 goto error;
222 if (ftruncate(task->file_fd, task->file_size) < 0) {
223 syslog(LOG_ERR, "file-xfer: err reserving %"PRIu64" bytes for %s: %s",
224 task->file_size, path, strerror(errno));
225 goto error;
228 g_hash_table_insert(xfers->xfers, GUINT_TO_POINTER(msg->id), task);
230 if (xfers->debug)
231 syslog(LOG_DEBUG, "file-xfer: Adding task %u %s %"PRIu64" bytes",
232 task->id, path, task->file_size);
234 udscs_write(xfers->vdagentd, VDAGENTD_FILE_XFER_STATUS,
235 msg->id, VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA, NULL, 0);
236 g_free(file_path);
237 g_free(dir);
238 return ;
240 error:
241 udscs_write(xfers->vdagentd, VDAGENTD_FILE_XFER_STATUS,
242 msg->id, VD_AGENT_FILE_XFER_STATUS_ERROR, NULL, 0);
243 if (task)
244 vdagent_file_xfer_task_free(task);
245 g_free(file_path);
246 g_free(dir);
249 void vdagent_file_xfers_status(struct vdagent_file_xfers *xfers,
250 VDAgentFileXferStatusMessage *msg)
252 AgentFileXferTask *task;
254 g_return_if_fail(xfers != NULL);
256 task = vdagent_file_xfers_get_task(xfers, msg->id);
257 if (!task)
258 return;
260 switch (msg->result) {
261 case VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA:
262 syslog(LOG_ERR, "file-xfer: task %u %s received unexpected 0 response",
263 task->id, task->file_name);
264 break;
265 default:
266 /* Cancel or Error, remove this task */
267 g_hash_table_remove(xfers->xfers, GUINT_TO_POINTER(msg->id));
271 void vdagent_file_xfers_data(struct vdagent_file_xfers *xfers,
272 VDAgentFileXferDataMessage *msg)
274 AgentFileXferTask *task;
275 int len, status = -1;
277 g_return_if_fail(xfers != NULL);
279 task = vdagent_file_xfers_get_task(xfers, msg->id);
280 if (!task)
281 return;
283 len = write(task->file_fd, msg->data, msg->size);
284 if (len == msg->size) {
285 task->read_bytes += msg->size;
286 if (task->read_bytes >= task->file_size) {
287 if (task->read_bytes == task->file_size) {
288 if (xfers->debug)
289 syslog(LOG_DEBUG, "file-xfer: task %u %s has completed",
290 task->id, task->file_name);
291 close(task->file_fd);
292 task->file_fd = -1;
293 if (xfers->open_save_dir &&
294 task->file_xfer_nr == task->file_xfer_total &&
295 g_hash_table_size(xfers->xfers) == 1) {
296 char buf[PATH_MAX];
297 snprintf(buf, PATH_MAX, "xdg-open '%s'&", xfers->save_dir);
298 status = system(buf);
300 status = VD_AGENT_FILE_XFER_STATUS_SUCCESS;
301 } else {
302 syslog(LOG_ERR, "file-xfer: error received too much data");
303 status = VD_AGENT_FILE_XFER_STATUS_ERROR;
306 } else {
307 syslog(LOG_ERR, "file-xfer: error writing %s: %s", task->file_name,
308 strerror(errno));
309 status = VD_AGENT_FILE_XFER_STATUS_ERROR;
312 if (status != -1) {
313 udscs_write(xfers->vdagentd, VDAGENTD_FILE_XFER_STATUS,
314 msg->id, status, NULL, 0);
315 g_hash_table_remove(xfers->xfers, GUINT_TO_POINTER(msg->id));
319 void vdagent_file_xfers_error(struct udscs_connection *vdagentd, uint32_t msg_id)
321 g_return_if_fail(vdagentd != NULL);
323 udscs_write(vdagentd, VDAGENTD_FILE_XFER_STATUS,
324 msg_id, VD_AGENT_FILE_XFER_STATUS_ERROR, NULL, 0);