1 /* GIO - GLib Input, Output and Streaming Library
2 *
3 * Copyright (C) 2006-2007 Red Hat, Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 *
20 * Author: Alexander Larsson <alexl@redhat.com>
21 */
22
23 #include <config.h>
24
25 #include <unistd.h>
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <sys/un.h>
29
30 #include <glib.h>
31 #include <glib/gi18n.h>
32 #include "gvfsjobunmount.h"
33
34 G_DEFINE_TYPE (GVfsJobUnmount, g_vfs_job_unmount, G_VFS_TYPE_JOB_DBUS)
35
36 static void run (GVfsJob *job);
37 static gboolean try (GVfsJob *job);
38 static void send_reply (GVfsJob *job);
39 static void create_reply (GVfsJob *job,
40 GVfsDBusMount *object,
41 GDBusMethodInvocation *invocation);
42
43 static void
g_vfs_job_unmount_finalize(GObject * object)44 g_vfs_job_unmount_finalize (GObject *object)
45 {
46 GVfsJobUnmount *job;
47
48 job = G_VFS_JOB_UNMOUNT (object);
49
50 if (job->mount_source)
51 g_object_unref (job->mount_source);
52
53 if (G_OBJECT_CLASS (g_vfs_job_unmount_parent_class)->finalize)
54 (*G_OBJECT_CLASS (g_vfs_job_unmount_parent_class)->finalize) (object);
55 }
56
57 static void
g_vfs_job_unmount_class_init(GVfsJobUnmountClass * klass)58 g_vfs_job_unmount_class_init (GVfsJobUnmountClass *klass)
59 {
60 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
61 GVfsJobClass *job_class = G_VFS_JOB_CLASS (klass);
62 GVfsJobDBusClass *job_dbus_class = G_VFS_JOB_DBUS_CLASS (klass);
63
64 gobject_class->finalize = g_vfs_job_unmount_finalize;
65 job_class->run = run;
66 job_class->try = try;
67 job_class->send_reply = send_reply;
68
69 job_dbus_class->create_reply = create_reply;
70 }
71
72 static void
g_vfs_job_unmount_init(GVfsJobUnmount * job)73 g_vfs_job_unmount_init (GVfsJobUnmount *job)
74 {
75 }
76
77 gboolean
g_vfs_job_unmount_new_handle(GVfsDBusMount * object,GDBusMethodInvocation * invocation,const gchar * arg_dbus_id,const gchar * arg_obj_path,guint arg_flags,GVfsBackend * backend)78 g_vfs_job_unmount_new_handle (GVfsDBusMount *object,
79 GDBusMethodInvocation *invocation,
80 const gchar *arg_dbus_id,
81 const gchar *arg_obj_path,
82 guint arg_flags,
83 GVfsBackend *backend)
84 {
85 GVfsJobUnmount *job;
86
87 if (g_vfs_backend_invocation_first_handler (object, invocation, backend))
88 return TRUE;
89
90 g_debug ("g_vfs_job_unmount_new request: %p\n", invocation);
91
92 job = g_object_new (G_VFS_TYPE_JOB_UNMOUNT,
93 "object", object,
94 "invocation", invocation,
95 NULL);
96
97 job->backend = backend;
98 job->flags = arg_flags;
99 job->mount_source = g_mount_source_new (arg_dbus_id, arg_obj_path);
100
101 g_vfs_job_source_new_job (G_VFS_JOB_SOURCE (backend), G_VFS_JOB (job));
102 g_object_unref (job);
103
104 return TRUE;
105 }
106
107 static void
unmount_progress_clear(GVfsJobUnmount * op_job)108 unmount_progress_clear (GVfsJobUnmount *op_job)
109 {
110 gchar *message;
111
112 if (op_job->unmount_progress_id > 0)
113 {
114 g_source_remove (op_job->unmount_progress_id);
115 op_job->unmount_progress_id = 0;
116 }
117
118 if (!op_job->unmount_progress_fired)
119 return;
120
121 g_debug ("gvfsjobunmount progress clear\n");
122
123 message = g_strdup_printf (_("%s has been unmounted\n"),
124 g_vfs_backend_get_display_name (op_job->backend));
125 g_mount_source_show_unmount_progress (op_job->mount_source,
126 message, 0, 0);
127 g_free (message);
128 }
129
130 static gboolean
unmount_progress_timeout(gpointer user_data)131 unmount_progress_timeout (gpointer user_data)
132 {
133 GVfsJobUnmount *op_job = user_data;
134 gchar *message;
135
136 op_job->unmount_progress_id = 0;
137 op_job->unmount_progress_fired = TRUE;
138
139 g_debug ("gvfsjobunmount progress timeout reached\n");
140
141 message = g_strdup_printf (_("Unmounting %s\nPlease wait"),
142 g_vfs_backend_get_display_name (op_job->backend));
143 /* TODO: report estimated bytes and time left */
144 g_mount_source_show_unmount_progress (op_job->mount_source,
145 message, -1, -1);
146 g_free (message);
147
148 return FALSE;
149 }
150
151 static void
unmount_progress_start(GVfsJobUnmount * op_job)152 unmount_progress_start (GVfsJobUnmount *op_job)
153 {
154 if (op_job->unmount_progress_id > 0)
155 return;
156
157 g_debug ("gvfsjobunmount progress timeout start\n");
158 op_job->unmount_progress_id = g_timeout_add (1500, unmount_progress_timeout, op_job);
159 }
160
161 static void
run(GVfsJob * job)162 run (GVfsJob *job)
163 {
164 GVfsJobUnmount *op_job = G_VFS_JOB_UNMOUNT (job);
165 GVfsBackendClass *class = G_VFS_BACKEND_GET_CLASS (op_job->backend);
166
167 if (class->unmount == NULL)
168 return;
169
170 unmount_progress_start (op_job);
171
172 class->unmount (op_job->backend,
173 op_job,
174 op_job->flags,
175 op_job->mount_source);
176
177 unmount_progress_clear (op_job);
178 }
179
180 static gboolean
job_finish_immediately_if_possible(GVfsJobUnmount * op_job)181 job_finish_immediately_if_possible (GVfsJobUnmount *op_job)
182 {
183 GVfsBackend *backend = op_job->backend;
184 GVfsBackendClass *class = G_VFS_BACKEND_GET_CLASS (op_job->backend);
185 gboolean is_busy;
186 gboolean force_unmount;
187
188 if (class->try_unmount != NULL || class->unmount != NULL)
189 return FALSE;
190
191 is_busy = g_vfs_daemon_has_blocking_processes (g_vfs_backend_get_daemon (backend));
192 force_unmount = op_job->flags & G_MOUNT_UNMOUNT_FORCE;
193
194 if (is_busy && ! force_unmount)
195 g_vfs_job_failed_literal (G_VFS_JOB (op_job),
196 G_IO_ERROR, G_IO_ERROR_BUSY,
197 _("File system is busy"));
198 else
199 g_vfs_job_succeeded (G_VFS_JOB (op_job));
200
201 return TRUE;
202 }
203
204 static void
unmount_cb(GVfsBackend * backend,GAsyncResult * res,gpointer user_data)205 unmount_cb (GVfsBackend *backend,
206 GAsyncResult *res,
207 gpointer user_data)
208 {
209 GVfsJobUnmount *op_job = G_VFS_JOB_UNMOUNT (user_data);
210 GVfsBackendClass *class = G_VFS_BACKEND_GET_CLASS (op_job->backend);
211 gboolean should_unmount;
212 gboolean finished;
213 GError *error = NULL;
214
215 should_unmount = g_vfs_backend_unmount_with_operation_finish (backend,
216 res,
217 &error);
218 if (!should_unmount)
219 {
220 g_vfs_job_failed_from_error (G_VFS_JOB (op_job), error);
221 g_error_free (error);
222 return;
223 }
224
225 op_job->flags |= G_MOUNT_UNMOUNT_FORCE;
226 finished = job_finish_immediately_if_possible (op_job);
227
228 if (! finished)
229 {
230 gboolean run_in_thread = TRUE;
231
232 if (class->try_unmount != NULL)
233 run_in_thread = ! class->try_unmount (op_job->backend,
234 op_job,
235 op_job->flags,
236 op_job->mount_source);
237
238 if (run_in_thread)
239 {
240 g_vfs_backend_set_block_requests (backend, TRUE);
241 g_vfs_daemon_run_job_in_thread (g_vfs_backend_get_daemon (backend),
242 G_VFS_JOB (op_job));
243 }
244 }
245 }
246
247 static gboolean
try(GVfsJob * job)248 try (GVfsJob *job)
249 {
250 GVfsJobUnmount *op_job = G_VFS_JOB_UNMOUNT (job);
251 GVfsBackend *backend = op_job->backend;
252 GVfsBackendClass *class = G_VFS_BACKEND_GET_CLASS (op_job->backend);
253 gboolean is_busy;
254 gboolean force_unmount;
255
256 is_busy = g_vfs_daemon_has_blocking_processes (g_vfs_backend_get_daemon (backend));
257 force_unmount = op_job->flags & G_MOUNT_UNMOUNT_FORCE;
258
259 if (is_busy && !force_unmount)
260 {
261 if (g_mount_source_is_dummy (op_job->mount_source))
262 g_vfs_job_failed_literal (G_VFS_JOB (op_job),
263 G_IO_ERROR, G_IO_ERROR_BUSY,
264 _("File system is busy"));
265 else
266 g_vfs_backend_unmount_with_operation (backend,
267 op_job->mount_source,
268 (GAsyncReadyCallback)unmount_cb,
269 op_job);
270 return TRUE;
271 }
272
273 if (job_finish_immediately_if_possible (op_job))
274 return TRUE;
275 else if (class->try_unmount != NULL)
276 return class->try_unmount (op_job->backend,
277 op_job,
278 op_job->flags,
279 op_job->mount_source);
280 else
281 {
282 /* We're going to run the backend's unmount method on a thread, block
283 * new jobs coming in. */
284 g_vfs_backend_set_block_requests (backend, TRUE);
285 return FALSE;
286 }
287 }
288
289 static void
unregister_mount_callback(GVfsBackend * backend,GAsyncResult * res,gpointer user_data)290 unregister_mount_callback (GVfsBackend *backend,
291 GAsyncResult *res,
292 gpointer user_data)
293 {
294 GVfsDaemon *daemon;
295 GVfsJobUnmount *op_job = G_VFS_JOB_UNMOUNT (user_data);
296 GError *error = NULL;
297
298 g_debug ("unregister_mount_callback\n");
299 if (!g_vfs_backend_unregister_mount_finish (backend, res, &error))
300 {
301 g_warning ("Error unregistering mount: %s (%s, %d)\n",
302 error->message, g_quark_to_string (error->domain), error->code);
303 g_error_free (error);
304 }
305
306 (*G_VFS_JOB_CLASS (g_vfs_job_unmount_parent_class)->send_reply) (G_VFS_JOB (op_job));
307
308 /* Unlink job source from daemon */
309 daemon = g_vfs_backend_get_daemon (backend);
310 g_vfs_daemon_close_active_channels (daemon, backend);
311 g_vfs_job_source_closed (G_VFS_JOB_SOURCE (backend));
312
313 }
314
315 /* Might be called on an i/o thread */
316 static void
send_reply(GVfsJob * job)317 send_reply (GVfsJob *job)
318 {
319 GVfsJobUnmount *op_job = G_VFS_JOB_UNMOUNT (job);
320 GVfsBackend *backend = op_job->backend;
321
322 if (job->failed)
323 {
324 g_vfs_backend_set_block_requests (backend, FALSE);
325 (*G_VFS_JOB_CLASS (g_vfs_job_unmount_parent_class)->send_reply) (G_VFS_JOB (op_job));
326 }
327 else
328 {
329 /* Setting the backend to block requests will also
330 set active GVfsChannels to block requets */
331 g_vfs_backend_set_block_requests (backend, TRUE);
332 g_vfs_backend_unregister_mount (backend,
333 (GAsyncReadyCallback) unregister_mount_callback,
334 job);
335 }
336 }
337
338 /* Might be called on an i/o thread */
339 static void
create_reply(GVfsJob * job,GVfsDBusMount * object,GDBusMethodInvocation * invocation)340 create_reply (GVfsJob *job,
341 GVfsDBusMount *object,
342 GDBusMethodInvocation *invocation)
343 {
344 gvfs_dbus_mount_complete_unmount (object, invocation);
345 }
346