/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * ubus.c - provides wifimngr UBUS commands.
 *
 * Copyright (C) 2024 Iopsys Software Solutions AB. All rights reserved.
 * Copyright (C) 2025 Genexis AB.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <limits.h>
#include <net/if.h>
#include <libubox/blobmsg.h>
#include <libubox/blobmsg_json.h>
#include <libubox/uloop.h>
#include <libubox/ustream.h>
#include <libubox/utils.h>
#include <libubus.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <easy/easy.h>
#include <wifi.h>
#include <wifidefs.h>

#include "wifimngr.h"
#include "debug.h"

#include "policy.c"

extern void *libsta_ratings;

int wifimngr_add_object(struct wifimngr *w, const char *objname,
			int (*add_methods)(struct wifimngr *w, struct ubus_object *o),
			struct ubus_object *object)
{
	struct ubus_object_type *obj_type;
	struct ubus_object *obj;
	int ret;

	obj = object;
	memset(obj, 0, sizeof(*obj));
	obj_type = calloc(1, sizeof(struct ubus_object_type));
	if (!obj_type)
		return -ENOMEM;

	obj->name = strdup(objname);
	if (add_methods)
		add_methods(w, obj);

	obj_type->name = obj->name;
	obj_type->n_methods = obj->n_methods;
	obj_type->methods = obj->methods;
	obj->type = obj_type;

	ret = ubus_add_object(w->ubus_ctx, obj);
	if (ret) {
		wifimngr_dbg("Failed to add '%s' object\n", obj->name);
		free((void *)obj->methods);
		free((void *)obj->name);
		free(obj_type);
	}

	return ret;
}

#define MAX_RADIO_METHODS	16

static int add_radio_methods(struct wifimngr *w, struct ubus_object *radio_obj,
				const char *radioname)
{
	struct wifimngr_device *wdev = wifimngr_lookup_wifi_device(w, radioname);
	struct ubus_method *radio_methods;
	enum wifi_band band;
	int n_methods = 0;
	char *phyname;


	if (!wdev)
		return -1;

	radio_methods = calloc(MAX_RADIO_METHODS, sizeof(struct ubus_method));
	if (!radio_methods)
		return -ENOMEM;

	phyname = wdev->phy;
	band = wdev->band;


	UBUS_METHOD_ADD(radio_methods, n_methods,
		UBUS_METHOD_NOARG("help", wl_radio_help));

	UBUS_METHOD_ADD(radio_methods, n_methods,
		UBUS_METHOD_NOARG("status", wl_radio_status));

	UBUS_METHOD_ADD(radio_methods, n_methods,
			UBUS_METHOD_NOARG("stats", wl_radio_stats));

	UBUS_METHOD_ADD(radio_methods, n_methods,
		UBUS_METHOD("get", wl_radio_get_param, radio_get_param_policy));

	//UBUS_METHOD_ADD(radio_methods, n_methods,
	//	UBUS_METHOD_NOARG("temperature", wl_temperature));

	UBUS_METHOD_ADD(radio_methods, n_methods,
		UBUS_METHOD("scan", wl_scan, wl_scan_policy));

	if (libwifi_supports(phyname, "wifi_scan_ex")) {
		UBUS_METHOD_ADD(radio_methods, n_methods,
			UBUS_METHOD("scan_ex", wl_scan_ex, wl_scan_ex_policy));
	}

	UBUS_METHOD_ADD(radio_methods, n_methods,
		UBUS_METHOD("scanresults", wl_scanresults, wl_scanres_policy));

	UBUS_METHOD_ADD(radio_methods, n_methods,
		UBUS_METHOD("autochannel", wl_autochannel, wl_acs_policy));

	if (libwifi_supports(phyname, "wifi_start_cac")) {
		UBUS_METHOD_ADD(radio_methods, n_methods,
			UBUS_METHOD("start_cac", wl_start_cac, wl_cac_policy));

		UBUS_METHOD_ADD(radio_methods, n_methods,
			UBUS_METHOD("stop_cac", wl_stop_cac, wl_cac_policy));
	}

	if (libwifi_supports(phyname, "wifi_add_iface")) {
		UBUS_METHOD_ADD(radio_methods, n_methods,
			UBUS_METHOD("add_iface", wl_add_iface, add_iface_policy));
	}

	if (libwifi_supports(phyname, "wifi_del_iface")) {
		UBUS_METHOD_ADD(radio_methods, n_methods,
			UBUS_METHOD("del_iface", wl_del_iface, del_iface_policy));
	}

	if (libwifi_supports(phyname, "wifi_channels_info")) {
		UBUS_METHOD_ADD(radio_methods, n_methods,
			UBUS_METHOD_NOARG("channels_info", wl_channels_info));
	}

	UBUS_METHOD_ADD(radio_methods, n_methods,
		UBUS_METHOD("channels", wl_channels, wl_dump_channels_policy));

	if (libwifi_supports(phyname, "wifi_get_opclass_preferences")) {
		UBUS_METHOD_ADD(radio_methods, n_methods,
			UBUS_METHOD_NOARG("opclass_preferences", wl_opclass_preferences));
	}

	if (libwifi_supports(phyname, "wifi_simulate_radar") &&
	                    (band == BAND_5 || band == BAND_DUAL || band == BAND_UNKNOWN)) {
		UBUS_METHOD_ADD(radio_methods, n_methods,
			UBUS_METHOD("simulate_radar", wl_simulate_radar, wl_radar_policy));
	}

	radio_obj->methods = radio_methods;
	radio_obj->n_methods = n_methods;

	return 0;
}


