Merge pull request #521 from LuttyYang/master
[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 DDNS_MIN = "2.4.2-1" -- minimum version of service required
19
20 function index()
21 local nxfs = require "nixio.fs" -- global definitions not available
22 local sys = require "luci.sys" -- in function index()
23 local ddns = require "luci.tools.ddns" -- ddns multiused functions
24 local verinst = ddns.ipkg_ver_installed("ddns-scripts")
25 local verok = ddns.ipkg_ver_compare(verinst, ">=", "2.0.0-0")
26 -- do NOT start it not ddns-scripts version 2.x
27 if not verok then
28 return
29 end
30 -- no config create an empty one
31 if not nxfs.access("/etc/config/ddns") then
32 nxfs.writefile("/etc/config/ddns", "")
33 end
34
35 entry( {"admin", "services", "ddns"}, cbi("ddns/overview"), _("Dynamic DNS"), 59)
36 entry( {"admin", "services", "ddns", "detail"}, cbi("ddns/detail"), nil ).leaf = true
37 entry( {"admin", "services", "ddns", "hints"}, cbi("ddns/hints",
38 {hideapplybtn=true, hidesavebtn=true, hideresetbtn=true}), nil ).leaf = true
39 entry( {"admin", "services", "ddns", "global"}, cbi("ddns/global"), nil ).leaf = true
40 entry( {"admin", "services", "ddns", "logview"}, call("logread") ).leaf = true
41 entry( {"admin", "services", "ddns", "startstop"}, post("startstop") ).leaf = true
42 entry( {"admin", "services", "ddns", "status"}, call("status") ).leaf = true
43 end
44
45 -- function to read all sections status and return data array
46 local function _get_status()
47 local uci = UCI.cursor()
48 local service = SYS.init.enabled("ddns") and 1 or 0
49 local url_start = DISP.build_url("admin", "system", "startup")
50 local data = {} -- Array to transfer data to javascript
51
52 data[#data+1] = {
53 enabled = service, -- service enabled
54 url_up = url_start, -- link to enable DDS (System-Startup)
55 }
56
57 uci:foreach("ddns", "service", function (s)
58
59 -- Get section we are looking at
60 -- and enabled state
61 local section = s[".name"]
62 local enabled = tonumber(s["enabled"]) or 0
63 local datelast = "_empty_" -- formatted date of last update
64 local datenext = "_empty_" -- formatted date of next update
65
66 -- get force seconds
67 local force_seconds = DDNS.calc_seconds(
68 tonumber(s["force_interval"]) or 72 ,
69 s["force_unit"] or "hours" )
70 -- get/validate pid and last update
71 local pid = DDNS.get_pid(section)
72 local uptime = SYS.uptime()
73 local lasttime = DDNS.get_lastupd(section)
74 if lasttime > uptime then -- /var might not be linked to /tmp
75 lasttime = 0 -- and/or not cleared on reboot
76 end
77
78 -- no last update happen
79 if lasttime == 0 then
80 datelast = "_never_"
81
82 -- we read last update
83 else
84 -- calc last update
85 -- sys.epoch - sys uptime + lastupdate(uptime)
86 local epoch = os.time() - uptime + lasttime
87 -- use linux date to convert epoch
88 datelast = DDNS.epoch2date(epoch)
89 -- calc and fill next update
90 datenext = DDNS.epoch2date(epoch + force_seconds)
91 end
92
93 -- process running but update needs to happen
94 -- problems if force_seconds > uptime
95 force_seconds = (force_seconds > uptime) and uptime or force_seconds
96 if pid > 0 and ( lasttime + force_seconds - uptime ) <= 0 then
97 datenext = "_verify_"
98
99 -- run once
100 elseif force_seconds == 0 then
101 datenext = "_runonce_"
102
103 -- no process running and NOT enabled
104 elseif pid == 0 and enabled == 0 then
105 datenext = "_disabled_"
106
107 -- no process running and enabled
108 elseif pid == 0 and enabled ~= 0 then
109 datenext = "_stopped_"
110 end
111
112 -- get/set monitored interface and IP version
113 local iface = s["interface"] or "_nonet_"
114 local use_ipv6 = tonumber(s["use_ipv6"]) or 0
115 if iface ~= "_nonet_" then
116 local ipv = (use_ipv6 == 1) and "IPv6" or "IPv4"
117 iface = ipv .. " / " .. iface
118 end
119
120 -- try to get registered IP
121 local domain = s["domain"] or "_nodomain_"
122 local dnsserver = s["dns_server"] or ""
123 local force_ipversion = tonumber(s["force_ipversion"] or 0)
124 local force_dnstcp = tonumber(s["force_dnstcp"] or 0)
125 local command = [[/usr/lib/ddns/dynamic_dns_lucihelper.sh]]
126 command = command .. [[ get_registered_ip ]] .. domain .. [[ ]] .. use_ipv6 ..
127 [[ ]] .. force_ipversion .. [[ ]] .. force_dnstcp .. [[ ]] .. dnsserver
128 local reg_ip = SYS.exec(command)
129 if reg_ip == "" then
130 reg_ip = "_nodata_"
131 end
132
133 -- fill transfer array
134 data[#data+1] = {
135 section = section,
136 enabled = enabled,
137 iface = iface,
138 domain = domain,
139 reg_ip = reg_ip,
140 pid = pid,
141 datelast = datelast,
142 datenext = datenext
143 }
144 end)
145
146 uci:unload("ddns")
147 return data
148 end
149
150 -- called by XHR.get from detail_logview.htm
151 function logread(section)
152 -- read application settings
153 local uci = UCI.cursor()
154 local log_dir = uci:get("ddns", "global", "log_dir") or "/var/log/ddns"
155 local lfile = log_dir .. "/" .. section .. ".log"
156 local ldata = NXFS.readfile(lfile)
157
158 if not ldata or #ldata == 0 then
159 ldata="_nodata_"
160 end
161 uci:unload("ddns")
162 HTTP.write(ldata)
163 end
164
165 -- called by XHR.get from overview_status.htm
166 function startstop(section, enabled)
167 local uci = UCI.cursor()
168 local pid = DDNS.get_pid(section)
169 local data = {} -- Array to transfer data to javascript
170
171 -- if process running we want to stop and return
172 if pid > 0 then
173 local tmp = NX.kill(pid, 15) -- terminate
174 NX.nanosleep(2) -- 2 second "show time"
175 -- status changed so return full status
176 data = _get_status()
177 HTTP.prepare_content("application/json")
178 HTTP.write_json(data)
179 return
180 end
181
182 -- read uncommitted changes
183 -- we don't save and commit data from other section or other options
184 -- only enabled will be done
185 local exec = true
186 local changed = uci:changes("ddns")
187 for k_config, v_section in pairs(changed) do
188 -- security check because uci.changes only gets our config
189 if k_config ~= "ddns" then
190 exec = false
191 break
192 end
193 for k_section, v_option in pairs(v_section) do
194 -- check if only section of button was changed
195 if k_section ~= section then
196 exec = false
197 break
198 end
199 for k_option, v_value in pairs(v_option) do
200 -- check if only enabled was changed
201 if k_option ~= "enabled" then
202 exec = false
203 break
204 end
205 end
206 end
207 end
208
209 -- we can not execute because other
210 -- uncommitted changes pending, so exit here
211 if not exec then
212 HTTP.write("_uncommitted_")
213 return
214 end
215
216 -- save enable state
217 uci:set("ddns", section, "enabled", ( (enabled == "true") and "1" or "0") )
218 uci:save("ddns")
219 uci:commit("ddns")
220 uci:unload("ddns")
221
222 -- start dynamic_dns_updater.sh script
223 os.execute ([[/usr/lib/ddns/dynamic_dns_updater.sh %s 0 > /dev/null 2>&1 &]] % section)
224 NX.nanosleep(3) -- 3 seconds "show time"
225
226 -- status changed so return full status
227 data = _get_status()
228 HTTP.prepare_content("application/json")
229 HTTP.write_json(data)
230 end
231
232 -- called by XHR.poll from overview_status.htm
233 function status()
234 local data = _get_status()
235 HTTP.prepare_content("application/json")
236 HTTP.write_json(data)
237 end