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