#define MAX_WIFI_METHODS	8
int add_wifi_methods(struct wifimngr *w, struct ubus_object *wifi_obj)
{
	int n_methods = 0;
	struct ubus_method *wifi_methods;

	wifi_methods = calloc(MAX_WIFI_METHODS, sizeof(struct ubus_method));
	if (!wifi_methods)
		return -ENOMEM;

	UBUS_METHOD_ADD(wifi_methods, n_methods,
		UBUS_METHOD_NOARG("help", wl_help));

	UBUS_METHOD_ADD(wifi_methods, n_methods,
		UBUS_METHOD_NOARG("status", wl_status));

	UBUS_METHOD_ADD(wifi_methods, n_methods,
		UBUS_METHOD_NOARG("assoclist", wl_wifi_assoclist));

	UBUS_METHOD_ADD(wifi_methods, n_methods,
		UBUS_METHOD("debug", wifi_set_debug, wifi_set_debug_policy));

	wifi_obj->methods = wifi_methods;
	wifi_obj->n_methods = n_methods;

	return 0;
}

int wifimngr_lookup_object(struct wifimngr *w, const char *objname)
{
	uint32_t id;
	int ret;

	ret = ubus_lookup_id(w->ubus_ctx, objname, &id);
	if (ret == UBUS_STATUS_OK)
		return id;

	return ret == UBUS_STATUS_OK ? id : -1;
}

int wifimngr_add_radio_object(struct wifimngr *w, struct wifimngr_device *wdev)
{
	char *radioname = wdev->device;
	struct wifi_ubus_object *wobj;
	char objname[64] = {0};
	uint32_t id;
	int ret;


	snprintf(objname, 63, "%s.%s", WIFI_RADIO_OBJECT, radioname);

	/* Already added */
	if (ubus_lookup_id(w->ubus_ctx, objname, &id) == UBUS_STATUS_OK) {
		wdev->object_id = id;
		return 0;
	}

	wobj = calloc(1, sizeof(struct wifi_ubus_object));
	if (!wobj)
		return -ENOMEM;

	wobj->priv = wdev;
	wobj->obj.name = strdup(objname);
	add_radio_methods(w, &wobj->obj, radioname);

	wobj->obj_type.name = wobj->obj.name;
	wobj->obj_type.n_methods = wobj->obj.n_methods;
	wobj->obj_type.methods = wobj->obj.methods;
	wobj->obj.type = &wobj->obj_type;

	ret = ubus_add_object(w->ubus_ctx, &wobj->obj);
	if (ret) {
		free((void *)wobj->obj.methods);
		free((void *)wobj->obj.name);
		free(wobj);
		return ret;
	}

	wifimngr_dbg("Added '%s' radio obj\n", wobj->obj.name);
	wdev->object_id = wobj->obj.id;
	list_add_tail(&wobj->list, &w->radiolist);

	return 0;
}

