/*******************************************************************************
* Copyright (c) 2011, 2016 Eurotech and/or its affiliates
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eurotech
*******************************************************************************/
package org.eclipse.kura.linux.net.util;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.eclipse.kura.KuraErrorCode;
import org.eclipse.kura.KuraException;
import org.eclipse.kura.core.linux.util.LinuxProcessUtil;
import org.eclipse.kura.core.util.ProcessUtil;
import org.eclipse.kura.core.util.SafeProcess;
import org.eclipse.kura.net.wifi.WifiAccessPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class iwScanTool extends ScanTool implements IScanTool {
private static final Logger s_logger = LoggerFactory.getLogger(iwScanTool.class);
private static final String SCAN_THREAD_NAME = "iwScanThread";
private static final Object s_lock = new Object();
private String m_ifaceName;
private ExecutorService m_executor;
private static Future<?> m_task;
private int m_timeout;
// FIXME:MC Is this process always closed?
private SafeProcess m_process;
private boolean m_status;
private String m_errmsg;
protected iwScanTool() {
this.m_timeout = 20;
}
protected iwScanTool(String ifaceName) {
this();
this.m_ifaceName = ifaceName;
this.m_errmsg = "";
this.m_status = false;
}
protected iwScanTool(String ifaceName, int tout) {
this(ifaceName);
this.m_timeout = tout;
}
@Override
public List<WifiAccessPoint> scan() throws KuraException {
List<WifiAccessPoint> wifiAccessPoints = new ArrayList<WifiAccessPoint>();
synchronized (s_lock) {
StringBuilder sb = new StringBuilder();
SafeProcess prIpLink = null;
SafeProcess prIpAddr = null;
try {
if (!LinuxNetworkUtil.hasAddress(this.m_ifaceName)) {
// activate the interface
sb.append("ip link set ").append(this.m_ifaceName).append(" up");
prIpLink = ProcessUtil.exec(sb.toString());
prIpLink.waitFor();
// remove the previous ip address (needed on mgw)
sb = new StringBuilder();
sb.append("ip addr flush dev ").append(this.m_ifaceName);
prIpAddr = ProcessUtil.exec(sb.toString());
prIpAddr.waitFor();
}
} catch (Exception e) {
throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
} finally {
if (prIpLink != null) {
ProcessUtil.destroy(prIpLink);
}
if (prIpAddr != null) {
ProcessUtil.destroy(prIpAddr);
}
}
long timerStart = System.currentTimeMillis();
this.m_executor = Executors.newSingleThreadExecutor();
m_task = this.m_executor.submit(new Runnable() {
@Override
public void run() {
Thread.currentThread().setName(SCAN_THREAD_NAME);
int stat = -1;
iwScanTool.this.m_process = null;
StringBuilder sb = new StringBuilder();
sb.append("iw dev ").append(iwScanTool.this.m_ifaceName).append(" scan");
s_logger.info("scan() :: executing: {}", sb.toString());
iwScanTool.this.m_status = false;
try {
iwScanTool.this.m_process = ProcessUtil.exec(sb.toString());
stat = iwScanTool.this.m_process.waitFor();
s_logger.info("scan() :: {} command returns status={}", sb.toString(), stat);
if (stat == 0) {
iwScanTool.this.m_status = true;
} else {
s_logger.error("scan() :: failed to execute {} error code is {}", sb.toString(), stat);
s_logger.error("scan() :: STDERR: " + LinuxProcessUtil
.getInputStreamAsString(iwScanTool.this.m_process.getErrorStream()));
}
} catch (Exception e) {
iwScanTool.this.m_errmsg = "exception executing scan command";
e.printStackTrace();
}
}
});
while (!m_task.isDone()) {
if (System.currentTimeMillis() > timerStart + this.m_timeout * 1000) {
s_logger.warn("scan() :: scan timeout");
sb = new StringBuilder();
sb.append("iw dev ").append(this.m_ifaceName).append(" scan");
try {
int pid = LinuxProcessUtil.getPid(sb.toString());
if (pid >= 0) {
s_logger.warn("scan() :: scan timeout :: killing pid {}", pid);
LinuxProcessUtil.kill(pid);
}
} catch (Exception e) {
e.printStackTrace();
}
m_task.cancel(true);
m_task = null;
this.m_errmsg = "timeout executing scan command";
break;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
if (this.m_status == false || this.m_process == null) {
throw new KuraException(KuraErrorCode.INTERNAL_ERROR, this.m_errmsg);
}
s_logger.info("scan() :: the 'iw scan' command executed successfully, parsing output ...");
try {
wifiAccessPoints = parse();
} catch (Exception e) {
throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e, "error parsing scan results");
} finally {
s_logger.info("scan() :: destroing scan proccess ...");
if (this.m_process != null) {
ProcessUtil.destroy(this.m_process);
}
this.m_process = null;
s_logger.info("scan() :: Terminating {} ...", SCAN_THREAD_NAME);
this.m_executor.shutdownNow();
try {
this.m_executor.awaitTermination(2, TimeUnit.SECONDS);
} catch (InterruptedException e) {
s_logger.warn("Interrupted", e);
}
s_logger.info("scan() :: 'iw scan' thread terminated? - {}", this.m_executor.isTerminated());
this.m_executor = null;
}
}
return wifiAccessPoints;
}
private List<WifiAccessPoint> parse() throws Exception {
List<IWAPParser> apInfos = new ArrayList<IWAPParser>();
IWAPParser currentAP = null;
BufferedReader br = new BufferedReader(new InputStreamReader(this.m_process.getInputStream()));
try {
String line = null;
while ((line = br.readLine()) != null) {
if (line.startsWith("scan aborted!")) {
br.close();
s_logger.warn("parse() :: scan operation was aborted");
throw new KuraException(KuraErrorCode.INTERNAL_ERROR, "iw scan operation was aborted");
}
if (line.startsWith("BSS")) {
// new AP - parse out the MAC
StringTokenizer st = new StringTokenizer(line, " ");
st.nextToken(); // eat BSS
String macAddressString = st.nextToken().substring(0, 16);
if (macAddressString != null) {
// Set this AP parser as the current one
currentAP = new IWAPParser(macAddressString);
}
// Add it to the list
apInfos.add(currentAP);
} else {
// Must be an AP property line
String propLine = line.trim();
if (currentAP != null) {
// We're currently parsing an AP
try {
// Give this line to the AP parser
currentAP.parsePropLine(propLine);
} catch (Exception e) {
currentAP = null;
s_logger.error("Failed to parse line: {}; giving up on the current AP", propLine, e);
}
}
}
}
} finally {
br.close();
}
// Generate list of WifiAccessPoint objects
List<WifiAccessPoint> wifiAccessPoints = new ArrayList<WifiAccessPoint>();
for (IWAPParser info : apInfos) {
wifiAccessPoints.add(info.toWifiAccessPoint());
}
return wifiAccessPoints;
}
}