1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 *
3 * Copyright (C) 2008 David Zeuthen <zeuthen@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program 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
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 */
20
21 #include "config.h"
22 #include <glib/gi18n-lib.h>
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <signal.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <sys/sysmacros.h>
32 #include <mntent.h>
33
34 #include <glib.h>
35 #include <glib-object.h>
36
37 #include "udiskslogging.h"
38 #include "udisksmountmonitor.h"
39 #include "udisksmount.h"
40 #include "udisksprivate.h"
41 #include "udisksdaemonutil.h"
42
43 /* build a %Ns format string macro with N == PATH_MAX */
44 #define xstr(s) str(s)
45 #define str(s) #s
46 #define PATH_MAX_FMT "%" xstr(PATH_MAX) "s"
47
48 /**
49 * SECTION:udisksmountmonitor
50 * @title: UDisksMountMonitor
51 * @short_description: Monitors mounted filesystems or in-use swap devices
52 *
53 * This type is used for monitoring mounted devices and swap devices
54 * in use. On Linux, this is done by inspecting and monitoring the
55 * <literal>/proc/self/mountinfo</literal> and
56 * <literal>/proc/swaps</literal> files.
57 */
58
59 /**
60 * UDisksMountMonitor:
61 *
62 * The #UDisksMountMonitor structure contains only private data and
63 * should only be accessed using the provided API.
64 */
65 struct _UDisksMountMonitor
66 {
67 GObject parent_instance;
68
69 GIOChannel *mounts_channel;
70 GSource *mounts_watch_source;
71
72 GIOChannel *swaps_channel;
73 GSource *swaps_watch_source;
74
75 GList *mounts;
76 GList *old_mounts;
77 GMutex mounts_mutex;
78
79 gchar *mountinfo_checksum;
80 gchar *swaps_checksum;
81
82 GMainContext *monitor_context;
83 };
84
85 typedef struct _UDisksMountMonitorClass UDisksMountMonitorClass;
86
87 struct _UDisksMountMonitorClass
88 {
89 GObjectClass parent_class;
90
91 void (*mount_added) (UDisksMountMonitor *monitor,
92 UDisksMount *mount);
93 void (*mount_removed) (UDisksMountMonitor *monitor,
94 UDisksMount *mount);
95 };
96
97 /*--------------------------------------------------------------------------------------------------------------*/
98
99 enum
100 {
101 MOUNT_ADDED_SIGNAL,
102 MOUNT_REMOVED_SIGNAL,
103 LAST_SIGNAL,
104 };
105
106 static guint signals[LAST_SIGNAL] = { 0 };
107
108 G_DEFINE_TYPE (UDisksMountMonitor, udisks_mount_monitor, G_TYPE_OBJECT)
109
110 static void udisks_mount_monitor_ensure (UDisksMountMonitor *monitor);
111 static void udisks_mount_monitor_constructed (GObject *object);
112
113 static void
udisks_mount_monitor_finalize(GObject * object)114 udisks_mount_monitor_finalize (GObject *object)
115 {
116 UDisksMountMonitor *monitor = UDISKS_MOUNT_MONITOR (object);
117
118 if (monitor->mounts_channel != NULL)
119 g_io_channel_unref (monitor->mounts_channel);
120 if (monitor->mounts_watch_source != NULL)
121 g_source_destroy (monitor->mounts_watch_source);
122
123 if (monitor->swaps_channel != NULL)
124 g_io_channel_unref (monitor->swaps_channel);
125 if (monitor->swaps_watch_source != NULL)
126 g_source_destroy (monitor->swaps_watch_source);
127
128 if (monitor->monitor_context != NULL)
129 g_main_context_unref (monitor->monitor_context);
130
131 g_list_free_full (monitor->mounts, g_object_unref);
132 g_list_free_full (monitor->old_mounts, g_object_unref);
133
134 g_free (monitor->mountinfo_checksum);
135 g_free (monitor->swaps_checksum);
136
137 g_mutex_clear (&monitor->mounts_mutex);
138
139 if (G_OBJECT_CLASS (udisks_mount_monitor_parent_class)->finalize != NULL)
140 G_OBJECT_CLASS (udisks_mount_monitor_parent_class)->finalize (object);
141 }
142
143 static void
udisks_mount_monitor_init(UDisksMountMonitor * monitor)144 udisks_mount_monitor_init (UDisksMountMonitor *monitor)
145 {
146 monitor->mounts = NULL;
147 monitor->old_mounts = NULL;
148 g_mutex_init (&monitor->mounts_mutex);
149 }
150
151 static void
udisks_mount_monitor_class_init(UDisksMountMonitorClass * klass)152 udisks_mount_monitor_class_init (UDisksMountMonitorClass *klass)
153 {
154 GObjectClass *gobject_class = (GObjectClass *) klass;
155
156 gobject_class->finalize = udisks_mount_monitor_finalize;
157 gobject_class->constructed = udisks_mount_monitor_constructed;
158
159 /**
160 * UDisksMountMonitor::mount-added
161 * @monitor: A #UDisksMountMonitor.
162 * @mount: The #UDisksMount that was added.
163 *
164 * Emitted when a mount is added.
165 *
166 * This signal is emitted in the
167 * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
168 * that @monitor was created in.
169 */
170 signals[MOUNT_ADDED_SIGNAL] = g_signal_new ("mount-added",
171 G_OBJECT_CLASS_TYPE (klass),
172 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
173 G_STRUCT_OFFSET (UDisksMountMonitorClass, mount_added),
174 NULL,
175 NULL,
176 g_cclosure_marshal_VOID__OBJECT,
177 G_TYPE_NONE,
178 1,
179 UDISKS_TYPE_MOUNT);
180
181 /**
182 * UDisksMountMonitor::mount-removed
183 * @monitor: A #UDisksMountMonitor.
184 * @mount: The #UDisksMount that was removed.
185 *
186 * Emitted when a mount is removed.
187 *
188 * This signal is emitted in the
189 * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
190 * that @monitor was created in.
191 */
192 signals[MOUNT_REMOVED_SIGNAL] = g_signal_new ("mount-removed",
193 G_OBJECT_CLASS_TYPE (klass),
194 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
195 G_STRUCT_OFFSET (UDisksMountMonitorClass, mount_removed),
196 NULL,
197 NULL,
198 g_cclosure_marshal_VOID__OBJECT,
199 G_TYPE_NONE,
200 1,
201 UDISKS_TYPE_MOUNT);
202 }
203
204 static void
diff_sorted_lists(GList * list1,GList * list2,GCompareFunc compare,GList ** added,GList ** removed)205 diff_sorted_lists (GList *list1,
206 GList *list2,
207 GCompareFunc compare,
208 GList **added,
209 GList **removed)
210 {
211 int order;
212
213 *added = *removed = NULL;
214
215 while (list1 != NULL && list2 != NULL)
216 {
217 order = (*compare) (list1->data, list2->data);
218 if (order < 0)
219 {
220 *removed = g_list_prepend (*removed, list1->data);
221 list1 = list1->next;
222 }
223 else if (order > 0)
224 {
225 *added = g_list_prepend (*added, list2->data);
226 list2 = list2->next;
227 }
228 else
229 { /* same item */
230 list1 = list1->next;
231 list2 = list2->next;
232 }
233 }
234
235 while (list1 != NULL)
236 {
237 *removed = g_list_prepend (*removed, list1->data);
238 list1 = list1->next;
239 }
240 while (list2 != NULL)
241 {
242 *added = g_list_prepend (*added, list2->data);
243 list2 = list2->next;
244 }
245 }
246
247 static void
reload_mounts(UDisksMountMonitor * monitor)248 reload_mounts (UDisksMountMonitor *monitor)
249 {
250 GList *cur_mounts;
251 GList *added;
252 GList *removed;
253 GList *l;
254
255 udisks_mount_monitor_ensure (monitor);
256
257 g_mutex_lock (&monitor->mounts_mutex);
258 cur_mounts = g_list_copy_deep (monitor->mounts, (GCopyFunc) udisks_g_object_ref_copy, NULL);
259 g_mutex_unlock (&monitor->mounts_mutex);
260
261 /* no need to lock monitor->old_mounts as reload_mounts() should
262 * always be called from monitor->monitor_context. */
263 cur_mounts = g_list_sort (cur_mounts, (GCompareFunc) udisks_mount_compare);
264 diff_sorted_lists (monitor->old_mounts, cur_mounts, (GCompareFunc) udisks_mount_compare, &added, &removed);
265
266 for (l = removed; l != NULL; l = l->next)
267 {
268 UDisksMount *mount = UDISKS_MOUNT (l->data);
269 g_signal_emit (monitor, signals[MOUNT_REMOVED_SIGNAL], 0, mount);
270 }
271
272 for (l = added; l != NULL; l = l->next)
273 {
274 UDisksMount *mount = UDISKS_MOUNT (l->data);
275 g_signal_emit (monitor, signals[MOUNT_ADDED_SIGNAL], 0, mount);
276 }
277
278 g_list_free_full (monitor->old_mounts, g_object_unref);
279 monitor->old_mounts = cur_mounts;
280
281 g_list_free (removed);
282 g_list_free (added);
283 }
284
285 static gboolean
mounts_changed_event(GIOChannel * channel,GIOCondition cond,gpointer user_data)286 mounts_changed_event (GIOChannel *channel,
287 GIOCondition cond,
288 gpointer user_data)
289 {
290 UDisksMountMonitor *monitor = UDISKS_MOUNT_MONITOR (user_data);
291 if (cond & ~G_IO_ERR)
292 goto out;
293 reload_mounts (monitor);
294 out:
295 return TRUE;
296 }
297
298 static gboolean
swaps_changed_event(GIOChannel * channel,GIOCondition cond,gpointer user_data)299 swaps_changed_event (GIOChannel *channel,
300 GIOCondition cond,
301 gpointer user_data)
302 {
303 UDisksMountMonitor *monitor = UDISKS_MOUNT_MONITOR (user_data);
304 if (cond & ~G_IO_ERR)
305 goto out;
306 reload_mounts (monitor);
307 out:
308 return TRUE;
309 }
310
311 static gboolean
mounts_changed_idle_cb(gpointer user_data)312 mounts_changed_idle_cb (gpointer user_data)
313 {
314 UDisksMountMonitor *monitor = UDISKS_MOUNT_MONITOR (user_data);
315
316 reload_mounts (monitor);
317
318 /* remove the source */
319 return FALSE;
320 }
321
322 static void
udisks_mount_monitor_constructed(GObject * object)323 udisks_mount_monitor_constructed (GObject *object)
324 {
325 UDisksMountMonitor *monitor = UDISKS_MOUNT_MONITOR (object);
326 GError *error;
327
328 monitor->monitor_context = g_main_context_ref_thread_default ();
329
330 /* fetch initial data */
331 udisks_mount_monitor_ensure (monitor);
332
333 error = NULL;
334 monitor->mounts_channel = g_io_channel_new_file ("/proc/self/mountinfo", "r", &error);
335 if (monitor->mounts_channel != NULL)
336 {
337 monitor->mounts_watch_source = g_io_create_watch (monitor->mounts_channel, G_IO_ERR);
338 #if __GNUC__ >= 8
339 #pragma GCC diagnostic push
340 #pragma GCC diagnostic ignored "-Wcast-function-type"
341 #endif
342 /* parameters of the callback depend on the source and can be different
343 * from the required "generic" GSourceFunc, see:
344 * https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-source-set-callback
345 */
346 g_source_set_callback (monitor->mounts_watch_source, (GSourceFunc) mounts_changed_event, monitor, NULL);
347 #if __GNUC__ >= 8
348 #pragma GCC diagnostic pop
349 #endif
350 g_source_attach (monitor->mounts_watch_source, monitor->monitor_context);
351 g_source_unref (monitor->mounts_watch_source);
352 }
353 else
354 {
355 g_error ("No /proc/self/mountinfo file: %s", error->message);
356 g_clear_error (&error);
357 }
358
359 error = NULL;
360 monitor->swaps_channel = g_io_channel_new_file ("/proc/swaps", "r", &error);
361 if (monitor->swaps_channel != NULL)
362 {
363 monitor->swaps_watch_source = g_io_create_watch (monitor->swaps_channel, G_IO_ERR);
364 #if __GNUC__ >= 8
365 #pragma GCC diagnostic push
366 #pragma GCC diagnostic ignored "-Wcast-function-type"
367 #endif
368 /* parameters of the callback depend on the source and can be different
369 * from the required "generic" GSourceFunc, see:
370 * https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-source-set-callback
371 */
372 g_source_set_callback (monitor->swaps_watch_source, (GSourceFunc) swaps_changed_event, monitor, NULL);
373 #if __GNUC__ >= 8
374 #pragma GCC diagnostic pop
375 #endif
376 g_source_attach (monitor->swaps_watch_source, monitor->monitor_context);
377 g_source_unref (monitor->swaps_watch_source);
378 }
379 else
380 {
381 if (!(error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT))
382 {
383 udisks_warning ("Error opening /proc/swaps file: %s (%s, %d)",
384 error->message, g_quark_to_string (error->domain), error->code);
385 }
386 g_clear_error (&error);
387 }
388
389 if (G_OBJECT_CLASS (udisks_mount_monitor_parent_class)->constructed != NULL)
390 (*G_OBJECT_CLASS (udisks_mount_monitor_parent_class)->constructed) (object);
391 }
392
393 /**
394 * udisks_mount_monitor_new:
395 *
396 * Creates a new #UDisksMountMonitor object.
397 *
398 * Signals are emitted in the <link
399 * linkend="g-main-context-push-thread-default">thread-default main
400 * loop</link> that this function is called from.
401 *
402 * Returns: A #UDisksMountMonitor. Free with g_object_unref().
403 */
404 UDisksMountMonitor *
udisks_mount_monitor_new(void)405 udisks_mount_monitor_new (void)
406 {
407 return UDISKS_MOUNT_MONITOR (g_object_new (UDISKS_TYPE_MOUNT_MONITOR, NULL));
408 }
409
410 static gboolean
have_mount(UDisksMountMonitor * monitor,dev_t dev,const gchar * mount_point)411 have_mount (UDisksMountMonitor *monitor,
412 dev_t dev,
413 const gchar *mount_point)
414 {
415 GList *l;
416 gboolean ret;
417
418 ret = FALSE;
419
420 for (l = monitor->mounts; l != NULL; l = l->next)
421 {
422 UDisksMount *mount = UDISKS_MOUNT (l->data);
423 if (udisks_mount_get_dev (mount) == dev &&
424 g_strcmp0 (udisks_mount_get_mount_path (mount), mount_point) == 0)
425 {
426 ret = TRUE;
427 break;
428 }
429 }
430
431 return ret;
432 }
433
434 /* ---------------------------------------------------------------------------------------------------- */
435
436 static gboolean
udisks_mount_monitor_read_mountinfo(gchar ** contents,gsize * length)437 udisks_mount_monitor_read_mountinfo (gchar **contents,
438 gsize *length)
439 {
440 GError *error = NULL;
441
442 if (!g_file_get_contents ("/proc/self/mountinfo", contents, length, &error))
443 {
444 udisks_warning ("Error reading /proc/self/mountinfo: %s (%s, %d)",
445 error->message, g_quark_to_string (error->domain), error->code);
446 g_clear_error (&error);
447 return FALSE;
448 }
449
450 return TRUE;
451 }
452
453 static void
udisks_mount_monitor_parse_mountinfo(UDisksMountMonitor * monitor,const gchar * contents)454 udisks_mount_monitor_parse_mountinfo (UDisksMountMonitor *monitor,
455 const gchar *contents)
456 {
457 gchar **lines;
458 guint n;
459
460 /* See Documentation/filesystems/proc.txt for the format of /proc/self/mountinfo
461 *
462 * Note that things like space are encoded as \020.
463 */
464 if (contents == NULL)
465 return;
466
467 lines = g_strsplit (contents, "\n", 0);
468 for (n = 0; lines[n] != NULL; n++)
469 {
470 guint mount_id;
471 guint parent_id;
472 guint major, minor;
473 gchar encoded_root[PATH_MAX + 1];
474 gchar encoded_mount_point[PATH_MAX + 1];
475 gchar *mount_point;
476 dev_t dev;
477
478 if (strlen (lines[n]) == 0)
479 continue;
480
481 if (sscanf (lines[n],
482 "%u %u %u:%u " PATH_MAX_FMT " " PATH_MAX_FMT,
483 &mount_id,
484 &parent_id,
485 &major,
486 &minor,
487 encoded_root,
488 encoded_mount_point) != 6)
489 {
490 udisks_warning ("Error parsing line '%s'", lines[n]);
491 continue;
492 }
493 encoded_root[sizeof encoded_root - 1] = '\0';
494 encoded_mount_point[sizeof encoded_mount_point - 1] = '\0';
495
496 /* Temporary work-around for btrfs, see
497 *
498 * https://bugzilla.redhat.com/show_bug.cgi?id=495152#c31
499 * http://article.gmane.org/gmane.comp.file-systems.btrfs/2851
500 *
501 * for details.
502 */
503 if (major == 0)
504 {
505 const gchar *sep;
506 sep = strstr (lines[n], " - ");
507 if (sep != NULL)
508 {
509 gchar fstype[PATH_MAX + 1];
510 gchar mount_source[PATH_MAX + 1];
511 struct stat statbuf;
512
513 if (sscanf (sep + 3, PATH_MAX_FMT " " PATH_MAX_FMT, fstype, mount_source) != 2)
514 {
515 udisks_warning ("Error parsing things past - for '%s'", lines[n]);
516 continue;
517 }
518 fstype[sizeof fstype - 1] = '\0';
519 mount_source[sizeof mount_source - 1] = '\0';
520
521 if (g_strcmp0 (fstype, "btrfs") != 0)
522 continue;
523
524 if (!g_str_has_prefix (mount_source, "/dev/"))
525 continue;
526
527 if (stat (mount_source, &statbuf) != 0)
528 {
529 udisks_warning ("Error statting %s: %m", mount_source);
530 continue;
531 }
532
533 if (!S_ISBLK (statbuf.st_mode))
534 {
535 udisks_warning ("%s is not a block device", mount_source);
536 continue;
537 }
538
539 dev = statbuf.st_rdev;
540 }
541 else
542 {
543 continue;
544 }
545 }
546 else
547 {
548 dev = makedev (major, minor);
549 }
550
551 mount_point = g_strcompress (encoded_mount_point);
552
553 /* TODO: we can probably use a hash table or something if this turns out to be slow */
554 if (!have_mount (monitor, dev, mount_point))
555 {
556 UDisksMount *mount;
557 mount = _udisks_mount_new (dev, mount_point, UDISKS_MOUNT_TYPE_FILESYSTEM);
558 monitor->mounts = g_list_prepend (monitor->mounts, mount);
559 }
560
561 g_free (mount_point);
562 }
563 g_strfreev (lines);
564 }
565
566 /* ---------------------------------------------------------------------------------------------------- */
567
568 static gboolean
udisks_mount_monitor_read_swaps(gchar ** contents,gsize * length)569 udisks_mount_monitor_read_swaps (gchar **contents,
570 gsize *length)
571 {
572 GError *error = NULL;
573
574 if (!g_file_get_contents ("/proc/swaps", contents, length, &error))
575 {
576 if (g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
577 {
578 g_clear_error (&error);
579 return TRUE;
580 }
581 else
582 {
583 udisks_warning ("Error reading /proc/swaps: %s (%s, %d)",
584 error->message, g_quark_to_string (error->domain), error->code);
585 g_clear_error (&error);
586 return FALSE;
587 }
588 }
589
590 return TRUE;
591 }
592
593 static void
udisks_mount_monitor_parse_swaps(UDisksMountMonitor * monitor,const gchar * contents)594 udisks_mount_monitor_parse_swaps (UDisksMountMonitor *monitor,
595 const gchar *contents)
596 {
597 gchar **lines;
598 guint n;
599
600 if (contents == NULL)
601 return;
602
603 lines = g_strsplit (contents, "\n", 0);
604 for (n = 0; lines[n] != NULL; n++)
605 {
606 gchar filename[PATH_MAX + 1];
607 struct stat statbuf;
608 dev_t dev;
609
610 /* skip first line of explanatory text */
611 if (n == 0)
612 continue;
613
614 if (strlen (lines[n]) == 0)
615 continue;
616
617 if (sscanf (lines[n], PATH_MAX_FMT, filename) != 1)
618 {
619 udisks_warning ("Error parsing line '%s'", lines[n]);
620 continue;
621 }
622 filename[sizeof filename - 1] = '\0';
623
624 if (stat (filename, &statbuf) != 0)
625 {
626 udisks_warning ("Error statting %s: %m", filename);
627 continue;
628 }
629
630 dev = statbuf.st_rdev;
631
632 if (!have_mount (monitor, dev, NULL))
633 {
634 UDisksMount *mount;
635 mount = _udisks_mount_new (dev, NULL, UDISKS_MOUNT_TYPE_SWAP);
636 monitor->mounts = g_list_prepend (monitor->mounts, mount);
637 }
638 }
639 g_strfreev (lines);
640 }
641
642 /* ---------------------------------------------------------------------------------------------------- */
643
644 static void
udisks_mount_monitor_ensure(UDisksMountMonitor * monitor)645 udisks_mount_monitor_ensure (UDisksMountMonitor *monitor)
646 {
647 gchar *mountinfo_contents = NULL;
648 gchar *swaps_contents = NULL;
649 gsize mountinfo_length = 0;
650 gsize swaps_length = 0;
651 gchar *mountinfo_checksum = NULL;
652 gchar *swaps_checksum = NULL;
653 GSource *idle_source;
654 gboolean have_mountinfo;
655 gboolean have_swaps;
656
657 g_mutex_lock (&monitor->mounts_mutex);
658
659 have_mountinfo = udisks_mount_monitor_read_mountinfo (&mountinfo_contents, &mountinfo_length);
660 have_swaps = udisks_mount_monitor_read_swaps (&swaps_contents, &swaps_length);
661 if (have_mountinfo || have_swaps)
662 {
663 /* compute contents checksums and compare them against current cache */
664 if (mountinfo_contents)
665 mountinfo_checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA1,
666 (const guchar *) mountinfo_contents,
667 mountinfo_length);
668 if (swaps_contents)
669 swaps_checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA1,
670 (const guchar *) swaps_contents,
671 swaps_length);
672 if (g_strcmp0 (mountinfo_checksum, monitor->mountinfo_checksum) != 0 ||
673 g_strcmp0 (swaps_checksum, monitor->swaps_checksum) != 0)
674 {
675 g_list_free_full (monitor->mounts, g_object_unref);
676 monitor->mounts = NULL;
677
678 udisks_mount_monitor_parse_mountinfo (monitor, mountinfo_contents);
679 udisks_mount_monitor_parse_swaps (monitor, swaps_contents);
680
681 /* save current checksums */
682 g_free (monitor->mountinfo_checksum);
683 g_free (monitor->swaps_checksum);
684 monitor->mountinfo_checksum = g_strdup (mountinfo_checksum);
685 monitor->swaps_checksum = g_strdup (swaps_checksum);
686
687 /* notify about the changes */
688 idle_source = g_idle_source_new ();
689 g_source_set_priority (idle_source, G_PRIORITY_DEFAULT_IDLE);
690 g_source_set_callback (idle_source, (GSourceFunc) mounts_changed_idle_cb, monitor, NULL);
691 g_source_attach (idle_source, monitor->monitor_context);
692 g_source_unref (idle_source);
693 }
694
695 g_free (mountinfo_checksum);
696 g_free (swaps_checksum);
697 }
698 g_free (mountinfo_contents);
699 g_free (swaps_contents);
700
701 g_mutex_unlock (&monitor->mounts_mutex);
702 }
703
704 /**
705 * udisks_mount_monitor_get_mounts_for_dev:
706 * @monitor: A #UDisksMountMonitor.
707 * @dev: A #dev_t device number.
708 *
709 * Gets all #UDisksMount objects for @dev.
710 *
711 * Returns: A #GList of #UDisksMount objects. The returned list must
712 * be freed with g_list_free() after each element has been freed with
713 * g_object_unref().
714 */
715 GList *
udisks_mount_monitor_get_mounts_for_dev(UDisksMountMonitor * monitor,dev_t dev)716 udisks_mount_monitor_get_mounts_for_dev (UDisksMountMonitor *monitor,
717 dev_t dev)
718 {
719 GList *ret;
720 GList *l;
721
722 ret = NULL;
723
724 udisks_mount_monitor_ensure (monitor);
725
726 g_mutex_lock (&monitor->mounts_mutex);
727
728 for (l = monitor->mounts; l != NULL; l = l->next)
729 {
730 UDisksMount *mount = UDISKS_MOUNT (l->data);
731
732 if (udisks_mount_get_dev (mount) == dev)
733 {
734 ret = g_list_prepend (ret, g_object_ref (mount));
735 }
736 }
737
738 g_mutex_unlock (&monitor->mounts_mutex);
739
740 /* Sort the list to ensure that shortest mount paths appear first */
741 ret = g_list_sort (ret, (GCompareFunc) udisks_mount_compare);
742
743 return ret;
744 }
745
746 /**
747 * udisks_mount_monitor_is_dev_in_use:
748 * @monitor: A #UDisksMountMonitor.
749 * @dev: A #dev_t device number.
750 * @out_type: (out allow-none): Return location for mount type, if in use or %NULL.
751 *
752 * Checks if @dev is in use (e.g. mounted or swap-area in-use).
753 *
754 * Returns: %TRUE if in use, %FALSE otherwise.
755 */
756 gboolean
udisks_mount_monitor_is_dev_in_use(UDisksMountMonitor * monitor,dev_t dev,UDisksMountType * out_type)757 udisks_mount_monitor_is_dev_in_use (UDisksMountMonitor *monitor,
758 dev_t dev,
759 UDisksMountType *out_type)
760 {
761 gboolean ret;
762 GList *l;
763
764 ret = FALSE;
765 udisks_mount_monitor_ensure (monitor);
766
767 g_mutex_lock (&monitor->mounts_mutex);
768
769 for (l = monitor->mounts; l != NULL; l = l->next)
770 {
771 UDisksMount *mount = UDISKS_MOUNT (l->data);
772
773 if (udisks_mount_get_dev (mount) == dev)
774 {
775 if (out_type != NULL)
776 *out_type = udisks_mount_get_mount_type (mount);
777 ret = TRUE;
778 goto out;
779 }
780 }
781
782 out:
783 g_mutex_unlock (&monitor->mounts_mutex);
784 return ret;
785 }
786
787 /**
788 * udisks_mount_monitor_get_mount_for_path:
789 * @monitor: A #UDisksMountMonitor.
790 * @mount_path: A filesystem path on which a device may be mounted.
791 *
792 * Gets the #UDisksMount mounted at @mount_path, if any.
793 *
794 * Returns: (transfer full) (nullable): the #UDisksMount (of type
795 * #UDISKS_MOUNT_TYPE_FILESYSTEM) mounted at @mount_path, or %NULL if nothing
796 * is mounted there.
797 */
798 UDisksMount *
udisks_mount_monitor_get_mount_for_path(UDisksMountMonitor * monitor,const gchar * mount_path)799 udisks_mount_monitor_get_mount_for_path (UDisksMountMonitor *monitor,
800 const gchar *mount_path)
801 {
802 GList *l;
803
804 g_return_val_if_fail (UDISKS_IS_MOUNT_MONITOR (monitor), NULL);
805 g_return_val_if_fail (mount_path != NULL, NULL);
806
807 udisks_mount_monitor_ensure (monitor);
808
809 g_mutex_lock (&monitor->mounts_mutex);
810
811 for (l = monitor->mounts; l != NULL; l = l->next)
812 {
813 UDisksMount *mount = UDISKS_MOUNT (l->data);
814
815 if (udisks_mount_get_mount_type (mount) == UDISKS_MOUNT_TYPE_FILESYSTEM &&
816 g_strcmp0 (udisks_mount_get_mount_path (mount), mount_path) == 0)
817 {
818 g_object_ref (mount);
819 g_mutex_unlock (&monitor->mounts_mutex);
820 return mount;
821 }
822 }
823
824 g_mutex_unlock (&monitor->mounts_mutex);
825 return NULL;
826 }
827