/*******************************************************************************
* 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.dhcp;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.StringTokenizer;
import org.eclipse.kura.KuraErrorCode;
import org.eclipse.kura.KuraException;
import org.eclipse.kura.core.linux.util.LinuxProcessUtil;
import org.eclipse.kura.net.IP4Address;
import org.eclipse.kura.net.IPAddress;
import org.eclipse.kura.net.dhcp.DhcpServer;
import org.eclipse.kura.net.dhcp.DhcpServerConfig4;
import org.eclipse.kura.net.dhcp.DhcpServerConfigIP4;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DhcpServerImpl implements DhcpServer {
private static final Logger s_logger = LoggerFactory.getLogger(DhcpServerImpl.class);
// public static final String CONFIGURATION_NAME = "org.eclipse.kura.linux.net.dhcp";
// private static final String TMP_FILE_DIR = "/tmp/.kura/org.eclipse.kura.linux.net.dhcp/";
private static final String FILE_DIR = "/etc/";
private static final String PID_FILE_DIR = "/var/run/";
private final String m_interfaceName;
private DhcpServerConfig4 m_dhcpServerConfig4;
private final String m_configFileName;
private final String m_pidFileName;
private final String persistentConfigFileName;
private final String persistentPidFilename;
DhcpServerImpl(String interfaceName, boolean enabled, boolean passDns) throws KuraException {
this.m_interfaceName = interfaceName;
StringBuffer sb = new StringBuffer();
sb.append("dhcpd-").append(interfaceName).append(".conf");
this.m_configFileName = sb.toString();
sb = new StringBuffer();
sb.append("dhcpd-").append(interfaceName).append(".pid");
this.m_pidFileName = sb.toString();
this.persistentConfigFileName = FILE_DIR + this.m_configFileName;
this.persistentPidFilename = PID_FILE_DIR + this.m_pidFileName;
readConfig(enabled, passDns);
}
private void readConfig(boolean enabled, boolean passDns) throws KuraException {
// TODO
File configFile = new File(this.persistentConfigFileName);
if (configFile.exists()) {
s_logger.debug("initing DHCP Server configuration for {}", this.m_interfaceName);
// parse the file
/*
* # dhcpd.conf - DHCPD configuration file
*
* subnet 192.168.2.0 netmask 255.255.255.0 {
* interface eth1;
* default-lease-time 7200;
* max-lease-time 7200;
* option domain-name-servers 192.168.2.1;
* option routers 192.168.2.1;
* pool {
* range 192.168.2.100 192.168.2.110;
* }
* }
*/
try {
IP4Address subnet = null;
IP4Address netmask = null;
IP4Address router = null;
String interfaceName = null;
int defaultLeaseTime = -1;
int maxLeaseTime = -1;
IP4Address rangeStart = null;
IP4Address rangeEnd = null;
ArrayList<IP4Address> dnsList = new ArrayList<IP4Address>();
BufferedReader br = new BufferedReader(new FileReader(configFile));
String line = null;
while ((line = br.readLine()) != null) {
// TODO - really simple for now
StringTokenizer st = new StringTokenizer(line);
while (st.hasMoreTokens()) {
String token = st.nextToken();
if (token.equals("#")) {
break;
} else if (token.equals("subnet")) {
subnet = (IP4Address) IPAddress.parseHostAddress(st.nextToken());
if (!st.nextToken().equals("netmask")) {
br.close();
br = null;
throw new KuraException(KuraErrorCode.CONFIGURATION_ERROR,
"invalid dhcp config file: " + this.persistentConfigFileName);
}
netmask = (IP4Address) IPAddress.parseHostAddress(st.nextToken());
} else if (token.equals("interface")) {
interfaceName = st.nextToken();
interfaceName = interfaceName.substring(0, interfaceName.indexOf(';'));
} else if (token.equals("default-lease-time")) {
String leaseTime = st.nextToken();
defaultLeaseTime = Integer.parseInt(leaseTime.substring(0, leaseTime.indexOf(';')));
} else if (token.equals("max-lease-time")) {
String leaseTime = st.nextToken();
maxLeaseTime = Integer.parseInt(leaseTime.substring(0, leaseTime.indexOf(';')));
} else if (token.equals("range")) {
rangeStart = (IP4Address) IPAddress.parseHostAddress(st.nextToken());
String rangeEndString = st.nextToken();
rangeEndString = rangeEndString.substring(0, rangeEndString.indexOf(';'));
rangeEnd = (IP4Address) IPAddress.parseHostAddress(rangeEndString);
} else if (token.equals("option")) {
String option = st.nextToken();
if (option.equals("routers")) {
String routerString = st.nextToken();
routerString = routerString.substring(0, routerString.indexOf(';'));
router = (IP4Address) IPAddress.parseHostAddress(routerString);
} else if (option.equals("domain-name-servers")) {
String dnsString = st.nextToken();
dnsString = dnsString.substring(0, dnsString.indexOf(';'));
dnsList.add((IP4Address) IPAddress.parseHostAddress(dnsString));
}
}
}
}
boolean running = isRunning();
/*
* LinuxNamed linuxNamed = LinuxNamed.getInstance();
* DnsServerConfigIP4 dnsServerConfig = linuxNamed.getDnsServerConfig();
* List<IP4Address> forwarders = dnsServerConfig.getForwarders();
* List<NetworkPair<IP4Address>> allowedNetworks = dnsServerConfig.getAllowedNetworks();
* boolean passDns = (forwarders != null && forwarders.size() > 0
* && allowedNetworks != null && allowedNetworks.size() > 0);
*/
// FIXME - prefix still hardcoded
s_logger.debug("instantiating DHCP server configuration during init with " + "\n\t\tinterfaceName: "
+ interfaceName + "\n\t\trunning: " + running + "\n\t\tsubnet: " + subnet.getHostAddress()
+ "\n\t\trouter: " + router.getHostAddress() + "\n\t\tnetmask: " + netmask.getHostAddress()
+ "\n\t\tdefaultLeaseTime: " + defaultLeaseTime + "\n\t\tmaxLeaseTime: " + maxLeaseTime
+ "\n\t\trangeStart: " + rangeStart.getHostAddress() + "\n\t\trangeEnd: "
+ rangeEnd.getHostAddress() + "\n\t\tpassDns: " + passDns + "\n\t\tdnsList: "
+ dnsList.toString());
this.m_dhcpServerConfig4 = new DhcpServerConfigIP4(interfaceName, enabled, subnet, router, netmask,
defaultLeaseTime, maxLeaseTime, (short) 24, rangeStart, rangeEnd, passDns, dnsList);
br.close();
br = null;
} catch (FileNotFoundException e) {
throw new KuraException(KuraErrorCode.CONFIGURATION_ERROR, e);
} catch (IOException e) {
throw new KuraException(KuraErrorCode.CONFIGURATION_ERROR, e);
}
} else {
s_logger.debug("There is no current DHCP server configuration for {}", this.m_interfaceName);
}
}
@Override
public boolean isRunning() throws KuraException {
try {
// Check if dhcpd is running
int pid = LinuxProcessUtil.getPid(formDhcpdCommand());
return pid > -1;
} catch (Exception e) {
throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
}
}
public boolean enable() throws KuraException {
s_logger.debug("enable()");
// write to config file;
try {
writeConfig();
} catch (Exception e1) {
s_logger.error("Error writing configuration to filesystem");
e1.printStackTrace();
return false;
}
try {
// Check if dhcpd is running
if (isRunning()) {
// If so, disable it
s_logger.error("DHCP server is already running, bringing it down...");
disable();
}
// Start dhcpd
// FIXME:MC This leads to a process leak
if (LinuxProcessUtil.startBackground(formDhcpdCommand(), false) == 0) {
s_logger.debug("DHCP server started.");
s_logger.trace(this.m_dhcpServerConfig4.toString());
return true;
}
} catch (Exception e) {
throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
}
return false;
}
public boolean disable() throws KuraException {
s_logger.debug("disable()");
// write to config file;
try {
if (this.m_dhcpServerConfig4 != null) {
writeConfig();
}
} catch (Exception e1) {
s_logger.error("Error writing configuration to filesystem");
e1.printStackTrace();
return false;
}
try {
// Check if dhcpd is running
int pid = LinuxProcessUtil.getPid(formDhcpdCommand());
if (pid > -1) {
// If so, kill it.
if (LinuxProcessUtil.stop(pid)) {
removePidFile();
} else {
s_logger.debug("Failed to stop process...try to kill");
if (LinuxProcessUtil.kill(pid)) {
removePidFile();
} else {
throw new KuraException(KuraErrorCode.INTERNAL_ERROR, "error killing process, pid=" + pid);
}
}
} else {
s_logger.debug("tried to kill DHCP server for interface but it is not running");
}
} catch (Exception e) {
throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
}
return true;
}
public boolean isConfigured() {
return this.m_dhcpServerConfig4 != null;
}
public void setConfig(DhcpServerConfigIP4 dhcpServerConfig4) throws KuraException {
s_logger.debug("setConfig()");
try {
this.m_dhcpServerConfig4 = dhcpServerConfig4;
if (this.m_dhcpServerConfig4 == null) {
s_logger.warn("Set DHCP configuration to null");
}
if (dhcpServerConfig4.isEnabled()) {
enable();
} else {
writeConfig();
disable();
}
} catch (Exception e) {
s_logger.error("Error setting subnet config for " + this.m_interfaceName);
throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
}
}
public DhcpServerConfig4 getDhcpServerConfig(boolean enabled, boolean passDns) {
try {
readConfig(enabled, passDns);
} catch (Exception e) {
s_logger.error("Error reading config", e);
}
return this.m_dhcpServerConfig4;
}
public String getConfigFilename() {
return this.persistentConfigFileName;
}
private void writeConfig() throws KuraException {
try {
s_logger.trace(
"writing to " + FILE_DIR + this.m_configFileName + " with: " + this.m_dhcpServerConfig4.toString());
FileOutputStream fos = new FileOutputStream(FILE_DIR + this.m_configFileName);
PrintWriter pw = new PrintWriter(fos);
pw.write(this.m_dhcpServerConfig4.toString());
pw.flush();
fos.getFD().sync();
pw.close();
fos.close();
} catch (Exception e) {
e.printStackTrace();
throw new KuraException(KuraErrorCode.CONFIGURATION_ERROR,
"error while building up new configuration files for dhcp servers: " + e.getMessage());
}
}
private void removePidFile() {
File pidFile = new File(this.persistentPidFilename);
if (pidFile.exists()) {
pidFile.delete();
}
}
private String formDhcpdCommand() {
StringBuffer sb = new StringBuffer();
sb.append("dhcpd -cf ").append(this.persistentConfigFileName).append(" -pf ")
.append(this.persistentPidFilename);
return sb.toString();
}
}