luci-mod-admin-full: fstab: Add ability to set global options like anonymous mounts
[project/luci.git] / modules / luci-mod-admin-full / luasrc / model / cbi / admin_system / fstab.lua
1 -- Copyright 2008 Steven Barth <steven@midlink.org>
2 -- Licensed to the public under the Apache License 2.0.
3
4 require("luci.tools.webadmin")
5
6 local fs   = require "nixio.fs"
7 local util = require "nixio.util"
8 local tp   = require "luci.template.parser"
9
10 local block = io.popen("block info", "r")
11 local ln, dev, devices = nil, nil, {}
12
13 repeat
14         ln = block:read("*l")
15         dev = ln and ln:match("^/dev/(.-):")
16
17         if dev then
18                 local e, s, key, val = { }
19
20                 for key, val in ln:gmatch([[(%w+)="(.-)"]]) do
21                         e[key:lower()] = val
22                         devices[val] = e
23                 end
24
25                 s = tonumber((fs.readfile("/sys/class/block/%s/size" % dev)))
26
27                 e.dev  = "/dev/%s" % dev
28                 e.size = s and math.floor(s / 2048)
29
30                 devices[e.dev] = e
31         end
32 until not ln
33
34 block:close()
35
36
37 m = Map("fstab", translate("Mount Points"))
38 s = m:section(TypedSection, "global", translate("Global Settings"))
39 s.addremove = false
40 s.anonymous = true
41
42 o = s:option(Flag, "anon_swap", translate("Anonymous Swap"), translate("Mount swap not specifically configured"))
43 o.default = o.disabled
44 o.rmempty = false
45
46 o = s:option(Flag, "anon_mount", translate("Anonymous Mount"), translate("Mount filesystems not specifically configured"))
47 o.default = o.disabled
48 o.rmempty = false
49
50 o = s:option(Flag, "auto_swap", translate("Automount Swap"), translate("Automatically mount swap on hotplug"))
51 o.default = o.enabled
52 o.rmempty = false
53
54 o = s:option(Flag, "auto_mount", translate("Automount Filesystem"), translate("Automatically mount filesystems on hotplug"))
55 o.default = o.enabled
56 o.rmempty = false
57
58 o = s:option(Flag, "check_fs", translate("Check fileystems before mount"), translate("Automatically check filesystem for errors before mounting"))
59 o.default = o.disabled
60 o.rmempty = false
61
62 local mounts = luci.sys.mounts()
63 local non_system_mounts = {}
64 for rawmount, val in pairs(mounts) do
65     if (string.find(val.mountpoint, "/tmp/.jail") == nil) then
66       repeat 
67           val.umount = false
68           if (val.mountpoint == "/") then
69               break
70           elseif (val.mountpoint == "/overlay") then
71               break
72           elseif (val.mountpoint == "/rom") then
73               break
74           elseif (val.mountpoint == "/tmp") then
75               break
76           elseif (val.mountpoint == "/tmp/shm") then
77               break
78           elseif (val.mountpoint == "/tmp/upgrade") then
79               break
80           elseif (val.mountpoint == "/dev") then
81               break
82           end
83           val.umount = true
84       until true
85       non_system_mounts[rawmount] = val       
86    end   
87 end
88
89 v = m:section(Table, non_system_mounts, translate("Mounted file systems"))
90
91 fs = v:option(DummyValue, "fs", translate("Filesystem"))
92
93 mp = v:option(DummyValue, "mountpoint", translate("Mount Point"))
94
95 avail = v:option(DummyValue, "avail", translate("Available"))
96 function avail.cfgvalue(self, section)
97         return luci.tools.webadmin.byte_format(
98                 ( tonumber(mounts[section].available) or 0 ) * 1024
99         ) .. " / " .. luci.tools.webadmin.byte_format(
100                 ( tonumber(mounts[section].blocks) or 0 ) * 1024
101         )
102 end
103
104 used = v:option(DummyValue, "used", translate("Used"))
105 function used.cfgvalue(self, section)
106         return ( mounts[section].percent or "0%" ) .. " (" ..
107         luci.tools.webadmin.byte_format(
108                 ( tonumber(mounts[section].used) or 0 ) * 1024
109         ) .. ")"
110 end
111
112 unmount = v:option(Button, "unmount", translate("Unmount"))
113 unmount.render = function(self, section, scope)
114         if non_system_mounts[section].umount then
115                 self.title = translate("Unmount")
116                 self.inputstyle = "remove"
117                 Button.render(self, section, scope)
118         end
119 end
120
121 unmount.write = function(self, section)
122         if non_system_mounts[section].umount then
123                 luci.sys.call("/bin/umount '%s'" % luci.util.shellstartsqescape(non_system_mounts[section].mountpoint))
124                 return luci.http.redirect(luci.dispatcher.build_url("admin/system", "fstab"))
125         end
126 end
127
128 mount = m:section(TypedSection, "mount", translate("Mount Points"), translate("Mount Points define at which point a memory device will be attached to the filesystem"))
129 mount.anonymous = true
130 mount.addremove = true
131 mount.template = "cbi/tblsection"
132 mount.extedit  = luci.dispatcher.build_url("admin/system/fstab/mount/%s")
133
134 mount.create = function(...)
135         local sid = TypedSection.create(...)
136         if sid then
137                 luci.http.redirect(mount.extedit % sid)
138                 return
139         end
140 end
141
142
143 mount:option(Flag, "enabled", translate("Enabled")).rmempty = false
144
145 dev = mount:option(DummyValue, "device", translate("Device"))
146 dev.rawhtml = true
147 dev.cfgvalue = function(self, section)
148         local v, e
149
150         v = m.uci:get("fstab", section, "uuid")
151         e = v and devices[v:lower()]
152         if v and e and e.size then
153                 return "UUID: %s (%s, %d MB)" %{ tp.pcdata(v), e.dev, e.size }
154         elseif v and e then
155                 return "UUID: %s (%s)" %{ tp.pcdata(v), e.dev }
156         elseif v then
157                 return "UUID: %s (<em>%s</em>)" %{ tp.pcdata(v), translate("not present") }
158         end
159
160         v = m.uci:get("fstab", section, "label")
161         e = v and devices[v]
162         if v and e and e.size then
163                 return "Label: %s (%s, %d MB)" %{ tp.pcdata(v), e.dev, e.size }
164         elseif v and e then
165                 return "Label: %s (%s)" %{ tp.pcdata(v), e.dev }
166         elseif v then
167                 return "Label: %s (<em>%s</em>)" %{ tp.pcdata(v), translate("not present") }
168         end
169
170         v = Value.cfgvalue(self, section) or "?"
171         e = v and devices[v]
172         if v and e and e.size then
173                 return "%s (%d MB)" %{ tp.pcdata(v), e.size }
174         elseif v and e then
175                 return tp.pcdata(v)
176         elseif v then
177                 return "%s (<em>%s</em>)" %{ tp.pcdata(v), translate("not present") }
178         end
179 end
180
181 mp = mount:option(DummyValue, "target", translate("Mount Point"))
182 mp.cfgvalue = function(self, section)
183         if m.uci:get("fstab", section, "is_rootfs") == "1" then
184                 return "/overlay"
185         else
186                 return Value.cfgvalue(self, section) or "?"
187         end
188 end
189
190 fs = mount:option(DummyValue, "fstype", translate("Filesystem"))
191 fs.cfgvalue = function(self, section)
192         local v, e
193
194         v = m.uci:get("fstab", section, "uuid")
195         v = v and v:lower() or m.uci:get("fstab", section, "label")
196         v = v or m.uci:get("fstab", section, "device")
197
198         e = v and devices[v]
199
200         return e and e.type or m.uci:get("fstab", section, "fstype") or "?"
201 end
202
203 op = mount:option(DummyValue, "options", translate("Options"))
204 op.cfgvalue = function(self, section)
205         return Value.cfgvalue(self, section) or "defaults"
206 end
207
208 rf = mount:option(DummyValue, "is_rootfs", translate("Root"))
209 rf.cfgvalue = function(self, section)
210         local target = m.uci:get("fstab", section, "target")
211         if target == "/" then
212                 return translate("yes")
213         elseif target == "/overlay" then
214                 return translate("overlay")
215         else
216                 return translate("no")
217         end
218 end
219
220 ck = mount:option(DummyValue, "enabled_fsck", translate("Check"))
221 ck.cfgvalue = function(self, section)
222         return Value.cfgvalue(self, section) == "1"
223                 and translate("yes") or translate("no")
224 end
225
226
227 swap = m:section(TypedSection, "swap", "SWAP", translate("If your physical memory is insufficient unused data can be temporarily swapped to a swap-device resulting in a higher amount of usable <abbr title=\"Random Access Memory\">RAM</abbr>. Be aware that swapping data is a very slow process as the swap-device cannot be accessed with the high datarates of the <abbr title=\"Random Access Memory\">RAM</abbr>."))
228 swap.anonymous = true
229 swap.addremove = true
230 swap.template = "cbi/tblsection"
231 swap.extedit  = luci.dispatcher.build_url("admin/system/fstab/swap/%s")
232
233 swap.create = function(...)
234         local sid = TypedSection.create(...)
235         if sid then
236                 luci.http.redirect(swap.extedit % sid)
237                 return
238         end
239 end
240
241
242 swap:option(Flag, "enabled", translate("Enabled")).rmempty = false
243
244 dev = swap:option(DummyValue, "device", translate("Device"))
245 dev.cfgvalue = function(self, section)
246         local v
247
248         v = m.uci:get("fstab", section, "uuid")
249         if v then return "UUID: %s" % v end
250
251         v = m.uci:get("fstab", section, "label")
252         if v then return "Label: %s" % v end
253
254         v = Value.cfgvalue(self, section) or "?"
255         e = v and devices[v]
256         if v and e and e.size then
257                 return "%s (%s MB)" % {v, e.size}
258         else
259                 return v
260         end
261 end
262
263 return m