#define MAX_AP_METHODS	48
static int add_ap_methods(struct wifimngr *w, struct ubus_object *interface_obj,
			  const char *ifname)
{
	struct ubus_method *ap_methods;
	int n_methods = 0;

	ap_methods = calloc(MAX_AP_METHODS, sizeof(struct ubus_method));
	if (!ap_methods)
		return -ENOMEM;

	UBUS_METHOD_ADD(ap_methods, n_methods,
		UBUS_METHOD_NOARG("help", wl_ap_help));

	UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD_NOARG("status", wl_ap_status));

	UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD_NOARG("stats", wl_ap_stats));

	UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD_NOARG("assoclist", wl_assoclist));

	UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD("stations", wl_stations, stainfo_policy));

	if (libsta_ratings) {
		UBUS_METHOD_ADD(ap_methods, n_methods,
				UBUS_METHOD("sta_ratings", wl_sta_ratings, stainfo_policy));
	}

	if (libwifi_supports(ifname, "wifi_get_blocked_stas")) {
		UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD_NOARG("blocked_stas", wl_blocked_stas));
	}

	if (libwifi_supports(ifname, "wifi_block_sta")) {
		UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD("block_sta", wl_block_sta, block_station_policy));
	}

	if (libwifi_supports(ifname, "wifi_disconnect_sta")) {
		UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD("disconnect", sta_disconnect, sta_disconnect_policy));
	}

	if (libwifi_supports(ifname, "wifi_probe_sta")) {
		UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD("probe_sta", sta_probe, sta_monitor_policy));
	}

	if (libwifi_supports(ifname, "wifi_monitor_sta")) {
		UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD("monitor_add", sta_monitor_add, sta_monitor_policy));
		UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD("monitor_del", sta_monitor_del, sta_monitor_policy));
		UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD("monitor_get", sta_monitor_get, sta_monitor_policy));
	}

	if (libwifi_supports(ifname, "wifi_subscribe_frame"))
		UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD("subscribe_frame", subscribe_frame, subscribe_frame_policy));
	if (libwifi_supports(ifname, "wifi_unsubscribe_frame"))
		UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD("unsubscribe_frame", unsubscribe_frame, subscribe_frame_policy));

	if (libwifi_supports(ifname, "wifi_add_neighbor"))
		UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD("add_neighbor", nbr_add, nbr_add_policy));

	if (libwifi_supports(ifname, "wifi_del_neighbor"))
		UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD("del_neighbor", nbr_del, nbr_del_policy));

	if (libwifi_supports(ifname, "wifi_get_neighbor_list"))
		UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD("list_neighbor", nbr_list, nbr_list_policy));

	if (libwifi_supports(ifname, "wifi_req_beacon_report"))
		UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD("request_neighbor", nbr_request, nbr_req_policy));

	if (libwifi_supports(ifname, "wifi_req_btm"))
		UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD("request_btm", btm_request, btmreq_policy));

	if (libwifi_supports(ifname, "wifi_restrict_sta"))
		UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD("assoc_control", assoc_control, assoc_cntrl_policy));

	if (libwifi_supports(ifname, "wifi_add_vendor_ie"))
		UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD("add_vendor_ie", vsie_add, vsie_policy));

	if (libwifi_supports(ifname, "wifi_del_vendor_ie"))
		UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD("del_vendor_ie", vsie_del, vsie_policy));

	if (libwifi_supports(ifname, "wifi_get_beacon_ies"))
		UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD_NOARG("dump_beacon", wl_ap_dump_beacon));

	if (libwifi_supports(ifname, "wifi_chan_switch"))
		UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD("chan_switch", ap_chan_switch, chan_switch_policy));

	if (libwifi_supports(ifname, "wifi_link_measure"))
		UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD("measure_link", ap_measure_link, linkmeas_policy));

	if (libwifi_supports(ifname, "wifi_mbo_disallow_assoc"))
		UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD("mbo_disallow_assoc", ap_mbo_disallow_assoc, mbo_disallow_assoc_policy));

	if (libwifi_supports(ifname, "wifi_ap_set_state")) {
		UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD_NOARG("up", ap_bss_up));
		UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD_NOARG("down", ap_bss_down));
	}

	if (libwifi_supports(ifname, "wifi_send_action_frame")) {
		UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD("send_action", ap_send_action, send_action_policy));
	}

	if (libwifi_supports(ifname, "wifi_ap_set_qos_map")) {
		UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD("set_qos_map", set_qos_map,
				        ap_set_qos_map_policy));
	}

	if (libwifi_supports(ifname, "wifi_ap_send_qos_map_conf")) {
		UBUS_METHOD_ADD(ap_methods, n_methods,
			UBUS_METHOD("send_qos_map_conf", send_qos_map_conf,
				        ap_send_qos_map_conf_policy));
	}

	interface_obj->methods = ap_methods;
	interface_obj->n_methods = n_methods;

	return 0;
}

