1 let libubus = require("ubus");
2 import { open, readfile } from "fs";
3 import { wdev_create, wdev_remove, is_equal, vlist_new, phy_is_fullmac } from "common";
5 let ubus = libubus.connect();
7 hostapd.data.config = {};
9 hostapd.data.file_fields = {
12 accept_mac_file: true,
24 function iface_remove(cfg)
26 if (!cfg || !cfg.bss || !cfg.bss[0] || !cfg.bss[0].ifname)
29 hostapd.remove_iface(cfg.bss[0].ifname);
30 for (let bss in cfg.bss)
31 wdev_remove(bss.ifname);
34 function iface_gen_config(phy, config)
37 ${join("\n", config.radio.data)}
38 channel=${config.radio.channel}
41 for (let i = 0; i < length(config.bss); i++) {
42 let bss = config.bss[i];
43 let type = i > 0 ? "bss" : "interface";
47 ${join("\n", bss.data)}
54 function iface_restart(phy, config, old_config)
56 iface_remove(old_config);
59 if (!config.bss || !config.bss[0]) {
60 hostapd.printf(`No bss for phy ${phy}`);
64 let bss = config.bss[0];
65 let err = wdev_create(phy, bss.ifname, { mode: "ap" });
67 hostapd.printf(`Failed to create ${bss.ifname} on phy ${phy}: ${err}`);
68 let config_inline = iface_gen_config(phy, config);
69 if (hostapd.add_iface(`bss_config=${bss.ifname}:${config_inline}`) < 0) {
70 hostapd.printf(`hostapd.add_iface failed for phy ${phy} ifname=${bss.ifname}`);
75 function array_to_obj(arr, key, start)
80 for (let i = start; i < length(arr); i++) {
88 function find_array_idx(arr, key, val)
90 for (let i = 0; i < length(arr); i++)
91 if (arr[i][key] == val)
97 function bss_reload_psk(bss, config, old_config)
99 if (is_equal(old_config.hash.wpa_psk_file, config.hash.wpa_psk_file))
102 old_config.hash.wpa_psk_file = config.hash.wpa_psk_file;
103 if (!is_equal(old_config, config))
106 let ret = bss.ctrl("RELOAD_WPA_PSK");
109 hostapd.printf(`Reload WPA PSK file for bss ${config.ifname}: ${ret}`);
112 function iface_reload_config(phy, config, old_config)
114 if (!old_config || !is_equal(old_config.radio, config.radio))
117 if (is_equal(old_config.bss, config.bss))
120 if (!old_config.bss || !old_config.bss[0])
123 if (config.bss[0].ifname != old_config.bss[0].ifname)
126 let iface = hostapd.interfaces[config.bss[0].ifname];
130 let config_inline = iface_gen_config(phy, config);
132 bss_reload_psk(iface.bss[0], config.bss[0], old_config.bss[0]);
133 if (!is_equal(config.bss[0], old_config.bss[0])) {
134 if (phy_is_fullmac(phy))
137 hostapd.printf(`Reload config for bss '${config.bss[0].ifname}' on phy '${phy}'`);
138 if (iface.bss[0].set_config(config_inline, 0) < 0) {
139 hostapd.printf(`Failed to set config`);
144 let bss_list = array_to_obj(iface.bss, "name", 1);
145 let new_cfg = array_to_obj(config.bss, "ifname", 1);
146 let old_cfg = array_to_obj(old_config.bss, "ifname", 1);
148 for (let name in old_cfg) {
149 let bss = bss_list[name];
151 hostapd.printf(`bss '${name}' not found`);
155 if (!new_cfg[name]) {
156 hostapd.printf(`Remove bss '${name}' on phy '${phy}'`);
162 let new_cfg_data = new_cfg[name];
163 delete new_cfg[name];
165 if (is_equal(old_cfg[name], new_cfg_data))
168 hostapd.printf(`Reload config for bss '${name}' on phy '${phy}'`);
169 let idx = find_array_idx(config.bss, "ifname", name);
171 hostapd.printf(`bss index not found`);
175 if (bss.set_config(config_inline, idx) < 0) {
176 hostapd.printf(`Failed to set config`);
181 for (let name in new_cfg) {
182 hostapd.printf(`Add bss '${name}' on phy '${phy}'`);
184 let idx = find_array_idx(config.bss, "ifname", name);
186 hostapd.printf(`bss index not found`);
190 if (iface.add_bss(config_inline, idx) < 0) {
191 hostapd.printf(`Failed to add bss`);
199 function iface_set_config(phy, config)
201 let old_config = hostapd.data.config[phy];
203 hostapd.data.config[phy] = config;
206 return iface_remove(old_config);
208 let ret = iface_reload_config(phy, config, old_config);
210 hostapd.printf(`Reloaded settings for phy ${phy}`);
214 hostapd.printf(`Restart interface for phy ${phy}`);
215 return iface_restart(phy, config, old_config);
218 function config_add_bss(config, name)
226 push(config.bss, bss);
231 function iface_load_config(filename)
233 let f = open(filename, "r");
247 while ((line = trim(f.read("line"))) != null) {
248 let val = split(line, "=", 2);
252 if (val[0] == "interface") {
253 bss = config_add_bss(config, val[1]);
257 if (val[0] == "channel") {
258 config.radio.channel = val[1];
262 push(config.radio.data, line);
265 while ((line = trim(f.read("line"))) != null) {
266 let val = split(line, "=", 2);
270 if (val[0] == "bss") {
271 bss = config_add_bss(config, val[1]);
275 if (hostapd.data.file_fields[val[0]])
276 bss.hash[val[0]] = hostapd.sha1(readfile(val[1]));
278 push(bss.data, line);
292 call: function(req) {
294 let phy_list = req.args.phy ? [ req.args.phy ] : keys(hostapd.data.config);
295 for (let phy_name in phy_list) {
296 let phy = hostapd.data.config[phy_name];
297 let config = iface_load_config(phy.orig_file);
298 iface_set_config(phy_name, config);
301 hostapd.printf(`Error reloading config: ${e}\n${e.stacktrace[0].context}`);
302 return libubus.STATUS_INVALID_ARGUMENT;
314 call: function(req) {
315 let phy = req.args.phy;
316 let file = req.args.config;
317 let prev_file = req.args.prev_config;
320 return libubus.STATUS_INVALID_ARGUMENT;
323 if (prev_file && !hostapd.data.config[phy]) {
324 let config = iface_load_config(prev_file);
326 config.radio.data = [];
327 hostapd.data.config[phy] = config;
330 let config = iface_load_config(file);
332 hostapd.printf(`Set new config for phy ${phy}: ${file}`);
333 iface_set_config(phy, config);
335 hostapd.printf(`Error loading config: ${e}\n${e.stacktrace[0].context}`);
336 return libubus.STATUS_INVALID_ARGUMENT;
340 pid: hostapd.getpid()
349 call: function(req) {
350 if (!req.args.iface || !req.args.config)
351 return libubus.STATUS_INVALID_ARGUMENT;
353 if (hostapd.add_iface(`bss_config=${req.args.iface}:${req.args.config}`) < 0)
354 return libubus.STATUS_INVALID_ARGUMENT;
357 pid: hostapd.getpid()
365 call: function(req) {
367 return libubus.STATUS_INVALID_ARGUMENT;
369 hostapd.remove_iface(req.args.iface);
375 hostapd.data.ubus = ubus;
376 hostapd.data.obj = ubus.publish("hostapd", main_obj);
378 function bss_event(type, name, data) {
379 let ubus = hostapd.data.ubus;
383 hostapd.data.obj.notify(`bss.${type}`, data, null, null, null, -1);
384 ubus.call("service", "event", { type: `hostapd.${name}.${type}`, data: {} });
388 shutdown: function() {
389 for (let phy in hostapd.data.config)
390 iface_set_config(phy, null);
391 hostapd.ubus.disconnect();
393 bss_add: function(name, obj) {
394 bss_event("add", name);
396 bss_reload: function(name, obj, reconf) {
397 bss_event("reload", name, { reconf: reconf != 0 });
399 bss_remove: function(name, obj) {
400 bss_event("remove", name);