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