File: MountPoints.hsc

package info (click to toggle)
haskell-mountpoints 1.0.2-10
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 120 kB
  • sloc: ansic: 87; haskell: 2; makefile: 2
file content (94 lines) | stat: -rw-r--r-- 2,697 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
-- | 
-- Copyright: 2012 Joey Hess <id@joeyh.name>
-- License: LGPL 2.1 or higher
-- 
-- Derived from hsshellscript, originally written by
-- Volker Wysk <hsss@volker-wysk.de>

{-# LANGUAGE ForeignFunctionInterface, CPP #-}

module System.MountPoints (
	Mntent(..),
	getMounts,
	getProcMounts,
) where

#include "libmounts.h"

import Control.Monad
import Control.Exception
import Data.Maybe
import Control.Applicative
import Foreign
import Foreign.C
import Prelude

-- | This is a stripped down mntent, containing only fields available
-- everywhere.
data Mntent = Mntent
	{ mnt_fsname :: String -- ^ what's mounted
	, mnt_dir :: FilePath  -- ^ where it's mounted
	, mnt_type :: String   -- ^ what sort of filesystem is mounted
	} deriving (Show, Eq, Ord)

-- | Get currently mounted filesystems.
--
-- This uses eiher getmntent or getmntinfo, depending on the OS.
getMounts :: IO [Mntent]
#ifndef linux_android_HOST_OS
getMounts = do
	h <- c_mounts_start
	when (h == nullPtr) $
		throwErrno "getMounts"
	mntent <- getmntent h []
	_ <- c_mounts_end h
	return mntent
  where
	getmntent h c = do
		ptr <- c_mounts_next h
		if ptr == nullPtr
			then return (reverse c)
			else do
				mnt_fsname_str <- #{peek struct mntent, mnt_fsname} ptr >>= peekCString
				mnt_dir_str <- #{peek struct mntent, mnt_dir} ptr >>= peekCString
				mnt_type_str <- #{peek struct mntent, mnt_type} ptr >>= peekCString
				let ent = Mntent
					{ mnt_fsname = mnt_fsname_str
					, mnt_dir = mnt_dir_str
					, mnt_type = mnt_type_str
					}
				getmntent h (ent:c)
#else
getMounts = getProcMounts
#endif

#ifndef linux_android_HOST_OS
-- Using unsafe imports because the C functions are belived to never block.
-- Note that getmntinfo is called with MNT_NOWAIT to avoid possibly blocking;
-- while getmntent only accesses a file in /etc (or /proc) that should not
-- block.
foreign import ccall unsafe "libmounts.h mounts_start" c_mounts_start
        :: IO (Ptr ())
foreign import ccall unsafe "libmounts.h mounts_next" c_mounts_next
        :: Ptr () -> IO (Ptr ())
foreign import ccall unsafe "libmounts.h mounts_end" c_mounts_end
        :: Ptr () -> IO CInt
#endif

-- | Read </proc/mounts> to get currently mounted filesystems.
-- 
-- This works on Linux and related systems, including Android. 

-- Note that on Android, `getMounts` calls this function.
getProcMounts :: IO [Mntent]
getProcMounts = do
	v <- try go :: IO (Either SomeException [Mntent])
	return (either (const []) id v)
  where
	go = mapMaybe (parse . words) . lines <$> readFile "/proc/mounts"
  	parse (device:mountpoint:fstype:_rest) = Just $ Mntent
		{ mnt_fsname = device
		, mnt_dir = mountpoint
		, mnt_type = fstype
		}
	parse _ = Nothing