manager: surface D-Bus call errors to the user
Connect / Forget / Set(Powered) / Scan / Disconnect / RegisterAgent /
ConnectHidden previously discarded reply errors with NULL callbacks, so
"Connecting…" could hang forever after a refused call (rfkill, busy
adapter, another agent already registered, bad credentials on a known
network). The user had no way to see the failure.
Add iwd_manager_{report,last,clear}_error and wire reply callbacks in
adapter / device / network / agent. The popup status line now appends
the latest error to the state label, and user actions (rescan, toggle,
connect, disconnect) clear it.
Scan errors that mean "already in flight" are filtered out — they're
the normal race when two scan triggers fire close together.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3fc41eef8f
commit
0418e8bab9
7 changed files with 165 additions and 24 deletions
|
|
@ -148,6 +148,7 @@ _on_net_clicked(void *data, Evas_Object *obj EINA_UNUSED, void *ev EINA_UNUSED)
|
|||
{
|
||||
Iwd_Network *n = data;
|
||||
if (!n) return;
|
||||
if (e_iwd && e_iwd->manager) iwd_manager_clear_error(e_iwd->manager);
|
||||
iwd_network_connect(n);
|
||||
}
|
||||
|
||||
|
|
@ -234,7 +235,17 @@ _refresh(Popup *p)
|
|||
if (!p || !e_iwd || !e_iwd->manager) return;
|
||||
Iwd_State s = iwd_manager_state(e_iwd->manager);
|
||||
if (p->status_lbl)
|
||||
elm_object_text_set(p->status_lbl, _state_label(s));
|
||||
{
|
||||
const char *err = iwd_manager_last_error(e_iwd->manager);
|
||||
if (err)
|
||||
{
|
||||
char buf[320];
|
||||
snprintf(buf, sizeof(buf), "%s — %s", _state_label(s), err);
|
||||
elm_object_text_set(p->status_lbl, buf);
|
||||
}
|
||||
else
|
||||
elm_object_text_set(p->status_lbl, _state_label(s));
|
||||
}
|
||||
if (p->btn_toggle)
|
||||
elm_object_text_set(p->btn_toggle, s == IWD_STATE_OFF ? "Enable" : "Disable");
|
||||
if (p->btn_scan)
|
||||
|
|
@ -260,18 +271,23 @@ _on_manager_change(void *data, Iwd_Manager *m EINA_UNUSED)
|
|||
|
||||
static void _on_rescan (void *d EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *e EINA_UNUSED)
|
||||
{
|
||||
if (e_iwd && e_iwd->manager) iwd_manager_scan_request(e_iwd->manager);
|
||||
if (!e_iwd || !e_iwd->manager) return;
|
||||
iwd_manager_clear_error(e_iwd->manager);
|
||||
iwd_manager_scan_request(e_iwd->manager);
|
||||
}
|
||||
static void _on_toggle(void *d EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *e EINA_UNUSED)
|
||||
{
|
||||
if (!e_iwd || !e_iwd->manager) return;
|
||||
iwd_manager_clear_error(e_iwd->manager);
|
||||
Eina_Bool off = (iwd_manager_state(e_iwd->manager) == IWD_STATE_OFF);
|
||||
iwd_manager_set_powered(e_iwd->manager, off);
|
||||
}
|
||||
static void _on_disconnect(void *d EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *e EINA_UNUSED)
|
||||
{
|
||||
Iwd_Device *dev = _active_device();
|
||||
if (dev) iwd_device_disconnect(dev);
|
||||
if (!dev) return;
|
||||
if (e_iwd && e_iwd->manager) iwd_manager_clear_error(e_iwd->manager);
|
||||
iwd_device_disconnect(dev);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
|||
|
|
@ -61,6 +61,16 @@ iwd_adapter_free(Iwd_Adapter *a)
|
|||
free(a);
|
||||
}
|
||||
|
||||
static void
|
||||
_on_set_powered_reply(void *data, const Eldbus_Message *msg, Eldbus_Pending *p EINA_UNUSED)
|
||||
{
|
||||
Iwd_Adapter *a = data;
|
||||
const char *en, *em;
|
||||
if (eldbus_message_error_get(msg, &en, &em) && a->manager)
|
||||
iwd_manager_report_error(a->manager,
|
||||
"Set Adapter.Powered failed: %s", em ? em : en);
|
||||
}
|
||||
|
||||
void
|
||||
iwd_adapter_set_powered(Iwd_Adapter *a, Eina_Bool on)
|
||||
{
|
||||
|
|
@ -83,7 +93,7 @@ iwd_adapter_set_powered(Iwd_Adapter *a, Eina_Bool on)
|
|||
eldbus_message_iter_basic_append(variant, 'b', v);
|
||||
eldbus_message_iter_container_close(iter, variant);
|
||||
|
||||
eldbus_proxy_send(props, msg, NULL, NULL, -1);
|
||||
eldbus_proxy_send(props, msg, _on_set_powered_reply, a, -1);
|
||||
/* Keep the props proxy alive on the adapter so the call isn't canceled. */
|
||||
if (a->_props_proxy_keepalive) eldbus_proxy_unref(a->_props_proxy_keepalive);
|
||||
a->_props_proxy_keepalive = props;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
#include "iwd_agent.h"
|
||||
#include "iwd_dbus.h"
|
||||
#include "iwd_manager.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
|
@ -135,11 +137,18 @@ iwd_agent_cancel(Iwd_Agent_Request *req)
|
|||
/* ----- Registration with iwd ------------------------------------------ */
|
||||
|
||||
static void
|
||||
_on_register(void *data EINA_UNUSED, const Eldbus_Message *msg,
|
||||
_on_register(void *data, const Eldbus_Message *msg,
|
||||
Eldbus_Pending *p EINA_UNUSED)
|
||||
{
|
||||
/* `data` is the manager — same pointer the trampoline carries. */
|
||||
Iwd_Manager *m = data;
|
||||
const char *en, *em;
|
||||
if (eldbus_message_error_get(msg, &en, &em))
|
||||
if (!eldbus_message_error_get(msg, &en, &em)) return;
|
||||
if (m)
|
||||
iwd_manager_report_error(m,
|
||||
"Wi-Fi agent registration refused (another agent running?): %s",
|
||||
em ? em : en);
|
||||
else
|
||||
fprintf(stderr, "e_iwd: agent register failed: %s: %s\n", en, em);
|
||||
}
|
||||
|
||||
|
|
@ -147,7 +156,7 @@ void
|
|||
iwd_agent_register(Iwd_Agent *a)
|
||||
{
|
||||
if (!a || !a->am_proxy) return;
|
||||
eldbus_proxy_call(a->am_proxy, "RegisterAgent", _on_register, NULL, -1,
|
||||
eldbus_proxy_call(a->am_proxy, "RegisterAgent", _on_register, a->data, -1,
|
||||
"o", IWD_AGENT_PATH);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
#include "iwd_props.h"
|
||||
#include "iwd_manager.h"
|
||||
#include "iwd_network.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
|
@ -186,35 +185,72 @@ _refresh_signals(Iwd_Device *d)
|
|||
_on_ordered_networks, d, -1, "");
|
||||
}
|
||||
|
||||
static void
|
||||
_on_scan_reply(void *data, const Eldbus_Message *msg, Eldbus_Pending *p EINA_UNUSED)
|
||||
{
|
||||
Iwd_Device *d = data;
|
||||
const char *en, *em;
|
||||
if (!eldbus_message_error_get(msg, &en, &em)) return;
|
||||
/* "AlreadyExists" / "InProgress" is the normal race when two scan
|
||||
* triggers fire close together — don't spam the user with that. */
|
||||
if (en && (strstr(en, "InProgress") || strstr(en, "Busy") ||
|
||||
strstr(en, "AlreadyExists")))
|
||||
return;
|
||||
if (d->manager)
|
||||
iwd_manager_report_error(d->manager, "Scan failed: %s", em ? em : en);
|
||||
}
|
||||
|
||||
void
|
||||
iwd_device_scan(Iwd_Device *d)
|
||||
{
|
||||
if (!d || !d->station_proxy) return;
|
||||
eldbus_proxy_call(d->station_proxy, "Scan", NULL, NULL, -1, "");
|
||||
eldbus_proxy_call(d->station_proxy, "Scan", _on_scan_reply, d, -1, "");
|
||||
}
|
||||
|
||||
static void
|
||||
_on_disconnect_reply(void *data, const Eldbus_Message *msg, Eldbus_Pending *p EINA_UNUSED)
|
||||
{
|
||||
Iwd_Device *d = data;
|
||||
const char *en, *em;
|
||||
if (eldbus_message_error_get(msg, &en, &em) && d->manager)
|
||||
iwd_manager_report_error(d->manager,
|
||||
"Disconnect failed: %s", em ? em : en);
|
||||
}
|
||||
|
||||
void
|
||||
iwd_device_disconnect(Iwd_Device *d)
|
||||
{
|
||||
if (!d || !d->station_proxy) return;
|
||||
eldbus_proxy_call(d->station_proxy, "Disconnect", NULL, NULL, -1, "");
|
||||
eldbus_proxy_call(d->station_proxy, "Disconnect", _on_disconnect_reply, d, -1, "");
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Iwd_Device *d;
|
||||
char *ssid;
|
||||
} _Connect_Hidden_Ctx;
|
||||
|
||||
static void
|
||||
_on_connect_hidden_reply(void *data, const Eldbus_Message *msg, Eldbus_Pending *p EINA_UNUSED)
|
||||
{
|
||||
_Connect_Hidden_Ctx *ctx = data;
|
||||
const char *en, *em;
|
||||
char *ssid = data;
|
||||
if (eldbus_message_error_get(msg, &en, &em))
|
||||
fprintf(stderr, "e_iwd: ConnectHiddenNetwork('%s') failed: %s: %s\n",
|
||||
ssid ? ssid : "?", en, em);
|
||||
free(ssid);
|
||||
if (eldbus_message_error_get(msg, &en, &em) && ctx->d->manager)
|
||||
iwd_manager_report_error(ctx->d->manager,
|
||||
"Connect to hidden '%s' failed: %s",
|
||||
ctx->ssid ? ctx->ssid : "?", em ? em : en);
|
||||
free(ctx->ssid);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
void
|
||||
iwd_device_connect_hidden(Iwd_Device *d, const char *ssid)
|
||||
{
|
||||
if (!d || !d->station_proxy || !ssid || !*ssid) return;
|
||||
_Connect_Hidden_Ctx *ctx = calloc(1, sizeof(*ctx));
|
||||
if (!ctx) return;
|
||||
ctx->d = d;
|
||||
ctx->ssid = strdup(ssid);
|
||||
eldbus_proxy_call(d->station_proxy, "ConnectHiddenNetwork",
|
||||
_on_connect_hidden_reply, strdup(ssid), -1, "s", ssid);
|
||||
_on_connect_hidden_reply, ctx, -1, "s", ssid);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
#include "iwd_device.h"
|
||||
#include "iwd_network.h"
|
||||
#include <Ecore.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
|
@ -23,6 +25,7 @@ struct _Iwd_Manager
|
|||
Eina_List *listeners; /* Listener * */
|
||||
Iwd_State state;
|
||||
Ecore_Job *notify_job;
|
||||
char *last_error;
|
||||
|
||||
Iwd_Agent_Passphrase_Cb pass_cb;
|
||||
void *pass_data;
|
||||
|
|
@ -102,6 +105,33 @@ iwd_manager_notify(Iwd_Manager *m)
|
|||
m->notify_job = ecore_job_add(_notify_job_cb, m);
|
||||
}
|
||||
|
||||
const char *
|
||||
iwd_manager_last_error(const Iwd_Manager *m) { return m ? m->last_error : NULL; }
|
||||
|
||||
void
|
||||
iwd_manager_clear_error(Iwd_Manager *m)
|
||||
{
|
||||
if (!m || !m->last_error) return;
|
||||
free(m->last_error);
|
||||
m->last_error = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
iwd_manager_report_error(Iwd_Manager *m, const char *fmt, ...)
|
||||
{
|
||||
if (!m || !fmt) return;
|
||||
char buf[256];
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
/* stderr keeps the dev-visible trail; the stashed copy drives the UI. */
|
||||
fprintf(stderr, "e_iwd: %s\n", buf);
|
||||
free(m->last_error);
|
||||
m->last_error = strdup(buf);
|
||||
iwd_manager_notify(m);
|
||||
}
|
||||
|
||||
/* ----- state aggregation ---------------------------------------------- */
|
||||
|
||||
static void
|
||||
|
|
@ -281,6 +311,7 @@ iwd_manager_free(Iwd_Manager *m)
|
|||
eina_hash_free(m->networks);
|
||||
Listener *li;
|
||||
EINA_LIST_FREE(m->listeners, li) free(li);
|
||||
free(m->last_error);
|
||||
free(m);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,15 @@ void iwd_manager_listener_del (Iwd_Manager *m, Iwd_Manager_Cb cb, void *data);
|
|||
/* Internal: invoked by sub-objects when their state changes. */
|
||||
void iwd_manager_notify (Iwd_Manager *m);
|
||||
|
||||
/* Latest user-facing error string, or NULL. Owned by the manager.
|
||||
* Cleared on next successful state change or by iwd_manager_clear_error. */
|
||||
const char *iwd_manager_last_error (const Iwd_Manager *m);
|
||||
void iwd_manager_clear_error(Iwd_Manager *m);
|
||||
|
||||
/* Stash a one-shot error message and notify listeners. Used by D-Bus reply
|
||||
* callbacks when iwd refuses a Connect/Forget/Set(Powered)/etc. */
|
||||
void iwd_manager_report_error(Iwd_Manager *m, const char *fmt, ...);
|
||||
|
||||
/* The UI installs its passphrase prompt here. The handler must
|
||||
* eventually call iwd_agent_reply()/iwd_agent_cancel() with the request. */
|
||||
void iwd_manager_set_passphrase_handler(Iwd_Manager *m,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
#include "iwd_dbus.h"
|
||||
#include "iwd_props.h"
|
||||
#include "iwd_manager.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
|
@ -80,15 +79,36 @@ iwd_network_free(Iwd_Network *n)
|
|||
free(n);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Iwd_Network *n;
|
||||
char *ssid;
|
||||
} _Net_Reply_Ctx;
|
||||
|
||||
static void
|
||||
_on_connect_reply(void *data, const Eldbus_Message *msg, Eldbus_Pending *p EINA_UNUSED)
|
||||
{
|
||||
_Net_Reply_Ctx *ctx = data;
|
||||
const char *en, *em;
|
||||
const char *ssid = data;
|
||||
if (eldbus_message_error_get(msg, &en, &em))
|
||||
fprintf(stderr, "e_iwd: connect to '%s' failed: %s: %s\n",
|
||||
ssid ? ssid : "?", en, em);
|
||||
free(data);
|
||||
if (eldbus_message_error_get(msg, &en, &em) && ctx->n->manager)
|
||||
iwd_manager_report_error(ctx->n->manager,
|
||||
"Connect to '%s' failed: %s",
|
||||
ctx->ssid ? ctx->ssid : "?", em ? em : en);
|
||||
free(ctx->ssid);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
_on_forget_reply(void *data, const Eldbus_Message *msg, Eldbus_Pending *p EINA_UNUSED)
|
||||
{
|
||||
_Net_Reply_Ctx *ctx = data;
|
||||
const char *en, *em;
|
||||
if (eldbus_message_error_get(msg, &en, &em) && ctx->n->manager)
|
||||
iwd_manager_report_error(ctx->n->manager,
|
||||
"Forget '%s' failed: %s",
|
||||
ctx->ssid ? ctx->ssid : "?", em ? em : en);
|
||||
free(ctx->ssid);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
int
|
||||
|
|
@ -104,6 +124,16 @@ iwd_network_signal_tier(const Iwd_Network *n)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static _Net_Reply_Ctx *
|
||||
_reply_ctx_new(Iwd_Network *n)
|
||||
{
|
||||
_Net_Reply_Ctx *ctx = calloc(1, sizeof(*ctx));
|
||||
if (!ctx) return NULL;
|
||||
ctx->n = n;
|
||||
ctx->ssid = n->ssid ? strdup(n->ssid) : NULL;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void
|
||||
iwd_network_connect(Iwd_Network *n)
|
||||
{
|
||||
|
|
@ -111,7 +141,7 @@ iwd_network_connect(Iwd_Network *n)
|
|||
/* Network.Connect() takes no args; iwd will dial the registered Agent
|
||||
* for a passphrase if needed. */
|
||||
eldbus_proxy_call(n->proxy, "Connect", _on_connect_reply,
|
||||
n->ssid ? strdup(n->ssid) : NULL, -1, "");
|
||||
_reply_ctx_new(n), -1, "");
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -124,7 +154,7 @@ iwd_network_forget(Iwd_Network *n)
|
|||
Eldbus_Proxy *kp = eldbus_proxy_get(kobj, IWD_IFACE_KNOWN_NETWORK);
|
||||
if (kp)
|
||||
{
|
||||
eldbus_proxy_call(kp, "Forget", NULL, NULL, -1, "");
|
||||
eldbus_proxy_call(kp, "Forget", _on_forget_reply, _reply_ctx_new(n), -1, "");
|
||||
eldbus_proxy_unref(kp);
|
||||
}
|
||||
eldbus_object_unref(kobj);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue