luci-app-ocserv: uclibc's crypt() doesn't support sha2crypt
[project/luci.git] / applications / luci-app-ddns / luasrc / controller / ddns.lua
1 -- Copyright 2008 Steven Barth <steven@midlink.org>
2 -- Copyright 2008 Jo-Philipp Wich <jow@openwrt.org>
3 -- Copyright 2013 Manuel Munz <freifunk at somakoma dot de>
4 -- Copyright 2014 Christian Schoenebeck <christian dot schoenebeck at gmail dot com>
5 -- Licensed to the public under the Apache License 2.0.
6
7 module("luci.controller.ddns", package.seeall)
8
9 local NX   = require "nixio"
10 local NXFS = require "nixio.fs"
11 local DISP = require "luci.dispatcher"
12 local HTTP = require "luci.http"
13 local UCI  = require "luci.model.uci"
14 local SYS  = require "luci.sys"
15 local DDNS = require "luci.tools.ddns"          -- ddns multiused functions
16 local UTIL = require "luci.util"
17
18 local luci_ddns_version = "2.1.0-4"     -- luci-app-ddns / openwrt Makefile compatible version
19 local ddns_scripts_min  = "2.1.0-3"     -- minimum version of ddns-scripts required
20
21 function index()
22         -- no services_ipv6 file or no dynamic_dns_lucihelper.sh
23         -- do NOT start
24         if not nixio.fs.access("/usr/lib/ddns/services_ipv6") 
25         or not nixio.fs.access("/usr/lib/ddns/dynamic_dns_lucihelper.sh") then
26                 return
27         end
28         -- no config create an empty one
29         if not nixio.fs.access("/etc/config/ddns") then
30                 nixio.fs.writefile("/etc/config/ddns", "")
31         end
32
33         entry( {"admin", "services", "ddns"}, cbi("ddns/overview"), _("Dynamic DNS"), 59)
34         entry( {"admin", "services", "ddns", "detail"}, cbi("ddns/detail"), nil ).leaf = true
35         entry( {"admin", "services", "ddns", "hints"}, cbi("ddns/hints",
36                 {hideapplybtn=true, hidesavebtn=true, hideresetbtn=true}), nil ).leaf = true
37         entry( {"admin", "services", "ddns", "logview"}, call("logread") ).leaf = true
38         entry( {"admin", "services", "ddns", "startstop"}, call("startstop") ).leaf = true
39         entry( {"admin", "services", "ddns", "status"}, call("status") ).leaf = true
40 end
41
42 -- function to read all sections status and return data array
43 local function _get_status()
44         local uci        = UCI.cursor()
45         local service    = SYS.init.enabled("ddns") and 1 or 0
46         local url_start  = DISP.build_url("admin", "system", "startup")
47         local luci_build = DDNS.ipkg_version("luci-app-ddns").version
48         local ddns_act   = DDNS.ipkg_version("ddns-scripts").version
49         local data       = {}   -- Array to transfer data to javascript
50
51         data[#data+1]   = {
52                 enabled    = service,           -- service enabled
53                 url_up     = url_start,         -- link to enable DDS (System-Startup)
54                 luci_ver   = luci_ddns_version, -- luci-app-ddns / openwrt Makefile compatible version
55                 luci_build = luci_build,        -- installed luci build
56                 script_min = ddns_scripts_min,  -- minimum version of ddns-scripts needed
57                 script_ver = ddns_act           -- installed ddns-scripts
58         }
59
60         uci:foreach("ddns", "service", function (s)
61
62                 -- Get section we are looking at
63                 -- and enabled state
64                 local section   = s[".name"]
65                 local enabled   = tonumber(s["enabled"]) or 0
66                 local datelast  = "_empty_"     -- formated date of last update
67                 local datenext  = "_empty_"     -- formated date of next update
68
69                 -- get force seconds
70                 local force_seconds = DDNS.calc_seconds(
71                                 tonumber(s["force_interval"]) or 72 ,
72                                 s["force_unit"] or "hours" )
73                 -- get/validate pid and last update
74                 local pid      = DDNS.get_pid(section)
75                 local uptime   = SYS.uptime()
76                 local lasttime = DDNS.get_lastupd(section)
77                 if lasttime > uptime then       -- /var might not be linked to /tmp
78                         lasttime = 0            -- and/or not cleared on reboot
79                 end
80
81                 -- no last update happen
82                 if lasttime == 0 then
83                         datelast = "_never_"
84
85                 -- we read last update
86                 else
87                         -- calc last update
88                         --             sys.epoch - sys uptime   + lastupdate(uptime)
89                         local epoch = os.time() - uptime + lasttime
90                         -- use linux date to convert epoch
91                         datelast = DDNS.epoch2date(epoch)
92                         -- calc and fill next update
93                         datenext = DDNS.epoch2date(epoch + force_seconds)
94                 end
95
96                 -- process running but update needs to happen
97                 -- problems it force_seconds > uptime
98                 force_seconds = (force_seconds > uptime) and uptime or force_seconds
99                 if pid > 0 and ( lasttime + force_seconds - uptime ) <= 0 then
100                         datenext = "_verify_"
101
102                 -- run once
103                 elseif force_seconds == 0 then
104                         datenext = "_runonce_"
105
106                 -- no process running and NOT enabled
107                 elseif pid == 0 and enabled == 0 then
108                         datenext  = "_disabled_"
109
110                 -- no process running and NOT
111                 elseif pid == 0 and enabled ~= 0 then
112                         datenext = "_stopped_"
113                 end
114
115                 -- get/set monitored interface and IP version
116                 local iface     = s["interface"] or "_nonet_"
117                 local use_ipv6  = tonumber(s["use_ipv6"]) or 0
118                 if iface ~= "_nonet_" then
119                         local ipv = (use_ipv6 == 1) and "IPv6" or "IPv4"
120                         iface = ipv .. " / " .. iface
121                 end
122
123                 -- try to get registered IP
124                 local domain    = s["domain"] or "_nodomain_"
125                 local dnsserver = s["dns_server"] or ""
126                 local force_ipversion = tonumber(s["force_ipversion"] or 0)
127                 local force_dnstcp = tonumber(s["force_dnstcp"] or 0)
128                 local command = [[/usr/lib/ddns/dynamic_dns_lucihelper.sh]]
129                 command = command .. [[ get_registered_ip ]] .. domain .. [[ ]] .. use_ipv6 ..
130                         [[ ]] .. force_ipversion .. [[ ]] .. force_dnstcp .. [[ ]] .. dnsserver
131                 local reg_ip = SYS.exec(command)
132                 if reg_ip == "" then
133                         reg_ip = "_nodata_"
134                 end
135
136                 -- fill transfer array
137                 data[#data+1]   = {
138                         section  = section,
139                         enabled  = enabled,
140                         iface    = iface,
141                         domain   = domain,
142                         reg_ip   = reg_ip,
143                         pid      = pid,
144                         datelast = datelast,
145                         datenext = datenext
146                 }
147         end)
148
149         uci:unload("ddns")
150         return data
151 end
152
153 -- called by XHR.get from detail_logview.htm
154 function logread(section)
155         -- read application settings
156         local uci         = UCI.cursor()
157         local log_dir     = uci:get("ddns", "global", "log_dir") or "/var/log/ddns"
158         local lfile=log_dir .. "/" .. section .. ".log"
159
160         local ldata=NXFS.readfile(lfile)
161         if not ldata or #ldata == 0 then
162                 ldata="_nodata_"
163         end
164         uci:unload("ddns")
165         HTTP.write(ldata)
166 end
167
168 -- called by XHR.get from overview_status.htm
169 function startstop(section, enabled)
170         local uci  = UCI.cursor()
171         local data = {}         -- Array to transfer data to javascript
172
173         -- if process running we want to stop and return
174         local pid = DDNS.get_pid(section)
175         if pid > 0 then
176                 local tmp = NX.kill(pid, 15)    -- terminate
177                 NX.nanosleep(2) -- 2 second "show time"
178                 -- status changed so return full status
179                 data = _get_status()
180                 HTTP.prepare_content("application/json")
181                 HTTP.write_json(data)
182                 return
183         end
184
185         -- read uncommited changes
186         -- we don't save and commit data from other section or other options
187         -- only enabled will be done
188         local exec        = true
189         local changed     = uci:changes("ddns")
190         for k_config, v_section in pairs(changed) do
191                 -- security check because uci.changes only gets our config
192                 if k_config ~= "ddns" then
193                         exec = false
194                         break
195                 end
196                 for k_section, v_option in pairs(v_section) do
197                         -- check if only section of button was changed
198                         if k_section ~= section then
199                                 exec = false
200                                 break
201                         end
202                         for k_option, v_value in pairs(v_option) do
203                                 -- check if only enabled was changed
204                                 if k_option ~= "enabled" then
205                                         exec = false
206                                         break
207                                 end
208                         end
209                 end
210         end
211
212         -- we can not execute because other
213         -- uncommited changes pending, so exit here
214         if not exec then
215                 HTTP.write("_uncommited_")
216                 return
217         end
218
219         -- save enable state
220         uci:set("ddns", section, "enabled", ( (enabled == "true") and "1" or "0") )
221         uci:save("ddns")
222         uci:commit("ddns")
223         uci:unload("ddns")
224
225         -- start dynamic_dns_updater.sh script
226         os.execute ([[/usr/lib/ddns/dynamic_dns_updater.sh %s 0 > /dev/null 2>&1 &]] % section)
227         NX.nanosleep(3) -- 3 seconds "show time"
228
229         -- status changed so return full status
230         data = _get_status()
231         HTTP.prepare_content("application/json")
232         HTTP.write_json(data)
233 end
234
235 -- called by XHR.poll from overview_status.htm
236 function status()
237         local data = _get_status()
238         HTTP.prepare_content("application/json")
239         HTTP.write_json(data)
240 end
241
242 -- check if installed ddns-scripts version < required version
243 function update_needed()
244         local sver = DDNS.ipkg_version("ddns-scripts")
245         local rver = UTIL.split(ddns_scripts_min, "[%.%-]", nil, true)
246         return (sver.major < (tonumber(rver[1]) or 0))
247             or (sver.minor < (tonumber(rver[2]) or 0))
248             or (sver.patch < (tonumber(rver[3]) or 0))
249             or (sver.build < (tonumber(rver[4]) or 0))
250 end
251