#define MAX_STA_METHODS	8
static int add_sta_methods(struct wifimngr *w, struct ubus_object *interface_obj,
			   const char *ifname)
{
	int n_methods = 0;
	struct ubus_method *sta_methods;

	sta_methods = calloc(MAX_STA_METHODS, sizeof(struct ubus_method));
	if (!sta_methods)
		return -ENOMEM;

	UBUS_METHOD_ADD(sta_methods, n_methods,
			UBUS_METHOD_NOARG("status", wl_sta_status));

	UBUS_METHOD_ADD(sta_methods, n_methods,
			UBUS_METHOD_NOARG("stats", wl_sta_stats));

	if (libwifi_supports(ifname, "wifi_sta_disconnect_ap")) {
		UBUS_METHOD_ADD(sta_methods, n_methods,
			UBUS_METHOD("disconnect", wl_sta_disconnect_ap,
						ap_disconnect_policy));
	}

	if (libwifi_supports(ifname, "wifi_set_4addr")) {
		UBUS_METHOD_ADD(sta_methods, n_methods,
			UBUS_METHOD("4addr", wl_sta_4addr,
					     sta_4addr_policy));
	}

	if (libwifi_supports(ifname, "wifi_send_action_frame")) {
		UBUS_METHOD_ADD(sta_methods, n_methods,
			UBUS_METHOD("send_action", ap_send_action, send_action_policy));
	}

	if (libwifi_supports(ifname, "wifi_dpp_listen")) {
		UBUS_METHOD_ADD(sta_methods, n_methods,
			UBUS_METHOD("dpp_listen", wl_sta_dpp_listen, dpp_listen_policy));
	}

	if (libwifi_supports(ifname, "wifi_dpp_stop_listen")) {
		UBUS_METHOD_ADD(sta_methods, n_methods,
			UBUS_METHOD_NOARG("dpp_stop_listen", wl_sta_dpp_stop_listen));
	}

	interface_obj->methods = sta_methods;
	interface_obj->n_methods = n_methods;

	return 0;
}

