hostapd: do not store data in object prototype
[openwrt/staging/neocturne.git] / package / network / services / hostapd / files / wpa_supplicant.uc
1 let libubus = require("ubus");
2 import { open, readfile } from "fs";
3 import { wdev_create, wdev_remove, is_equal, vlist_new } from "common";
4
5 let ubus = libubus.connect();
6
7 wpas.data.config = {};
8 wpas.data.iface_phy = {};
9
10 function iface_stop(iface)
11 {
12 let ifname = iface.config.iface;
13
14 if (!iface.running)
15 return;
16
17 delete wpas.data.iface_phy[ifname];
18 wpas.remove_iface(ifname);
19 wdev_remove(ifname);
20 iface.running = false;
21 }
22
23 function iface_start(phy, iface)
24 {
25 if (iface.running)
26 return;
27
28 let ifname = iface.config.iface;
29
30 wpas.data.iface_phy[ifname] = phy;
31 wdev_remove(ifname);
32 let ret = wdev_create(phy, ifname, iface.config);
33 if (ret)
34 wpas.printf(`Failed to create device ${ifname}: ${ret}`);
35 wpas.add_iface(iface.config);
36 iface.running = true;
37 }
38
39 function iface_cb(new_if, old_if)
40 {
41 if (old_if && new_if && is_equal(old_if.config, new_if.config)) {
42 new_if.running = old_if.running;
43 return;
44 }
45
46 if (old_if)
47 iface_stop(old_if);
48 }
49
50 function prepare_config(config)
51 {
52 config.config_data = readfile(config.config);
53
54 return { config: config };
55 }
56
57 function set_config(phy_name, config_list)
58 {
59 let phy = wpas.data.config[phy_name];
60
61 if (!phy) {
62 phy = vlist_new(iface_cb, false);
63 wpas.data.config[phy_name] = phy;
64 }
65
66 let values = [];
67 for (let config in config_list)
68 push(values, [ config.iface, prepare_config(config) ]);
69
70 phy.update(values);
71 }
72
73 function start_pending(phy_name)
74 {
75 let phy = wpas.data.config[phy_name];
76
77 for (let ifname in phy.data)
78 iface_start(phy_name, phy.data[ifname]);
79 }
80
81 let main_obj = {
82 phy_set_state: {
83 args: {
84 phy: "",
85 stop: true,
86 },
87 call: function(req) {
88 if (!req.args.phy || req.args.stop == null)
89 return libubus.STATUS_INVALID_ARGUMENT;
90
91 let phy = wpas.data.config[req.args.phy];
92 if (!phy)
93 return libubus.STATUS_NOT_FOUND;
94
95 try {
96 if (req.args.stop) {
97 for (let ifname in phy.data)
98 iface_stop(phy.data[ifname]);
99 } else {
100 start_pending(req.args.phy);
101 }
102 } catch (e) {
103 wpas.printf(`Error chaging state: ${e}\n${e.stacktrace[0].context}`);
104 return libubus.STATUS_INVALID_ARGUMENT;
105 }
106 return 0;
107 }
108 },
109 config_set: {
110 args: {
111 phy: "",
112 config: [],
113 defer: true,
114 },
115 call: function(req) {
116 if (!req.args.phy)
117 return libubus.STATUS_INVALID_ARGUMENT;
118
119 try {
120 if (req.args.config)
121 set_config(req.args.phy, req.args.config);
122
123 if (!req.args.defer)
124 start_pending(req.args.phy);
125 } catch (e) {
126 wpas.printf(`Error loading config: ${e}\n${e.stacktrace[0].context}`);
127 return libubus.STATUS_INVALID_ARGUMENT;
128 }
129
130 return {
131 pid: wpas.getpid()
132 };
133 }
134 },
135 config_add: {
136 args: {
137 driver: "",
138 iface: "",
139 bridge: "",
140 hostapd_ctrl: "",
141 ctrl: "",
142 config: "",
143 },
144 call: function(req) {
145 if (!req.args.iface || !req.args.config)
146 return libubus.STATUS_INVALID_ARGUMENT;
147
148 if (wpas.add_iface(req.args) < 0)
149 return libubus.STATUS_INVALID_ARGUMENT;
150
151 return {
152 pid: wpas.getpid()
153 };
154 }
155 },
156 config_remove: {
157 args: {
158 iface: ""
159 },
160 call: function(req) {
161 if (!req.args.iface)
162 return libubus.STATUS_INVALID_ARGUMENT;
163
164 wpas.remove_iface(req.args.iface);
165 return 0;
166 }
167 },
168 };
169
170 wpas.data.ubus = ubus;
171 wpas.data.obj = ubus.publish("wpa_supplicant", main_obj);
172
173 function iface_event(type, name, data) {
174 let ubus = wpas.data.ubus;
175
176 data ??= {};
177 data.name = name;
178 wpas.data.obj.notify(`iface.${type}`, data, null, null, null, -1);
179 ubus.call("service", "event", { type: `wpa_supplicant.${name}.${type}`, data: {} });
180 }
181
182 function iface_hostapd_notify(phy, ifname, iface, state)
183 {
184 let ubus = wpas.data.ubus;
185 let status = iface.status();
186 let msg = { phy: phy };
187
188 switch (state) {
189 case "DISCONNECTED":
190 case "AUTHENTICATING":
191 msg.up = false;
192 break;
193 case "INTERFACE_DISABLED":
194 case "INACTIVE":
195 msg.up = true;
196 break;
197 case "COMPLETED":
198 msg.up = true;
199 msg.frequency = status.frequency;
200 msg.sec_chan_offset = status.sec_chan_offset;
201 break;
202 default:
203 return;
204 }
205
206 ubus.call("hostapd", "apsta_state", msg);
207 }
208
209 function iface_channel_switch(phy, ifname, iface, info)
210 {
211 let msg = {
212 phy: phy,
213 up: true,
214 csa: true,
215 csa_count: info.csa_count ? info.csa_count - 1 : 0,
216 frequency: info.frequency,
217 sec_chan_offset: info.sec_chan_offset,
218 };
219 ubus.call("hostapd", "apsta_state", msg);
220 }
221
222 return {
223 shutdown: function() {
224 for (let phy in wpas.data.config)
225 set_config(phy, []);
226 wpas.ubus.disconnect();
227 },
228 iface_add: function(name, obj) {
229 iface_event("add", name);
230 },
231 iface_remove: function(name, obj) {
232 iface_event("remove", name);
233 },
234 state: function(ifname, iface, state) {
235 let phy = wpas.data.iface_phy[ifname];
236 if (!phy) {
237 wpas.printf(`no PHY for ifname ${ifname}`);
238 return;
239 }
240
241 iface_hostapd_notify(phy, ifname, iface, state);
242 },
243 event: function(ifname, iface, ev, info) {
244 let phy = wpas.data.iface_phy[ifname];
245 if (!phy) {
246 wpas.printf(`no PHY for ifname ${ifname}`);
247 return;
248 }
249
250 if (ev == "CH_SWITCH_STARTED")
251 iface_channel_switch(phy, ifname, iface, info);
252 }
253 };