blob: 9cd5e971ffdc47f7471e44b1782c2af9d0e9a922 [file] [log] [blame]
/*
*
* Copyright (c) 2016 Nest Labs, Inc.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <getopt.h>
#include "wpanctl-utils.h"
#include "tool-cmd-scan.h"
#include "assert-macros.h"
#include "wpan-dbus-v1.h"
#include "string-utils.h"
#include "args.h"
const char scan_cmd_syntax[] = "[args] [seconds-to-scan]";
static const arg_list_item_t scan_option_list[] = {
{'h', "help", NULL, "Print Help"},
{'t', "timeout", "ms", "Set timeout period"},
{'c', "channel", "channel", "Set the desired channel"},
{'e', "energy", NULL, "Perform an energy scan"},
{'d', "discover", NULL, "Perform a MLE discover scan"},
{'j', "joiner-only", NULL, "Scan for joiner only devices (used in discover scan)"},
{'f', "enable-filtering", NULL, "Enable scan result filtering (used in discover scan)"},
{'p', "panid-filtering", NULL, "PANID used for filtering, 0xFFFF to disable (used in discover scan)"},
{0}
};
int gScannedNetworkCount = 0;
struct wpan_network_info_s gScannedNetworks[SCANNED_NET_BUFFER_SIZE];
static bool sEnergyScan;
static bool sMleDiscoverScan;
static void
print_scan_header(void)
{
if (sEnergyScan) {
printf(" Ch | RSSI\n");
printf(" ----+-------\n");
} else {
printf( " | NetworkName | PAN ID | Ch | XPanID | HWAddr | RSSI\n");
printf( "---+--------------------+--------+----+------------------+------------------+------\n");
}
}
static DBusHandlerResult
dbus_beacon_handler(
DBusConnection *connection,
DBusMessage * message,
void * user_data
) {
DBusMessageIter iter;
int ret;
struct wpan_network_info_s network_info;
if (!dbus_message_is_signal(message, WPANTUND_DBUS_APIv1_INTERFACE, WPANTUND_IF_SIGNAL_NET_SCAN_BEACON)) {
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
dbus_message_iter_init(message, &iter);
ret = parse_network_info_from_iter(&network_info, &iter);
require_noerr(ret, bail);
if (network_info.network_name[0]) {
if (gScannedNetworkCount < SCANNED_NET_BUFFER_SIZE) {
gScannedNetworks[gScannedNetworkCount++] = network_info;
printf("%2d", gScannedNetworkCount);
} else {
printf("--"); //This means that we cannot act on the PAN as we do not save the info
}
} else {
printf(" ");
}
if (network_info.network_name[0]) {
printf(" | \"%s\"%s",
network_info.network_name,
&" "[strlen(network_info.network_name)]);
} else {
printf(" | ------ NONE ------");
}
printf(" | 0x%04X", network_info.pan_id);
printf(" | %2d", network_info.channel);
printf(" | %016llX", (unsigned long long)network_info.xpanid);
printf(" | %02X%02X%02X%02X%02X%02X%02X%02X",
network_info.hwaddr[0],
network_info.hwaddr[1],
network_info.hwaddr[2],
network_info.hwaddr[3],
network_info.hwaddr[4],
network_info.hwaddr[5],
network_info.hwaddr[6],
network_info.hwaddr[7]);
printf(" | %4d", network_info.rssi);
printf("\n");
bail:
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult
dbus_energy_scan_handler(
DBusConnection *connection,
DBusMessage * message,
void * user_data
) {
DBusMessageIter iter;
int ret;
int16_t channel;
int8_t maxRssi;
if (!dbus_message_is_signal(message, WPANTUND_DBUS_APIv1_INTERFACE, WPANTUND_IF_SIGNAL_ENERGY_SCAN_RESULT)) {
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
dbus_message_iter_init(message, &iter);
ret = parse_energy_scan_result_from_iter(&channel, &maxRssi, &iter);
require_noerr(ret, bail);
printf(" %4d | %4d\n", channel, maxRssi);
bail:
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult
dbus_signal_handler(
DBusConnection *connection,
DBusMessage * message,
void * user_data
) {
if (sEnergyScan) {
return dbus_energy_scan_handler(connection, message, user_data);
}
return dbus_beacon_handler(connection, message, user_data);
}
static const char gDBusObjectManagerMatchString[] =
"type='signal'"
// ",interface='" WPANTUND_DBUS_APIv1_INTERFACE "'"
;
int tool_cmd_scan(int argc, char *argv[])
{
int ret = 0;
int c;
int timeout = DEFAULT_TIMEOUT_IN_SECONDS * 1000;
const char *method_name;
DBusConnection* connection = NULL;
DBusMessage *message = NULL;
DBusMessage *reply = NULL;
DBusPendingCall *pending = NULL;
DBusError error;
int32_t scan_period = 0;
uint32_t channel_mask = 0;
dbus_bool_t joiner_flag = FALSE;
dbus_bool_t enable_filtering = FALSE;
uint16_t pan_id_filter = 0xffff;
dbus_error_init(&error);
sEnergyScan = false;
sMleDiscoverScan = false;
while (1) {
static struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"timeout", required_argument, 0, 't'},
{"channel", required_argument, 0, 'c'},
{"energy", no_argument, 0, 'e'},
{"discover", no_argument, 0, 'd'},
{"joiner-only", no_argument, 0, 'j'},
{"enable-filtering", no_argument, 0, 'f'},
{"panid-filtering", required_argument, 0, 'p'},
{0, 0, 0, 0}
};
int option_index = 0;
c = getopt_long(argc, argv, "hc:t:edjfp:", long_options,
&option_index);
if (c == -1)
break;
switch (c) {
case 'h':
print_arg_list_help(scan_option_list, argv[0],
scan_cmd_syntax);
ret = ERRORCODE_HELP;
goto bail;
case 't':
timeout = strtol(optarg, NULL, 0);
break;
case 'c':
channel_mask = strtomask_uint32(optarg);
break;
case 'e':
sEnergyScan = true;
break;
case 'd':
sMleDiscoverScan = true;
break;
case 'j':
joiner_flag = TRUE;
break;
case 'f':
enable_filtering = TRUE;
break;
case 'p':
pan_id_filter = strtol(optarg, NULL, 0);
break;
}
}
if (optind < argc) {
if (scan_period == 0) {
scan_period = strtol(argv[optind], NULL, 0);
optind++;
}
}
if (optind < argc) {
fprintf(stderr,
"%s: error: Unexpected extra argument: \"%s\"\n",
argv[0], argv[optind]);
ret = ERRORCODE_BADARG;
goto bail;
}
if (gInterfaceName[0] == 0) {
fprintf(stderr,
"%s: error: No WPAN interface set (use the `cd` command, or the `-I` argument for `wpanctl`).\n",
argv[0]);
ret = ERRORCODE_BADARG;
goto bail;
}
connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
require_string(connection != NULL, bail, error.message);
dbus_bus_add_match(connection, gDBusObjectManagerMatchString, &error);
require_string(error.name == NULL, bail, error.message);
dbus_connection_add_filter(connection, &dbus_signal_handler, NULL, NULL);
{
char path[DBUS_MAXIMUM_NAME_LENGTH+1];
char interface_dbus_name[DBUS_MAXIMUM_NAME_LENGTH+1];
DBusMessageIter iter;
ret = lookup_dbus_name_from_interface(interface_dbus_name, gInterfaceName);
if (ret != 0) {
print_error_diagnosis(ret);
goto bail;
}
snprintf(
path,
sizeof(path),
"%s/%s",
WPANTUND_DBUS_PATH,
gInterfaceName
);
if (sEnergyScan) {
method_name = WPANTUND_IF_CMD_ENERGY_SCAN_START;
} else {
if (sMleDiscoverScan) {
method_name = WPANTUND_IF_CMD_DISCOVER_SCAN_START;
} else {
method_name = WPANTUND_IF_CMD_NET_SCAN_START;
}
}
message = dbus_message_new_method_call(
interface_dbus_name,
path,
WPANTUND_DBUS_APIv1_INTERFACE,
method_name
);
dbus_message_append_args(
message,
DBUS_TYPE_UINT32, &channel_mask,
DBUS_TYPE_INVALID
);
if (sMleDiscoverScan) {
dbus_message_append_args(
message,
DBUS_TYPE_BOOLEAN, &joiner_flag,
DBUS_TYPE_BOOLEAN, &enable_filtering,
DBUS_TYPE_UINT16, &pan_id_filter,
DBUS_TYPE_INVALID
);
}
print_scan_header();
if (!sEnergyScan) {
gScannedNetworkCount = 0;
}
if(!dbus_connection_send_with_reply(
connection,
message,
&pending,
timeout
)) {
fprintf(stderr, "%s: error: IPC failure\n", argv[0]);
ret = ERRORCODE_UNKNOWN;
goto bail;
}
while ((dbus_connection_get_dispatch_status(connection) == DBUS_DISPATCH_DATA_REMAINS)
|| dbus_connection_has_messages_to_send(connection)
|| !dbus_pending_call_get_completed(pending)
) {
dbus_connection_read_write_dispatch(connection, 5000 /*ms*/);
}
reply = dbus_pending_call_steal_reply(pending);
require(reply != NULL, bail);
dbus_message_iter_init(reply, &iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) {
fprintf(stderr, "%s: error: Server returned a bad response ('%c')\n",
argv[0], dbus_message_iter_get_arg_type(&iter));
ret = ERRORCODE_UNKNOWN;
goto bail;
}
// Get return code
dbus_message_iter_get_basic(&iter, &ret);
if (ret) {
fprintf(stderr, "%s failed with error %d. %s\n", argv[0], ret, wpantund_status_to_cstr(ret));
print_error_diagnosis(ret);
goto bail;
}
}
bail:
if (reply) {
dbus_message_unref(reply);
}
if (pending != NULL) {
dbus_pending_call_unref(pending);
}
if (message) {
dbus_message_unref(message);
}
if (connection) {
dbus_bus_remove_match(connection, gDBusObjectManagerMatchString, NULL);
dbus_connection_remove_filter(connection,&dbus_signal_handler,NULL);
dbus_connection_unref(connection);
}
dbus_error_free(&error);
return ret;
}