[RFCv3] Document P2P dbus interfaces
by Andrew Zaborowski
From: Andrew Zaborowski <andrew.zaborowski(a)intel.com>
Proposed minimum P2P interfaces for establishing basic connections. The
device discovery results in creation of P2PPeer objects.
In the Wi-Fi Display API we are passing raw IE data because there's a
relatively big set of different values that may be encoded in them. We
could reduce them to 2-3 bools and integers but this might limit the
client implementations feature set.
---
doc/p2p-api.txt | 82 ++++++++++++++++++++++++++++++++++
doc/p2p-peer-api.txt | 66 +++++++++++++++++++++++++++
doc/p2p-wfd-agent-api.txt | 34 ++++++++++++++
doc/signal-level-agent-api.txt | 4 ++
4 files changed, 186 insertions(+)
create mode 100644 doc/p2p-api.txt
create mode 100644 doc/p2p-peer-api.txt
create mode 100644 doc/p2p-wfd-agent-api.txt
diff --git a/doc/p2p-api.txt b/doc/p2p-api.txt
new file mode 100644
index 00000000..84f862e5
--- /dev/null
+++ b/doc/p2p-api.txt
@@ -0,0 +1,82 @@
+P2P hierarchy
+=============
+
+Service net.connman.iwd
+Interface net.connman.iwd.P2P [Experimental]
+Object path /{phy0,phy1,...}
+
+Methods array(on) GetPeers()
+
+ Returns a list (possibly empty) of detected P2P peers.
+ Each record returned contains a tuple of the following
+ values.
+
+ object Object
+
+ net.connman.iwd.P2PPeer object representing
+ the peer device.
+
+ int16 SignalStrength
+
+ Peer's maximum signal strength expressed
+ in 100 * dBm. The value is the range of 0
+ (strongest signal) to -10000 (weakest signal)
+
+ void RegisterSignalLevelAgent(object path,
+ array(int16) levels)
+
+ Register the agent object to receive signal strength
+ level change notifications on the
+ net.connman.iwd.SignalLevelAgent interface, see
+ signal-level-agent-api.txt. The "levels"
+ parameters decides the thresholds in dBm that will
+ generate a call to the agent's Changed
+ method whenever current RSSI crosses any of the
+ values. The values must be passed in descending
+ order. The number and distance between requested
+ threshold values is a compromise between resolution
+ and the frequency of system wakeups and
+ context-switches that are going to be occuring to
+ update the client's signal meter. Only one agent
+ can be registered at any time.
+
+ Possible Errors: [service].Error.InvalidArguments
+ [service].Error.Failed
+ [service].Error.AlreadyExists
+ [service].Error.NotSupported
+
+ void RegisterWFDService(object path, array(byte) payload)
+
+ Register the Wi-Fi Display service running on local
+ host with this P2P device so that device discovery
+ and connection setup phases include necessary WFD
+ information for the peers and receive peer's WFD
+ service information. An object may be provided to
+ receive relevant WFD information about peers and
+ connections through method calls on the object's
+ net.connman.iwd.P2PWFDAgent interface. See
+ p2p-wfd-agent-api.txt. Only one agent can be
+ registered at any time. The byte array provided
+ will be included in WFD Information Elements
+ present in relevant frames sent to peers.
+
+ Possible Errors: [service].Error.InvalidArguments
+ [service].Error.AlreadyExists
+ [service].Error.NotSupported
+
+Properties boolean Enabled [readwrite]
+
+ Whether local P2P device is started, i.e. is scanning
+ for peers, is discoverable by peers and/or connected
+ to peer(s).
+
+ string Name [readwrite]
+
+ Sets local P2P device name as it is going to be
+ presented on other devices that we will connect to
+ or ones that discover us in scanning.
+
+ uint16 MaxConnections [readonly]
+
+ Maximum number of concurrent P2P peers that local
+ hardware is capable of connecting to. Often 1.
diff --git a/doc/p2p-peer-api.txt b/doc/p2p-peer-api.txt
new file mode 100644
index 00000000..898f4fb9
--- /dev/null
+++ b/doc/p2p-peer-api.txt
@@ -0,0 +1,66 @@
+P2PPeer hierarchy
+=================
+
+Service net.connman.iwd
+Interface net.connman.iwd.P2PPeer [Experimental]
+Object path /{phy0,phy1,...}/p2p_peers/{aa_bb_cc_dd_ee_ff}
+
+Methods ConnectPushButton()
+
+ Connect to the P2P peer in the Push Button mode
+ using the device pointed to by the .Device property.
+ Returns when connection is complete.
+
+ Possible errors: net.connman.iwd.Aborted
+ net.connman.iwd.Busy
+ net.connman.iwd.Failed
+ net.connman.iwd.NotSupported
+ net.connman.iwd.Timeout
+ net.connman.iwd.InProgress
+
+ ConnectPIN(string pin)
+
+ Connect to the P2P peer in PIN mode. Returns when
+ connection is complete.
+
+ See net.connman.iwd.WiFiSimpleConfiguration.StartPIN()
+ for how the pin argument is used.
+
+ Possible errors: net.connman.iwd.Aborted
+ net.connman.iwd.Busy
+ net.connman.iwd.Failed
+ net.connman.iwd.NotSupported
+ net.connman.iwd.Timeout
+ net.connman.iwd.InProgress
+
+ Disconnect() [optional]
+
+ If connected, disconnect from this peer.
+
+ Possible errors: net.connman.iwd.Failed
+
+Properties string Name [readonly]
+
+ P2P Peer's display name
+
+ string DeviceCategory [readonly]
+
+ The category part of the peer's declared
+ Primary Device Type.
+
+ string DeviceSubcategory [readonly, optional]
+
+ The Sub Category part of the peer's declared
+ Primary Device Type.
+
+ object Device [readonly]
+
+ The object with a net.connman.iwd.P2P interface
+ that discovered this peer.
+
+ boolean Connected [readonly]
+
+ Whether there's currently an active connection
+ to this peer. The property is read-only and
+ changes as a result of the Connect and
+ Disconnect methods calls.
diff --git a/doc/p2p-wfd-agent-api.txt b/doc/p2p-wfd-agent-api.txt
new file mode 100644
index 00000000..be0ee01b
--- /dev/null
+++ b/doc/p2p-wfd-agent-api.txt
@@ -0,0 +1,34 @@
+P2PWFDAgent hierarchy
+=====================
+
+Service unique name
+Interface net.connman.iwd.P2PWFDAgent
+Object path freely definable
+
+Methods void Release(object device)
+
+ This method gets called when IWD unregisters the
+ WFD service on a specific P2P device. An agent can
+ use it to do cleanup tasks. There is no need to
+ unregister the agent, because when this method
+ gets called it has already been unregistered.
+
+ void WFDPeersChanged(object device,
+ array(object, array(byte)) peers)
+
+ Called when the set of discovered WFD-capable
+ peers has changed during device discovery.
+
+ The device object has the net.connman.iwd.P2P
+ interface while the objects in the peers array
+ have net.connman.iwd.P2PPeer interfaces. The byte
+ array included contains the reassembled payload of
+ the WFD Information Elements presented by the peer.
+
+ void NewConnection(object peer, array(byte) payload)
+
+ Called when a new P2P connection has been established
+ to a WFD-capable peer. The peer object has the
+ net.connman.iwd.P2PPerr interface. The byte array
+ contains the reassembled payload of the WFD
+ Information Elements presented by the peer.
diff --git a/doc/signal-level-agent-api.txt b/doc/signal-level-agent-api.txt
index 847e7ca3..1c786d96 100644
--- a/doc/signal-level-agent-api.txt
+++ b/doc/signal-level-agent-api.txt
@@ -31,3 +31,7 @@ Methods void Release(object device)
0 would mean signal is received at -40 or more dBm
and 3 would mean below -60 dBm and might correspond
to 1 out of 4 bars on a UI signal meter.
+
+ The device parameter may be either a
+ net.connman.iwd.Station object or a
+ net.connman.iwd.P2PPeer object.
--
2.20.1
1 year, 4 months
[PATCH 1/5] auto-t: support hostapd event processing
by James Prestwood
Hostapd has a feature where you can connect to its control socket and
receive events it generates. Currently we only send commands via this
socket.
First we open the socket (/var/run/hostapd/<iface>) and send the
ATTACH command. This tells hostapd we are ready and after this any
events will be sent over this socket.
A new API, wait_for_event, was added which takes an event string and
waits for some timeout. The glib event loop has been integrated into
this, though its not technically async since we are selecting over a
socket which blocks. To mitigate this a small timeout was chosen for
each select call and then wrapped in a while loop which waits for the
full timeout.
---
autotests/util/hostapd.py | 68 +++++++++++++++++++++++++++++++++++++++
1 file changed, 68 insertions(+)
diff --git a/autotests/util/hostapd.py b/autotests/util/hostapd.py
index 209d8c24..49047f38 100644
--- a/autotests/util/hostapd.py
+++ b/autotests/util/hostapd.py
@@ -3,6 +3,9 @@ import os, os.path
from wiphy import wiphy_map
import re
import socket
+import select
+import time
+from gi.repository import GLib
chan_freq_map = [
None,
@@ -22,12 +25,17 @@ chan_freq_map = [
2484
]
+ctrl_count = 0
+mainloop = GLib.MainLoop()
+
hostapd_map = {ifname: intf for wname, wiphy in wiphy_map.items()
for ifname, intf in wiphy.interface_map.items()
if wiphy.use == 'hostapd'}
class HostapdCLI:
def __init__(self, interface=None, config=None):
+ global ctrl_count
+
if not interface and not config:
raise Exception('interface or config must be provided')
@@ -48,10 +56,64 @@ class HostapdCLI:
self._hostapd_restarted = False
+ self.local_ctrl = '/tmp/hostapd_' + str(os.getpid()) + '_' + \
+ str(ctrl_count)
+ self.ctrl_sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
+ self.ctrl_sock.bind(self.local_ctrl)
+ self.ctrl_sock.connect(self.socket_path + '/' + self.ifname)
+
+ if 'OK' not in self._ctrl_request('ATTACH'):
+ raise Exception('ATTACH failed')
+
+ ctrl_count = ctrl_count + 1
+
+ def wait_for_event(self, event, timeout=10):
+ global mainloop
+ self._wait_timed_out = False
+
+ def wait_timeout_cb():
+ self._wait_timed_out = True
+ return False
+
+ timeout = GLib.timeout_add_seconds(timeout, wait_timeout_cb)
+ context = mainloop.get_context()
+
+ while True:
+ context.iteration(may_block=False)
+
+ while self._data_available(0.25):
+ data = self.ctrl_sock.recv(4096).decode('utf-8')
+ if event in data:
+ return data
+
+ if self._wait_timed_out:
+ raise TimeoutError('waiting for hostapd event timed out')
+
+ return None
+
+ def _data_available(self, timeout=2):
+ [r, w, e] = select.select([self.ctrl_sock], [], [], timeout)
+ if r:
+ return True
+ return False
+
+ def _ctrl_request(self, command, timeout=10):
+ if type(command) is str:
+ command = str.encode(command)
+
+ self.ctrl_sock.send(bytes(command))
+
+ if self._data_available(timeout):
+ return self.ctrl_sock.recv(4096).decode('utf-8')
+
+ raise Exception('timeout waiting for control response')
+
def __del__(self):
if self._hostapd_restarted:
os.system('killall hostapd')
+ self.ctrl_sock.close()
+
def wps_push_button(self):
os.system(self.cmdline + ' wps_pbc')
@@ -140,3 +202,9 @@ class HostapdCLI:
# set flag so hostapd can be killed after the test
self._hostapd_restarted = True
+
+ def req_beacon(self, addr, request):
+ '''
+ Send a RRM Beacon request
+ '''
+ os.system(self.cmdline + ' req_beacon ' + addr + ' ' + request)
--
2.17.1
1 year, 5 months
[PATCH 01/16] wiphy: Add wiphy_get_supported_rates
by Andrew Zaborowski
Add code to parse the supported data rates info from the wiphy dumps and
expose it for P2P's use with a getter function.
---
src/wiphy.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++---
src/wiphy.h | 2 ++
2 files changed, 82 insertions(+), 4 deletions(-)
diff --git a/src/wiphy.c b/src/wiphy.c
index 3ec9ef4f..a6d6b6a0 100644
--- a/src/wiphy.c
+++ b/src/wiphy.c
@@ -76,6 +76,7 @@ struct wiphy {
struct watchlist state_watches;
uint8_t extended_capabilities[EXT_CAP_LEN + 2]; /* max bitmap size + IE header */
uint8_t *iftype_extended_capabilities[NUM_NL80211_IFTYPES];
+ uint8_t *supported_rates[NUM_NL80211_BANDS];
uint8_t rm_enabled_capabilities[7]; /* 5 size max + header */
bool support_scheduled_scan:1;
@@ -212,6 +213,9 @@ static void wiphy_free(void *data)
for (i = 0; i < NUM_NL80211_IFTYPES; i++)
l_free(wiphy->iftype_extended_capabilities[i]);
+ for (i = 0; i < NUM_NL80211_BANDS; i++)
+ l_free(wiphy->supported_rates[i]);
+
scan_freq_set_free(wiphy->supported_freqs);
watchlist_destroy(&wiphy->state_watches);
l_free(wiphy->model_str);
@@ -478,6 +482,20 @@ bool wiphy_supports_iftype(struct wiphy *wiphy, uint32_t iftype)
return wiphy->supported_iftypes & (1 << (iftype - 1));
}
+const uint8_t *wiphy_get_supported_rates(struct wiphy *wiphy, unsigned int band,
+ unsigned int *out_num)
+{
+ if (band >= L_ARRAY_SIZE(wiphy->supported_rates))
+ return NULL;
+
+ if (out_num)
+ *out_num =
+ (uint8_t *) rawmemchr(wiphy->supported_rates[band], 0) -
+ wiphy->supported_rates[band];
+
+ return wiphy->supported_rates[band];
+}
+
uint32_t wiphy_state_watch_add(struct wiphy *wiphy,
wiphy_state_watch_func_t func,
void *user_data, wiphy_destroy_func_t destroy)
@@ -622,20 +640,70 @@ static void parse_supported_frequencies(struct wiphy *wiphy,
}
}
+static uint8_t *parse_supported_rates(struct l_genl_attr *attr)
+{
+ uint16_t type;
+ uint16_t len;
+ const void *data;
+ struct l_genl_attr nested;
+ int count = 0;
+ uint8_t *ret;
+
+ if (!l_genl_attr_recurse(attr, &nested))
+ return NULL;
+
+ while (l_genl_attr_next(&nested, NULL, NULL, NULL))
+ count++;
+
+ if (!l_genl_attr_recurse(attr, &nested))
+ return NULL;
+
+ ret = l_malloc(count + 1);
+ ret[count] = 0;
+
+ count = 0;
+
+ while (l_genl_attr_next(&nested, NULL, NULL, NULL)) {
+ struct l_genl_attr nested2;
+
+ if (!l_genl_attr_recurse(&nested, &nested2)) {
+ l_free(ret);
+ return NULL;
+ }
+
+ while (l_genl_attr_next(&nested2, &type, &len, &data)) {
+ if (type != NL80211_BITRATE_ATTR_RATE || len != 4)
+ continue;
+
+ /*
+ * Convert from the 100kb/s units reported by the
+ * kernel to the 500kb/s used in 802.11 IEs.
+ */
+ ret[count++] = *(const uint32_t *) data / 5;
+ }
+ }
+
+ return ret;
+}
+
static void parse_supported_bands(struct wiphy *wiphy,
struct l_genl_attr *bands)
{
- uint16_t type, len;
- const void *data;
+ uint16_t type;
struct l_genl_attr attr;
l_debug("");
- while (l_genl_attr_next(bands, NULL, NULL, NULL)) {
+ while (l_genl_attr_next(bands, &type, NULL, NULL)) {
+ enum nl80211_band band = type;
+
+ if (band != NL80211_BAND_2GHZ && band != NL80211_BAND_5GHZ)
+ continue;
+
if (!l_genl_attr_recurse(bands, &attr))
continue;
- while (l_genl_attr_next(&attr, &type, &len, &data)) {
+ while (l_genl_attr_next(&attr, &type, NULL, NULL)) {
struct l_genl_attr freqs;
switch (type) {
@@ -645,6 +713,14 @@ static void parse_supported_bands(struct wiphy *wiphy,
parse_supported_frequencies(wiphy, &freqs);
break;
+
+ case NL80211_BAND_ATTR_RATES:
+ if (wiphy->supported_rates[band])
+ continue;
+
+ wiphy->supported_rates[band] =
+ parse_supported_rates(&attr);
+ break;
}
}
}
diff --git a/src/wiphy.h b/src/wiphy.h
index 7287cddd..85fa3f56 100644
--- a/src/wiphy.h
+++ b/src/wiphy.h
@@ -66,6 +66,8 @@ bool wiphy_has_ext_feature(struct wiphy *wiphy, uint32_t feature);
uint8_t wiphy_get_max_num_ssids_per_scan(struct wiphy *wiphy);
uint32_t wiphy_get_max_roc_duration(struct wiphy *wiphy);
bool wiphy_supports_iftype(struct wiphy *wiphy, uint32_t iftype);
+const uint8_t *wiphy_get_supported_rates(struct wiphy *wiphy, unsigned int band,
+ unsigned int *out_num);
bool wiphy_supports_adhoc_rsn(struct wiphy *wiphy);
bool wiphy_can_offchannel_tx(struct wiphy *wiphy);
bool wiphy_supports_qos_set_map(struct wiphy *wiphy);
--
2.20.1
1 year, 5 months
[PATCH] knownnetworks: Check result of setting getter
by Tim Kourt
Set the value of 'is_hidden' if necessary.
---
src/knownnetworks.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/knownnetworks.c b/src/knownnetworks.c
index e5cbc01b..d8d6947d 100644
--- a/src/knownnetworks.c
+++ b/src/knownnetworks.c
@@ -338,7 +338,7 @@ void known_network_update(struct network_info *network,
struct l_settings *settings,
uint64_t connected_time)
{
- bool is_hidden = false;
+ bool is_hidden;
bool is_autoconnectable;
if (network->connected_time != connected_time) {
@@ -354,7 +354,8 @@ void known_network_update(struct network_info *network,
network->connected_time = connected_time;
- l_settings_get_bool(settings, "Settings", "Hidden", &is_hidden);
+ if (!l_settings_get_bool(settings, "Settings", "Hidden", &is_hidden))
+ is_hidden = false;
if (network->is_hidden != is_hidden) {
if (network->is_hidden && !is_hidden)
--
2.13.6
1 year, 5 months
[PATCH v2 1/2] client: Increase passphrase buffer to accommodate for nil byte
by Tim Kourt
---
client/display.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/client/display.c b/client/display.c
index cd17ad84..105d05cb 100644
--- a/client/display.c
+++ b/client/display.c
@@ -397,7 +397,7 @@ static void display_completion_matches(char **matches, int num_matches,
static struct masked_input {
bool use_mask;
- char passphrase[MAX_PASSPHRASE_LEN];
+ char passphrase[MAX_PASSPHRASE_LEN + 1];
uint8_t point;
uint8_t end;
} masked_input;
@@ -453,7 +453,7 @@ done:
static void reset_masked_input(void)
{
- memset(masked_input.passphrase, 0, MAX_PASSPHRASE_LEN);
+ memset(masked_input.passphrase, 0, MAX_PASSPHRASE_LEN + 1);
masked_input.point = 0;
masked_input.end = 0;
}
--
2.13.6
1 year, 5 months
[PATCH 1/2] client: Increase passphrase buffer to accommodate for nil byte
by Tim Kourt
---
client/display.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/client/display.c b/client/display.c
index cd17ad84..74b50e2e 100644
--- a/client/display.c
+++ b/client/display.c
@@ -397,7 +397,7 @@ static void display_completion_matches(char **matches, int num_matches,
static struct masked_input {
bool use_mask;
- char passphrase[MAX_PASSPHRASE_LEN];
+ char passphrase[MAX_PASSPHRASE_LEN + 1];
uint8_t point;
uint8_t end;
} masked_input;
@@ -436,6 +436,7 @@ static void mask_input(void)
masked_input.passphrase + masked_input.point
+ 1,
rl_end - rl_point);
+
memset(masked_input.passphrase + rl_end, 0,
masked_input.end - rl_end);
}
@@ -453,7 +454,7 @@ done:
static void reset_masked_input(void)
{
- memset(masked_input.passphrase, 0, MAX_PASSPHRASE_LEN);
+ memset(masked_input.passphrase, 0, MAX_PASSPHRASE_LEN + 1);
masked_input.point = 0;
masked_input.end = 0;
}
--
2.13.6
1 year, 5 months
[PATCH] manpage: Add command options and examples for iwctl
by Tim Kourt
---
client/iwctl.rst | 50 ++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 48 insertions(+), 2 deletions(-)
diff --git a/client/iwctl.rst b/client/iwctl.rst
index 51873965..54af4b7d 100644
--- a/client/iwctl.rst
+++ b/client/iwctl.rst
@@ -16,12 +16,58 @@ Internet wireless control utility
SYNOPSIS
========
-**iwctl** [*options* ...]
+**iwctl** [*options* ...] [*commands* ...]
DESCRIPTION
===========
-Tool for configuring **iwd** daemon via D-Bus interface.
+Tool for configuring **iwd** daemon via D-Bus interface. It supports both an
+interactive mode and command line mode.
+
+OPTIONS
+=======
+
+--username, -u Provide username.
+--password, -p Provide password.
+--passphrase, -P Provide passphrase.
+--dont-ask, -v Don’t ask for missing credentials.
+--help, -h Show help message and exit.
+
+EXAMPLES
+========
+
+Interactive mode
+----------------
+
+To start an interactive mode and list all available commands do:
+.. code-block::
+
+ $ iwctl
+ [iwd]# help
+
+To connect to a network:
+.. code-block::
+
+ [iwd]# device list
+ [iwd]# station DEVICE scan
+ [iwd]# station DEVICE get-networks
+ [iwd]# station DEVICE connect SSID
+
+Command line mode
+----------------------
+
+To list all available commands in command line mode and exit do:
+.. code-block::
+
+ $ iwctl --help
+
+To connect to a network:
+.. code-block::
+
+ $ iwctl device list
+ $ iwctl station DEVICE scan
+ $ iwctl station DEVICE get-networks
+ $ iwctl --passphrase=PASSPHRASE station DEVICE connect SSID
SEE ALSO
========
--
2.13.6
1 year, 5 months
[PATCH] client: Separate command option words by dash
by Tim Kourt
---
client/command.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/client/command.h b/client/command.h
index 2161c075..d906dc9b 100644
--- a/client/command.h
+++ b/client/command.h
@@ -23,7 +23,7 @@
#define COMMAND_OPTION_USERNAME "username"
#define COMMAND_OPTION_PASSWORD "password"
#define COMMAND_OPTION_PASSPHRASE "passphrase"
-#define COMMAND_OPTION_DONTASK "dontask"
+#define COMMAND_OPTION_DONTASK "dont-ask"
typedef char *(*command_completion_func_t) (const char *text, int state);
--
2.13.6
1 year, 5 months
[PATCH] auto-t: Update the base paths for iwd
by Tim Kourt
---
autotests/util/iwd.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/autotests/util/iwd.py b/autotests/util/iwd.py
index 35e5a693..abcdcbc1 100755
--- a/autotests/util/iwd.py
+++ b/autotests/util/iwd.py
@@ -36,8 +36,8 @@ IWD_AP_INTERFACE = 'net.connman.iwd.AccessPoint'
IWD_ADHOC_INTERFACE = 'net.connman.iwd.AdHoc'
IWD_STATION_INTERFACE = 'net.connman.iwd.Station'
-IWD_AGENT_MANAGER_PATH = '/'
-IWD_TOP_LEVEL_PATH = '/'
+IWD_AGENT_MANAGER_PATH = '/net/connman/iwd'
+IWD_TOP_LEVEL_PATH = '/net/connman/iwd'
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
--
2.13.6
1 year, 5 months
[PATCH] display: fix sources of crashing in completion callback
by Will Dietz
* For some reason, the max_length parameter provided by readline
(readline8.0p1) is smaller than max(strlen(matches[*]))?
This seems like a bug but I'm not sure-- for now add a workaround
and compute our own max_length value. This might somehow not be a
bug, perhaps measuring string length differently than raw strlen().
Erring on defensive to ensure iwd isn't vulnerable to cleverly
named SSID's (or set of SSID's).
* Assert in various bad situations, until this can be rewritten
so that uncertain code under development is less user-hostile.
* Comment that if ssid name can be >= 81 it'll overflow the buffer
I don't know if this is a thing to worry about.
* Fix wrapping check: in particular the inserted newline requires
space (1char) and printf will write (up to) max_length + 1.
Ensure space is available for all of this.
This is not pretty, but resolves the issues I've encountered thus far.
These loops take some attention to get right, which is probably why
various line-editing libraries try to handle this where possible.
AFAIK the code here exists in order to work with custom printing code
but FWIW readline and similar have routines for this
(used by default, I think?) which may be worth taking inspiration from
if licenses are compatible and no one wants to tackle this.
-----
Motivated by using `iwctl`, and attempting to complete with prefix
`station wlan0 connect <tab>`. Problem was specific to location
(-> list of avail SSIDs and their names), I'll share anything
helpful from debuging logs or by trying to reproduce again
next time I'm able.
---
client/display.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/client/display.c b/client/display.c
index c08183da..b6693681 100644
--- a/client/display.c
+++ b/client/display.c
@@ -30,6 +30,7 @@
#include <readline/history.h>
#include <readline/readline.h>
+#include <assert.h>
#include <ell/ell.h>
#include "agent.h"
@@ -364,6 +365,7 @@ static void display_completion_matches(char **matches, int num_matches,
char line[LINE_LEN];
size_t index;
size_t line_used;
+ size_t match_len;
char *input = rl_copy_text(0, rl_end);
prompt = l_strdup_printf("%s%s\n", IWD_PROMPT, input);
@@ -372,8 +374,16 @@ static void display_completion_matches(char **matches, int num_matches,
display_text(prompt);
l_free(prompt);
+ // Fix max_length, account for quoting (apparently??)
+ for (index = 1; matches[index]; index++) {
+ match_len = strlen(matches[index]);
+ max_length = match_len > max_length ? match_len : max_length;
+ }
+
for (index = 1, line_used = 0; matches[index]; index++) {
- if ((line_used + max_length) > LINE_LEN) {
+ assert(index <= num_matches);
+ assert(strlen(matches[index]) <= max_length);
+ if ((line_used + max_length + 1 + 1 /* :( */) >= LINE_LEN) {
strcpy(&line[line_used], "\n");
display_text(line);
@@ -381,10 +391,11 @@ static void display_completion_matches(char **matches, int num_matches,
line_used = 0;
}
+ // XXX: entry > LINE_LEN, if possible, will overflow 'line'! :(
entry = l_strdup_printf("%-*s ", max_length, matches[index]);
+ assert(strlen(entry) == max_length + 1);
strcpy(&line[line_used], entry);
l_free(entry);
-
line_used += max_length + 1;
}
--
2.23.0
1 year, 5 months