int wifimngr_add_interface_object(struct wifimngr *w, struct wifimngr_iface *iface)
{
	struct wifi_ubus_object *wobj;
	char objname[64] = {0};
	uint32_t id;
	int ret;


	if (iface->mode == WIFI_MODE_AP) {
		snprintf(objname, 63, "%s.%s", WIFI_AP_OBJECT, iface->iface);
	} else if (iface->mode == WIFI_MODE_STA) {
		snprintf(objname, 63, "%s.%s", WIFI_BSTA_OBJECT, iface->iface);
	} else {
		/* unhandled wifi mode */
		return -EINVAL;
	}

	/* Already added */
	if (ubus_lookup_id(w->ubus_ctx, objname, &id) == UBUS_STATUS_OK) {
		iface->object_id = id;
		return 0;
	}

	wobj = calloc(1, sizeof(struct wifi_ubus_object));
	if (!wobj)
		return -ENOMEM;

	wobj->priv = iface;
	if (iface->mode == WIFI_MODE_AP)
		add_ap_methods(w, &wobj->obj, iface->iface);
	else
		add_sta_methods(w, &wobj->obj, iface->iface);

	wobj->obj.name = strdup(objname);
	wobj->obj_type.name = wobj->obj.name;
	wobj->obj_type.n_methods = wobj->obj.n_methods;
	wobj->obj_type.methods = wobj->obj.methods;
	wobj->obj.type = &wobj->obj_type;

	ret = ubus_add_object(w->ubus_ctx, &wobj->obj);
	if (!ret) {
		iface->object_id = wobj->obj.id;
		list_add_tail(&wobj->list, &w->iflist);
	} else {
		free((void *)wobj->obj.methods);
		free((void *)wobj->obj.name);
		free(wobj);
	}

	return ret;
}

#define MAX_APMLD_METHODS	16
static int add_apmld_methods(struct wifimngr *w,
			     struct ubus_object *interface_obj,
			     const char *ifname)
{
	struct ubus_method *apmld_methods;
	int n_methods = 0;

	apmld_methods = calloc(MAX_APMLD_METHODS, sizeof(struct ubus_method));
	if (!apmld_methods)
		return -ENOMEM;

	UBUS_METHOD_ADD(apmld_methods, n_methods,
		UBUS_METHOD_NOARG("help", wl_apmld_help));

	UBUS_METHOD_ADD(apmld_methods, n_methods,
			UBUS_METHOD_NOARG("status", wl_apmld_status));

	UBUS_METHOD_ADD(apmld_methods, n_methods,
			UBUS_METHOD_NOARG("stats", wl_apmld_stats));

	UBUS_METHOD_ADD(apmld_methods, n_methods,
			UBUS_METHOD_NOARG("assoclist", wl_apmld_assoclist));

	UBUS_METHOD_ADD(apmld_methods, n_methods,
			UBUS_METHOD("stations", wl_apmld_stations, stainfo_policy));

	if (libsta_ratings) {
		UBUS_METHOD_ADD(apmld_methods, n_methods,
				UBUS_METHOD("sta_ratings", wl_apmld_sta_ratings, stainfo_policy));
	}

	if (libwifi_supports(ifname, "wifi_disconnect_sta")) {
		UBUS_METHOD_ADD(apmld_methods, n_methods,
			UBUS_METHOD("disconnect", wl_apmld_disconnect_sta, sta_disconnect_policy));
	}

	if (libwifi_supports(ifname, "wifi_block_sta")) {
		UBUS_METHOD_ADD(apmld_methods, n_methods,
			UBUS_METHOD("block_sta", wl_apmld_block_sta, apmld_block_station_policy));
	}

	if (libwifi_supports(ifname, "wifi_get_blocked_stas")) {
		UBUS_METHOD_ADD(apmld_methods, n_methods,
			UBUS_METHOD("blocked_stas", wl_apmld_blocked_stas, apmld_blocked_maclist_policy));
	}

	if (libwifi_supports(ifname, "wifi_get_beacon_ies")) {
		UBUS_METHOD_ADD(apmld_methods, n_methods,
			UBUS_METHOD("dump_beacon", wl_apmld_dump_beacon, dump_beacon_policy));
	}

	interface_obj->methods = apmld_methods;
	interface_obj->n_methods = n_methods;

	return 0;
}

