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