#define MAX_MLDSTA_METHODS	8
static int add_mldsta_methods(struct wifimngr *w,
			      struct ubus_object *interface_obj,
			      const char *ifname)
{
	struct ubus_method *mldsta_methods;
	int n_methods = 0;

	mldsta_methods = calloc(MAX_MLDSTA_METHODS, sizeof(struct ubus_method));
	if (!mldsta_methods)
		return -ENOMEM;

	UBUS_METHOD_ADD(mldsta_methods, n_methods,
			UBUS_METHOD_NOARG("status", wl_mldsta_status));

	UBUS_METHOD_ADD(mldsta_methods, n_methods,
			UBUS_METHOD_NOARG("stats", wl_mldsta_stats));

	if (libwifi_supports(ifname, "wifi_sta_disconnect_ap")) {
		UBUS_METHOD_ADD(mldsta_methods, n_methods,
			UBUS_METHOD("disconnect", wl_mldsta_disconnect_ap,
						ap_disconnect_policy));
	}

	if (libwifi_supports(ifname, "wifi_set_4addr")) {
		UBUS_METHOD_ADD(mldsta_methods, n_methods,
			UBUS_METHOD("4addr", wl_sta_4addr,
					     sta_4addr_policy));
	}

	interface_obj->methods = mldsta_methods;
	interface_obj->n_methods = n_methods;

	return 0;
}

int wifimngr_add_mld_interface_object(struct wifimngr *w, struct wifimngr_mld *mldif)
{
	struct wifi_ubus_object *wobj;
	char objname[64] = {0};
	uint32_t id;
	int ret;

	if (mldif->mode == WIFI_MODE_AP)
		snprintf(objname, 63, "%s.%s", WIFI_APMLD_OBJECT, mldif->ifname);
	else if (mldif->mode == WIFI_MODE_STA)
		snprintf(objname, 63, "%s.%s", WIFI_BSTAMLD_OBJECT, mldif->ifname);
	else
		return -EINVAL;

	/* Already added */
	if (ubus_lookup_id(w->ubus_ctx, objname, &id) == UBUS_STATUS_OK) {
		mldif->object_id = id;
		return 0;
	}

	wobj = calloc(1, sizeof(struct wifi_ubus_object));
	if (!wobj)
		return -ENOMEM;

	wobj->priv = mldif;
	if (mldif->mode == WIFI_MODE_AP)
		add_apmld_methods(w, &wobj->obj, mldif->ifname);
	else
		add_mldsta_methods(w, &wobj->obj, mldif->ifname);

	wobj->obj.name = strdup(objname);
	wobj->obj_type.name = wobj->obj.name;
	wobj->obj_type.n_methods = wobj->obj.n_methods;
	wobj->obj_type.methods = wobj->obj.methods;
	wobj->obj.type = &wobj->obj_type;

	ret = ubus_add_object(w->ubus_ctx, &wobj->obj);
	if (!ret) {
		mldif->object_id = wobj->obj.id;
		list_add_tail(&wobj->list, &w->iflist);
	} else {
		free((void *)wobj->obj.methods);
		free((void *)wobj->obj.name);
		free(wobj);
	}

	return ret;
}

void wifimngr_del_object(struct wifimngr *w, struct ubus_object *obj, bool remove_from_ubus)
{
	/* Remove from ubus before freeing memory to avoid use-after-free.
	 * Skip removal only during final cleanup when ubus_free() will handle it. */
	if (remove_from_ubus && w->ubus_ctx && obj->id)
		ubus_remove_object(w->ubus_ctx, obj);

	if (obj->methods)
		free((void *)obj->methods);

	if (obj->name)
		free((void *)obj->name);
}

int wifimngr_add_objects(struct wifimngr *w)
{
	int ret;

	ret = wifimngr_add_object(w, WIFI_OBJECT, add_wifi_methods, &w->wifi_obj);
	if (ret) {
		wifimngr_err("Failed to add '%s' ubus object: %s\n",
			     WIFI_OBJECT, ubus_strerror(ret));
		goto out_exit;
	}

	ret = wifimngr_add_object(w, WIFI_WPS_OBJECT, add_wps_methods, &w->wifi_wps_obj);
	if (ret) {
		wifimngr_err("Failed to add '%s' ubus object: %s\n",
			     WIFI_WPS_OBJECT, ubus_strerror(ret));
		goto out_exit;
	}

	wifimngr_reconfig(w);
	return 0;

out_exit:
	return -1;
}

