From 49417da2aa0c0724d4bff0939f329e6f319de3bc Mon Sep 17 00:00:00 2001 From: pierantoniomerlino Date: Wed, 20 Nov 2024 12:29:01 +0100 Subject: [PATCH 01/13] Implemented systemd unit exists method Signed-off-by: pierantoniomerlino --- .../linux/net/dhcp/DhcpServerManager.java | 5 +- .../kura/linux/net/util/LinuxNetworkUtil.java | 56 ++++++++++++------- 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/dhcp/DhcpServerManager.java b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/dhcp/DhcpServerManager.java index a6ee0ad1d69..b91842986cf 100644 --- a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/dhcp/DhcpServerManager.java +++ b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/dhcp/DhcpServerManager.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2011, 2023 Eurotech and/or its affiliates and others + * Copyright (c) 2011, 2024 Eurotech and/or its affiliates and others * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -58,7 +58,8 @@ public DhcpServerManager(CommandExecutorService service) { public static DhcpServerTool getTool() { if (dhcpServerTool == DhcpServerTool.NONE) { - if (LinuxNetworkUtil.toolExists(DhcpServerTool.DNSMASQ.getValue())) { + if (LinuxNetworkUtil.toolExists(DhcpServerTool.DNSMASQ.getValue()) + && LinuxNetworkUtil.systemdSystemUnitExists(DhcpServerTool.DNSMASQ.getValue() + ".service")) { dhcpServerTool = DhcpServerTool.DNSMASQ; } else if (LinuxNetworkUtil.toolExists(DhcpServerTool.UDHCPD.getValue())) { dhcpServerTool = DhcpServerTool.UDHCPD; diff --git a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java index 1d964c0a210..d31f447d63f 100644 --- a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java +++ b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2011, 2022 Eurotech and/or its affiliates and others + * Copyright (c) 2011, 2024 Eurotech and/or its affiliates and others * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -44,12 +44,9 @@ public class LinuxNetworkUtil { - public static final String ACCESS_POINT_INTERFACE_SUFFIX = "_ap"; - - private static final String ETHTOOL_COMMAND = "ethtool"; - private static final Logger logger = LoggerFactory.getLogger(LinuxNetworkUtil.class); + public static final String ACCESS_POINT_INTERFACE_SUFFIX = "_ap"; private static Map ifconfigs = new HashMap<>(); private static final String[] IGNORE_IFACES = { "can", "sit", "mon.wlan" }; private static final ArrayList TOOLS = new ArrayList<>(); @@ -65,12 +62,15 @@ public class LinuxNetworkUtil { private static final String IFCONFIG = "ifconfig"; private static final String IWCONFIG = "iwconfig"; private static final String IP = "ip"; - private static final String LINE_MSG = "line: {}"; - private static final String ERR_EXECUTING_CMD_MSG = "error executing command --- {} --- exit value={}"; - private static final String FAKE_MAC_ADDRESS = "12:34:56:78:ab:cd"; + private static final String ETHTOOL_COMMAND = "ethtool"; + private static final String[] DEFAULT_SYSTEMD_SYSTEM_FOLDERS = new String[] { "/etc/systemd/system.control/", + "/run/systemd/system.control/", "/run/systemd/transient/", "/run/systemd/generator.early/", + "/etc/systemd/system/", "run/systemd/system/", "/run/systemd/generator/", "/usr/local/lib/systemd/system/", + "/usr/lib/systemd/system/", "/run/systemd/generator.late/" }; + private static final ArrayList SYSTEMD_SYSTEM_UNITS = new ArrayList<>(); private final CommandExecutorService executorService; private final WifiOptions wifiOptions; @@ -264,22 +264,40 @@ private boolean isWifiLinkUpInternal(String ifaceName) throws KuraException { } public static boolean toolExists(String tool) { - boolean ret = false; - final String[] searchFolders = new String[] { "/sbin/", "/usr/sbin/", "/bin/", "/usr/bin/" }; + return searchResourceInFolders(tool, TOOLS, new String[] { "/sbin/", "/usr/sbin/", "/bin/", "/usr/bin/" }); + } - if (TOOLS.contains(tool)) { - ret = true; + /** + * Checks if the given Systemd system unit is installed. + * This method search the unit in the default folders presented in + * https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html#Unit%20File%20Load%20Path + * If the unit is installed in a no-standard folder, this method will not + * be able to find it. + * + * It applies only to system units, not user ones. + * + * @param unitName + * the name of the Systemd system unit + * @return true if it is present + */ + public static boolean systemdSystemUnitExists(String unitName) { + return searchResourceInFolders(unitName, SYSTEMD_SYSTEM_UNITS, DEFAULT_SYSTEMD_SYSTEM_FOLDERS); + } + + private static boolean searchResourceInFolders(String resourceName, List resourceList, String[] folders) { + + if (resourceList.contains(resourceName)) { + return true; } else { - for (String folder : searchFolders) { - File fTool = new File(folder + tool); - if (fTool.exists()) { - TOOLS.add(tool); - ret = true; - break; + for (String folder : folders) { + File fUnit = new File(folder + resourceName); + if (fUnit.exists()) { + resourceList.add(resourceName); + return true; } } } - return ret; + return false; } /** From 6c51851b0f5d7d0ca3e3f181ff0333e81a0f3314 Mon Sep 17 00:00:00 2001 From: pierantoniomerlino Date: Wed, 20 Nov 2024 18:16:06 +0100 Subject: [PATCH 02/13] Added folder and tests Signed-off-by: pierantoniomerlino --- .../kura/linux/net/util/LinuxNetworkUtil.java | 8 +- .../linux/net/dhcp/DhcpServerManagerTest.java | 86 +++++++++++++++++++ 2 files changed, 90 insertions(+), 4 deletions(-) diff --git a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java index d31f447d63f..7297a2126d5 100644 --- a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java +++ b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java @@ -66,10 +66,10 @@ public class LinuxNetworkUtil { private static final String ERR_EXECUTING_CMD_MSG = "error executing command --- {} --- exit value={}"; private static final String FAKE_MAC_ADDRESS = "12:34:56:78:ab:cd"; private static final String ETHTOOL_COMMAND = "ethtool"; - private static final String[] DEFAULT_SYSTEMD_SYSTEM_FOLDERS = new String[] { "/etc/systemd/system.control/", - "/run/systemd/system.control/", "/run/systemd/transient/", "/run/systemd/generator.early/", - "/etc/systemd/system/", "run/systemd/system/", "/run/systemd/generator/", "/usr/local/lib/systemd/system/", - "/usr/lib/systemd/system/", "/run/systemd/generator.late/" }; + private static final String[] DEFAULT_SYSTEMD_SYSTEM_FOLDERS = new String[] { "/lib/systemd/system/", + "/etc/systemd/system.control/", "/run/systemd/system.control/", "/run/systemd/transient/", + "/run/systemd/generator.early/", "/etc/systemd/system/", "run/systemd/system/", "/run/systemd/generator/", + "/usr/local/lib/systemd/system/", "/usr/lib/systemd/system/", "/run/systemd/generator.late/" }; private static final ArrayList SYSTEMD_SYSTEM_UNITS = new ArrayList<>(); private final CommandExecutorService executorService; diff --git a/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/dhcp/DhcpServerManagerTest.java b/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/dhcp/DhcpServerManagerTest.java index 284219f6b19..e96ab891025 100644 --- a/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/dhcp/DhcpServerManagerTest.java +++ b/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/dhcp/DhcpServerManagerTest.java @@ -27,9 +27,12 @@ import org.eclipse.kura.linux.net.dhcp.server.DnsmasqLeaseReader; import org.eclipse.kura.linux.net.dhcp.server.UdhcpdConfigConverter; import org.eclipse.kura.linux.net.dhcp.server.UdhcpdLeaseReader; +import org.eclipse.kura.linux.net.util.LinuxNetworkUtil; +import org.junit.After; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.mockito.MockedStatic; import org.mockito.Mockito; public class DhcpServerManagerTest { @@ -50,6 +53,8 @@ public class DhcpServerManagerTest { private Optional returnedConfigConverter; private Optional returnedLeaseReader; private Exception occurredException; + private MockedStatic mockedLinuxNetworkUtil; + private DhcpServerTool tool; /* * Scenarios @@ -268,6 +273,63 @@ public void shouldReturnLeaseReaderForNone() throws NoSuchFieldException { thenReturnedLeaseReaderIsEmpty(); } + @Test + public void shouldGetDnsmasqToolIfBinaryAndUnitArePresent() throws NoSuchFieldException { + givenLinuxNetworkUtil("dnsmasq", Optional.of("dnsmasq.service")); + givenDhcpServerManager(DhcpServerTool.NONE); + + whenGetTool(); + + thenToolIs(DhcpServerTool.DNSMASQ); + } + + @Test + public void shouldNotGetDnsmasqToolIfOnlyBinaryIsPresent() throws NoSuchFieldException { + givenLinuxNetworkUtil("dnsmasq", Optional.empty()); + givenDhcpServerManager(DhcpServerTool.NONE); + + whenGetTool(); + + thenToolIs(DhcpServerTool.NONE); + } + + @Test + public void shouldGetDhcpdTool() throws NoSuchFieldException { + givenLinuxNetworkUtil("dhcpd", Optional.empty()); + givenDhcpServerManager(DhcpServerTool.NONE); + + whenGetTool(); + + thenToolIs(DhcpServerTool.DHCPD); + } + + @Test + public void shouldGetUdhcpdTool() throws NoSuchFieldException { + givenLinuxNetworkUtil("udhcpd", Optional.empty()); + givenDhcpServerManager(DhcpServerTool.NONE); + + whenGetTool(); + + thenToolIs(DhcpServerTool.UDHCPD); + } + + @Test + public void shouldNotGetAnyTool() throws NoSuchFieldException { + givenLinuxNetworkUtil("", Optional.empty()); + givenDhcpServerManager(DhcpServerTool.NONE); + + whenGetTool(); + + thenToolIs(DhcpServerTool.NONE); + } + + @After + public void deregisterStaticMocks() { + if (this.mockedLinuxNetworkUtil != null) { + mockedLinuxNetworkUtil.close(); + } + } + /* * Steps */ @@ -284,6 +346,15 @@ private void givenDhcpServerManager(DhcpServerTool dhcpServerTool) throws NoSuch this.dhcpServerManager = new DhcpServerManager(executorMock); } + private void givenLinuxNetworkUtil(String toolName, Optional unitName) { + this.mockedLinuxNetworkUtil = Mockito.mockStatic(LinuxNetworkUtil.class); + mockedLinuxNetworkUtil.when(() -> LinuxNetworkUtil.toolExists(toolName)).thenReturn(true); + if (unitName.isPresent()) { + mockedLinuxNetworkUtil.when(() -> LinuxNetworkUtil.systemdSystemUnitExists(unitName.get())) + .thenReturn(true); + } + } + /* * When */ @@ -328,6 +399,14 @@ private void whenGetLeaseReader() { } } + private void whenGetTool() { + try { + this.tool = DhcpServerManager.getTool(); + } catch (Exception e) { + this.occurredException = e; + } + } + /* * Then */ @@ -364,4 +443,11 @@ private void thenReturnedLeaseReaderIs(Class dhcpServerLeaseReader) { assertEquals(dhcpServerLeaseReader, this.returnedLeaseReader.get().getClass()); } + private void thenToolIs(DhcpServerTool expectedTool) { + assertEquals(expectedTool, this.tool); + } + + private void thenConverterIsNotPresent() { + assertFalse(this.returnedConfigConverter.isPresent()); + } } From 7829dc03ca146dca1f1c7df9dcc6859ad4074281 Mon Sep 17 00:00:00 2001 From: pierantoniomerlino Date: Thu, 21 Nov 2024 11:23:36 +0100 Subject: [PATCH 03/13] Replaced method for checking unit presence; test updated Signed-off-by: pierantoniomerlino --- .../kura/linux/net/util/LinuxNetworkUtil.java | 86 ++++++++++----- .../linux/net/dhcp/DhcpServerManagerTest.java | 2 +- .../linux/net/util/LinuxNetworkUtilTest.java | 104 +++++++++++++++++- 3 files changed, 158 insertions(+), 34 deletions(-) diff --git a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java index 7297a2126d5..5de2a198a48 100644 --- a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java +++ b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java @@ -17,6 +17,8 @@ import java.io.File; import java.io.FileReader; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collection; @@ -26,6 +28,13 @@ import java.util.Optional; import java.util.Set; import java.util.StringTokenizer; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; import org.apache.commons.io.Charsets; import org.eclipse.kura.KuraErrorCode; @@ -66,12 +75,8 @@ public class LinuxNetworkUtil { private static final String ERR_EXECUTING_CMD_MSG = "error executing command --- {} --- exit value={}"; private static final String FAKE_MAC_ADDRESS = "12:34:56:78:ab:cd"; private static final String ETHTOOL_COMMAND = "ethtool"; - private static final String[] DEFAULT_SYSTEMD_SYSTEM_FOLDERS = new String[] { "/lib/systemd/system/", - "/etc/systemd/system.control/", "/run/systemd/system.control/", "/run/systemd/transient/", - "/run/systemd/generator.early/", "/etc/systemd/system/", "run/systemd/system/", "/run/systemd/generator/", - "/usr/local/lib/systemd/system/", "/usr/lib/systemd/system/", "/run/systemd/generator.late/" }; - private static final ArrayList SYSTEMD_SYSTEM_UNITS = new ArrayList<>(); - + private static final String[] DEFAULT_PATH = new String[] { "/sbin/", "/usr/sbin/", "/bin/", "/usr/bin/" }; + private static final ProcessBuilder PROCESS_BUILDER = new ProcessBuilder(); private final CommandExecutorService executorService; private final WifiOptions wifiOptions; @@ -264,40 +269,45 @@ private boolean isWifiLinkUpInternal(String ifaceName) throws KuraException { } public static boolean toolExists(String tool) { - return searchResourceInFolders(tool, TOOLS, new String[] { "/sbin/", "/usr/sbin/", "/bin/", "/usr/bin/" }); + if (TOOLS.contains(tool)) { + return true; + } else { + for (String folder : DEFAULT_PATH) { + File fUnit = new File(folder + tool); + if (fUnit.exists()) { + TOOLS.add(tool); + return true; + } + } + } + return false; } /** * Checks if the given Systemd system unit is installed. - * This method search the unit in the default folders presented in - * https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html#Unit%20File%20Load%20Path - * If the unit is installed in a no-standard folder, this method will not - * be able to find it. - * - * It applies only to system units, not user ones. + * The result is based on the exit code of "systemctl status " + * as presented in https://www.man7.org/linux/man-pages/man1/systemctl.1.html#EXIT_STATUS * * @param unitName * the name of the Systemd system unit - * @return true if it is present + * @return true if the unit is installed */ public static boolean systemdSystemUnitExists(String unitName) { - return searchResourceInFolders(unitName, SYSTEMD_SYSTEM_UNITS, DEFAULT_SYSTEMD_SYSTEM_FOLDERS); - } - - private static boolean searchResourceInFolders(String resourceName, List resourceList, String[] folders) { - - if (resourceList.contains(resourceName)) { - return true; - } else { - for (String folder : folders) { - File fUnit = new File(folder + resourceName); - if (fUnit.exists()) { - resourceList.add(resourceName); - return true; - } - } + PROCESS_BUILDER.command("sh", "-c", "systemctl status " + unitName); + Process process; + try { + process = PROCESS_BUILDER.start(); + StreamGobbler streamGobbler = new StreamGobbler(process.getInputStream(), logger::debug); + ExecutorService executor = Executors.newSingleThreadExecutor(); + Future future = executor.submit(streamGobbler); + + int exitCode = process.waitFor(); + future.get(10, TimeUnit.SECONDS); + return exitCode < 4; + } catch (IOException | ExecutionException | TimeoutException | InterruptedException e) { + logger.error(String.format("Cannot check %s unit existence", unitName), e); + return false; } - return false; } /** @@ -1196,4 +1206,20 @@ private CommandStatus executeCommand(String[] commandString) { return this.executorService.execute(command); } + private static class StreamGobbler implements Runnable { + + private InputStream inputStream; + private Consumer consumer; + + public StreamGobbler(InputStream inputStream, Consumer consumer) { + this.inputStream = inputStream; + this.consumer = consumer; + } + + @Override + public void run() { + new BufferedReader(new InputStreamReader(inputStream)).lines().forEach(consumer); + } + } + } diff --git a/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/dhcp/DhcpServerManagerTest.java b/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/dhcp/DhcpServerManagerTest.java index e96ab891025..053eb4fdc48 100644 --- a/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/dhcp/DhcpServerManagerTest.java +++ b/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/dhcp/DhcpServerManagerTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017, 2023 Eurotech and/or its affiliates and others + * Copyright (c) 2017, 2024 Eurotech and/or its affiliates and others * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 diff --git a/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtilTest.java b/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtilTest.java index 98f2b86db94..17b6f27b12a 100644 --- a/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtilTest.java +++ b/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtilTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022 Eurotech and/or its affiliates and others + * Copyright (c) 2022, 2024 Eurotech and/or its affiliates and others * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -13,7 +13,20 @@ package org.eclipse.kura.linux.net.util; import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import org.eclipse.kura.KuraException; import org.eclipse.kura.core.linux.executor.LinuxExitStatus; @@ -29,6 +42,8 @@ public class LinuxNetworkUtilTest { private CommandExecutorServiceStub commandExecutorServiceStub; private String macAddress; private String linkStatus; + private boolean toolExists; + private boolean systemdUnitExists; @Test public void createApNetworkInterface() { @@ -70,11 +85,56 @@ public void setNetworkInterfaceLinkDown() { thenNetworkInterfaceLinkIsDown(); } + @Test + public void shouldCheckToolExistence() throws NoSuchFieldException, IllegalAccessException, IOException { + givenLinuxNetworkUtil(); + givenToolPaths("/tmp/"); + givenTool("/tmp/dhcpd"); + + whenCheckTool("dhcpd"); + + thenToolExists(); + } + + @Test + public void shouldCheckSystemdUnitExistence() + throws NoSuchFieldException, IllegalAccessException, IOException, InterruptedException { + givenLinuxNetworkUtil(); + givenProcessBuilder(0); + + whenCheckSystemdUnit("dnsmask.service"); + + thenSystemdUnitExists(); + } + + @Test + public void shouldNotCheckSystemdUnitExistence() + throws NoSuchFieldException, IllegalAccessException, IOException, InterruptedException { + givenLinuxNetworkUtil(); + givenProcessBuilder(4); + + whenCheckSystemdUnit("dnsmask.service"); + + thenSystemdUnitNotExist(); + } + private void givenLinuxNetworkUtil() { CommandStatus status = new CommandStatus(new Command(new String[] {}), new LinuxExitStatus(0)); this.commandExecutorServiceStub = new CommandExecutorServiceStub(status); this.linuxNetworkUtil = new LinuxNetworkUtil(this.commandExecutorServiceStub); + } + private void givenToolPaths(String path) throws NoSuchFieldException, IllegalAccessException { + setFinalStaticField(LinuxNetworkUtil.class, "DEFAULT_PATH", new String[] { path }); + } + + private void givenTool(String tool) throws IOException { + Path newFilePath = Paths.get(tool); + try { + Files.createFile(newFilePath); + } catch (FileAlreadyExistsException e) { + // do nothing + } } private void givenInterfaceName(String interfaceName) { @@ -83,16 +143,32 @@ private void givenInterfaceName(String interfaceName) { private void givenMacAddress(String macAddress) { this.macAddress = macAddress; + } + + private void givenLinkStatus(String linkStatus) { + this.linkStatus = linkStatus; + } + private void givenProcessBuilder(int returnCode) + throws NoSuchFieldException, IOException, InterruptedException, IllegalAccessException { + Process mockedProcess = mock(Process.class); + when(mockedProcess.waitFor()).thenReturn(returnCode); + when(mockedProcess.getInputStream()).thenReturn(new ByteArrayInputStream(new byte[] {})); + ProcessBuilder mockedProcessBuilder = mock(ProcessBuilder.class); + when(mockedProcessBuilder.start()).thenReturn(mockedProcess); + setFinalStaticField(LinuxNetworkUtil.class, "PROCESS_BUILDER", mockedProcessBuilder); } private void whenDedicatedInterfaceName(String dedicatedInterfaceName) { this.dedicatedInterfaceName = dedicatedInterfaceName; + } + private void whenCheckTool(String toolName) { + this.toolExists = LinuxNetworkUtil.toolExists(toolName); } - private void givenLinkStatus(String linkStatus) { - this.linkStatus = linkStatus; + private void whenCheckSystemdUnit(String unitName) { + this.systemdUnitExists = LinuxNetworkUtil.systemdSystemUnitExists(unitName); } private void thenApNetworkInterfaceIsCreated() { @@ -136,4 +212,26 @@ private void thenNetworkInterfaceLinkIsDown() { } } + private void thenToolExists() { + assertTrue(this.toolExists); + } + + private void thenSystemdUnitExists() { + assertTrue(this.systemdUnitExists); + } + + private void thenSystemdUnitNotExist() { + assertFalse(this.systemdUnitExists); + } + + static void setFinalStaticField(Class clazz, String fieldName, Object value) + throws NoSuchFieldException, IllegalAccessException { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + Field modifiers = field.getClass().getDeclaredField("modifiers"); + modifiers.setAccessible(true); + modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL); + field.set(null, value); + } + } From d1a7da1b278384fe4e971f27e8f600c7eb95cca0 Mon Sep 17 00:00:00 2001 From: pierantoniomerlino Date: Thu, 21 Nov 2024 11:36:22 +0100 Subject: [PATCH 04/13] Applied cleanup Signed-off-by: pierantoniomerlino --- .../linux/net/dhcp/DhcpServerManager.java | 6 ++--- .../kura/linux/net/util/LinuxNetworkUtil.java | 22 +++++++++---------- .../linux/net/dhcp/DhcpServerManagerTest.java | 14 ++++++------ .../linux/net/util/LinuxNetworkUtilTest.java | 6 ++--- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/dhcp/DhcpServerManager.java b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/dhcp/DhcpServerManager.java index b91842986cf..53b9ad3c345 100644 --- a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/dhcp/DhcpServerManager.java +++ b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/dhcp/DhcpServerManager.java @@ -1,12 +1,12 @@ /******************************************************************************* * Copyright (c) 2011, 2024 Eurotech and/or its affiliates and others - * + * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 * which is available at https://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 - * + * * Contributors: * Eurotech *******************************************************************************/ diff --git a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java index 5de2a198a48..ad262d63e2e 100644 --- a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java +++ b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java @@ -1,12 +1,12 @@ /******************************************************************************* * Copyright (c) 2011, 2024 Eurotech and/or its affiliates and others - * + * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 * which is available at https://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 - * + * * Contributors: * Eurotech *******************************************************************************/ @@ -287,7 +287,7 @@ public static boolean toolExists(String tool) { * Checks if the given Systemd system unit is installed. * The result is based on the exit code of "systemctl status " * as presented in https://www.man7.org/linux/man-pages/man1/systemctl.1.html#EXIT_STATUS - * + * * @param unitName * the name of the Systemd system unit * @return true if the unit is installed @@ -1152,7 +1152,7 @@ public void createApNetworkInterface(String ifaceName, String dedicatedApInterfa return; } - CommandStatus status = this.executeCommand(formIwDevIfaceInterfaceAddAp(ifaceName, dedicatedApInterface)); + CommandStatus status = executeCommand(formIwDevIfaceInterfaceAddAp(ifaceName, dedicatedApInterface)); if (!status.getExitStatus().isSuccessful()) { throw new KuraException(KuraErrorCode.OS_COMMAND_ERROR, @@ -1165,7 +1165,7 @@ public void setNetworkInterfaceMacAddress(String ifaceName) throws KuraException return; } - CommandStatus status = this.executeCommand(formIpLinkSetAddress(ifaceName, FAKE_MAC_ADDRESS)); + CommandStatus status = executeCommand(formIpLinkSetAddress(ifaceName, FAKE_MAC_ADDRESS)); if (!status.getExitStatus().isSuccessful()) { throw new KuraException(KuraErrorCode.OS_COMMAND_ERROR, @@ -1178,7 +1178,7 @@ public void setNetworkInterfaceLinkUp(String ifaceName) throws KuraException { return; } - CommandStatus status = this.executeCommand(formIpLinkSetStatus(ifaceName, "up")); + CommandStatus status = executeCommand(formIpLinkSetStatus(ifaceName, "up")); if (!status.getExitStatus().isSuccessful()) { throw new KuraException(KuraErrorCode.OS_COMMAND_ERROR, "Failed to set link up for interface " + ifaceName); @@ -1190,7 +1190,7 @@ public void setNetworkInterfaceLinkDown(String ifaceName) throws KuraException { return; } - CommandStatus status = this.executeCommand(formIpLinkSetStatus(ifaceName, "down")); + CommandStatus status = executeCommand(formIpLinkSetStatus(ifaceName, "down")); if (!status.getExitStatus().isSuccessful()) { throw new KuraException(KuraErrorCode.OS_COMMAND_ERROR, "Failed to set link up for interface " + ifaceName); @@ -1208,8 +1208,8 @@ private CommandStatus executeCommand(String[] commandString) { private static class StreamGobbler implements Runnable { - private InputStream inputStream; - private Consumer consumer; + private final InputStream inputStream; + private final Consumer consumer; public StreamGobbler(InputStream inputStream, Consumer consumer) { this.inputStream = inputStream; @@ -1218,7 +1218,7 @@ public StreamGobbler(InputStream inputStream, Consumer consumer) { @Override public void run() { - new BufferedReader(new InputStreamReader(inputStream)).lines().forEach(consumer); + new BufferedReader(new InputStreamReader(this.inputStream)).lines().forEach(this.consumer); } } diff --git a/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/dhcp/DhcpServerManagerTest.java b/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/dhcp/DhcpServerManagerTest.java index 053eb4fdc48..48d6df7ed9b 100644 --- a/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/dhcp/DhcpServerManagerTest.java +++ b/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/dhcp/DhcpServerManagerTest.java @@ -1,12 +1,12 @@ /******************************************************************************* * Copyright (c) 2017, 2024 Eurotech and/or its affiliates and others - * + * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 * which is available at https://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 - * + * * Contributors: * Eurotech ******************************************************************************/ @@ -326,7 +326,7 @@ public void shouldNotGetAnyTool() throws NoSuchFieldException { @After public void deregisterStaticMocks() { if (this.mockedLinuxNetworkUtil != null) { - mockedLinuxNetworkUtil.close(); + this.mockedLinuxNetworkUtil.close(); } } @@ -343,14 +343,14 @@ private void givenDhcpServerManager(DhcpServerTool dhcpServerTool) throws NoSuch TestUtil.setFieldValue(new DhcpServerManager(null), "dhcpServerTool", dhcpServerTool); - this.dhcpServerManager = new DhcpServerManager(executorMock); + this.dhcpServerManager = new DhcpServerManager(this.executorMock); } private void givenLinuxNetworkUtil(String toolName, Optional unitName) { this.mockedLinuxNetworkUtil = Mockito.mockStatic(LinuxNetworkUtil.class); - mockedLinuxNetworkUtil.when(() -> LinuxNetworkUtil.toolExists(toolName)).thenReturn(true); + this.mockedLinuxNetworkUtil.when(() -> LinuxNetworkUtil.toolExists(toolName)).thenReturn(true); if (unitName.isPresent()) { - mockedLinuxNetworkUtil.when(() -> LinuxNetworkUtil.systemdSystemUnitExists(unitName.get())) + this.mockedLinuxNetworkUtil.when(() -> LinuxNetworkUtil.systemdSystemUnitExists(unitName.get())) .thenReturn(true); } } diff --git a/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtilTest.java b/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtilTest.java index 17b6f27b12a..66a9c9f7de1 100644 --- a/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtilTest.java +++ b/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtilTest.java @@ -1,12 +1,12 @@ /******************************************************************************* * Copyright (c) 2022, 2024 Eurotech and/or its affiliates and others - * + * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 * which is available at https://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 - * + * * Contributors: * Eurotech *******************************************************************************/ From d36776d2115bb813ba510a937bc5f37d2aa2672e Mon Sep 17 00:00:00 2001 From: pierantoniomerlino Date: Thu, 21 Nov 2024 12:28:20 +0100 Subject: [PATCH 05/13] Reverted variable name Signed-off-by: pierantoniomerlino --- .../org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java index ad262d63e2e..75ebd99cd99 100644 --- a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java +++ b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java @@ -273,8 +273,8 @@ public static boolean toolExists(String tool) { return true; } else { for (String folder : DEFAULT_PATH) { - File fUnit = new File(folder + tool); - if (fUnit.exists()) { + File fTool = new File(folder + tool); + if (fTool.exists()) { TOOLS.add(tool); return true; } From ff50d42c3e0918f8e465d7264bedc9ce626a88ea Mon Sep 17 00:00:00 2001 From: pierantoniomerlino Date: Thu, 21 Nov 2024 14:24:11 +0100 Subject: [PATCH 06/13] Fixed sonar complains Signed-off-by: pierantoniomerlino --- .../eclipse/kura/linux/net/util/LinuxNetworkUtil.java | 10 +++++++--- .../kura/linux/net/dhcp/DhcpServerManagerTest.java | 4 ---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java index 75ebd99cd99..a55bc44d3b7 100644 --- a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java +++ b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java @@ -293,20 +293,24 @@ public static boolean toolExists(String tool) { * @return true if the unit is installed */ public static boolean systemdSystemUnitExists(String unitName) { - PROCESS_BUILDER.command("sh", "-c", "systemctl status " + unitName); + PROCESS_BUILDER.command("systemctl", "status", unitName); Process process; try { process = PROCESS_BUILDER.start(); - StreamGobbler streamGobbler = new StreamGobbler(process.getInputStream(), logger::debug); + StreamGobbler streamGobbler = new StreamGobbler(process.getInputStream(), logger::info); ExecutorService executor = Executors.newSingleThreadExecutor(); Future future = executor.submit(streamGobbler); int exitCode = process.waitFor(); future.get(10, TimeUnit.SECONDS); return exitCode < 4; - } catch (IOException | ExecutionException | TimeoutException | InterruptedException e) { + } catch (IOException | ExecutionException | TimeoutException e) { logger.error(String.format("Cannot check %s unit existence", unitName), e); return false; + } catch (InterruptedException e1) { + Thread.currentThread().interrupt(); + logger.error(String.format("Cannot check %s unit existence", unitName), e1); + return false; } } diff --git a/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/dhcp/DhcpServerManagerTest.java b/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/dhcp/DhcpServerManagerTest.java index 48d6df7ed9b..2ab9d3e69a5 100644 --- a/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/dhcp/DhcpServerManagerTest.java +++ b/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/dhcp/DhcpServerManagerTest.java @@ -446,8 +446,4 @@ private void thenReturnedLeaseReaderIs(Class dhcpServerLeaseReader) { private void thenToolIs(DhcpServerTool expectedTool) { assertEquals(expectedTool, this.tool); } - - private void thenConverterIsNotPresent() { - assertFalse(this.returnedConfigConverter.isPresent()); - } } From 64ce41001d2867a05f3475995b47f4117e1236e7 Mon Sep 17 00:00:00 2001 From: pierantoniomerlino Date: Thu, 21 Nov 2024 17:38:03 +0100 Subject: [PATCH 07/13] Improved tool search Signed-off-by: pierantoniomerlino --- .../kura/linux/net/util/LinuxNetworkUtil.java | 33 ++++++++++++---- .../linux/net/util/LinuxNetworkUtilTest.java | 38 +++++++++++++++++++ 2 files changed, 63 insertions(+), 8 deletions(-) diff --git a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java index a55bc44d3b7..9a2eb53f09b 100644 --- a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java +++ b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java @@ -269,18 +269,30 @@ private boolean isWifiLinkUpInternal(String ifaceName) throws KuraException { } public static boolean toolExists(String tool) { - if (TOOLS.contains(tool)) { - return true; + return getToolPath(tool).isPresent(); + } + + public static Optional getToolPath(String tool) { + Optional optionalToolPath = getCachedTool(tool); + if (optionalToolPath.isPresent()) { + return optionalToolPath; } else { for (String folder : DEFAULT_PATH) { - File fTool = new File(folder + tool); - if (fTool.exists()) { - TOOLS.add(tool); - return true; + String toolPath = folder + tool; + File toolFile = new File(toolPath); + if (toolFile.exists()) { + TOOLS.add(toolPath); + return Optional.of(toolPath); } } } - return false; + return Optional.empty(); + } + + private static Optional getCachedTool(String tool) { + return TOOLS.stream().filter(item -> { + return item.endsWith(tool); + }).findFirst(); } /** @@ -293,7 +305,12 @@ public static boolean toolExists(String tool) { * @return true if the unit is installed */ public static boolean systemdSystemUnitExists(String unitName) { - PROCESS_BUILDER.command("systemctl", "status", unitName); + Optional optionalSystemctlPath = getToolPath("systemctl"); + if (!optionalSystemctlPath.isPresent()) { + logger.debug("Systemctl command not found in default paths"); + return false; + } + PROCESS_BUILDER.command(optionalSystemctlPath.get(), "status", unitName); Process process; try { process = PROCESS_BUILDER.start(); diff --git a/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtilTest.java b/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtilTest.java index 66a9c9f7de1..cff0daa5f6a 100644 --- a/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtilTest.java +++ b/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtilTest.java @@ -13,6 +13,7 @@ package org.eclipse.kura.linux.net.util; import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -27,6 +28,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Optional; import org.eclipse.kura.KuraException; import org.eclipse.kura.core.linux.executor.LinuxExitStatus; @@ -44,6 +46,7 @@ public class LinuxNetworkUtilTest { private String linkStatus; private boolean toolExists; private boolean systemdUnitExists; + private Optional toolPath; @Test public void createApNetworkInterface() { @@ -100,6 +103,8 @@ public void shouldCheckToolExistence() throws NoSuchFieldException, IllegalAcces public void shouldCheckSystemdUnitExistence() throws NoSuchFieldException, IllegalAccessException, IOException, InterruptedException { givenLinuxNetworkUtil(); + givenToolPaths("/tmp/"); + givenTool("/tmp/systemctl"); givenProcessBuilder(0); whenCheckSystemdUnit("dnsmask.service"); @@ -111,6 +116,8 @@ public void shouldCheckSystemdUnitExistence() public void shouldNotCheckSystemdUnitExistence() throws NoSuchFieldException, IllegalAccessException, IOException, InterruptedException { givenLinuxNetworkUtil(); + givenToolPaths("/tmp/"); + givenTool("/tmp/systemctl"); givenProcessBuilder(4); whenCheckSystemdUnit("dnsmask.service"); @@ -118,6 +125,28 @@ public void shouldNotCheckSystemdUnitExistence() thenSystemdUnitNotExist(); } + @Test + public void shouldNotCheckSystemdUnitExistenceIfSystemctlNotExist() + throws NoSuchFieldException, IllegalAccessException, IOException, InterruptedException { + givenLinuxNetworkUtil(); + + whenCheckSystemdUnit("dnsmask.service"); + + thenSystemdUnitNotExist(); + } + + @Test + public void shouldGetToolPath() + throws NoSuchFieldException, IllegalAccessException, IOException, InterruptedException { + givenLinuxNetworkUtil(); + givenToolPaths("/tmp/"); + givenTool("/tmp/myAwesomeCommand"); + + whenGetTool("myAwesomeCommand"); + + thenToolIsRetrieved("/tmp/myAwesomeCommand"); + } + private void givenLinuxNetworkUtil() { CommandStatus status = new CommandStatus(new Command(new String[] {}), new LinuxExitStatus(0)); this.commandExecutorServiceStub = new CommandExecutorServiceStub(status); @@ -171,6 +200,10 @@ private void whenCheckSystemdUnit(String unitName) { this.systemdUnitExists = LinuxNetworkUtil.systemdSystemUnitExists(unitName); } + private void whenGetTool(String tool) { + this.toolPath = LinuxNetworkUtil.getToolPath(tool); + } + private void thenApNetworkInterfaceIsCreated() { try { this.linuxNetworkUtil.createApNetworkInterface(this.interfaceName, this.dedicatedInterfaceName); @@ -224,6 +257,11 @@ private void thenSystemdUnitNotExist() { assertFalse(this.systemdUnitExists); } + private void thenToolIsRetrieved(String expectedToolPath) { + assertTrue(this.toolPath.isPresent()); + assertEquals(expectedToolPath, this.toolPath.get()); + } + static void setFinalStaticField(Class clazz, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException { Field field = clazz.getDeclaredField(fieldName); From a0d0f30e7a32b1033d942cee6078c2244f447233 Mon Sep 17 00:00:00 2001 From: pierantoniomerlino Date: Thu, 21 Nov 2024 17:43:24 +0100 Subject: [PATCH 08/13] Changed logger level Signed-off-by: pierantoniomerlino --- .../java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java index 9a2eb53f09b..095f837bae9 100644 --- a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java +++ b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java @@ -314,7 +314,7 @@ public static boolean systemdSystemUnitExists(String unitName) { Process process; try { process = PROCESS_BUILDER.start(); - StreamGobbler streamGobbler = new StreamGobbler(process.getInputStream(), logger::info); + StreamGobbler streamGobbler = new StreamGobbler(process.getInputStream(), logger::debug); ExecutorService executor = Executors.newSingleThreadExecutor(); Future future = executor.submit(streamGobbler); From a89a97a1cbf358f4beed90d39fa0041bdd8961b5 Mon Sep 17 00:00:00 2001 From: pierantoniomerlino Date: Fri, 22 Nov 2024 12:44:19 +0100 Subject: [PATCH 09/13] Added log when dnsmasq start fails Signed-off-by: pierantoniomerlino --- .../eclipse/kura/linux/net/dhcp/server/DnsmasqTool.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/dhcp/server/DnsmasqTool.java b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/dhcp/server/DnsmasqTool.java index 9d6fa752a4f..49da7a1a9c6 100644 --- a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/dhcp/server/DnsmasqTool.java +++ b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/dhcp/server/DnsmasqTool.java @@ -42,10 +42,10 @@ public class DnsmasqTool implements DhcpLinuxTool { private String globalConfigFilename = "/etc/dnsmasq.d/dnsmasq-globals.conf"; private static final String GLOBAL_CONFIGURATION = "port=0\nbind-dynamic\n"; - static final Command IS_ACTIVE_COMMAND = new Command(new String[] { "systemctl", "is-active", "--quiet", - DhcpServerTool.DNSMASQ.getValue() }); - static final Command RESTART_COMMAND = new Command(new String[] { "systemctl", "restart", - DhcpServerTool.DNSMASQ.getValue() }); + static final Command IS_ACTIVE_COMMAND = new Command( + new String[] { "systemctl", "is-active", "--quiet", DhcpServerTool.DNSMASQ.getValue() }); + static final Command RESTART_COMMAND = new Command( + new String[] { "systemctl", "restart", DhcpServerTool.DNSMASQ.getValue() }); private CommandExecutorService executorService; private Map configsLastHash = Collections.synchronizedMap(new HashMap<>()); @@ -88,6 +88,7 @@ public CommandStatus startInterface(String interfaceName) throws KuraProcessExec CommandStatus restartStatus = this.executorService.execute(RESTART_COMMAND); if (!restartStatus.getExitStatus().isSuccessful()) { + logger.error("dnsmasq Systemd unit startup failed. Is dnsmasq package installed on the system?"); removeInterfaceConfig(interfaceName); restartStatus = this.executorService.execute(RESTART_COMMAND); } From 442c9271bf01606a410dc9fe9c0db580583004eb Mon Sep 17 00:00:00 2001 From: pierantoniomerlino Date: Fri, 22 Nov 2024 12:46:53 +0100 Subject: [PATCH 10/13] Fixed minor issues Signed-off-by: pierantoniomerlino --- .../org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java index 095f837bae9..fdff77f8f23 100644 --- a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java +++ b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java @@ -290,9 +290,7 @@ public static Optional getToolPath(String tool) { } private static Optional getCachedTool(String tool) { - return TOOLS.stream().filter(item -> { - return item.endsWith(tool); - }).findFirst(); + return TOOLS.stream().filter(item -> item.endsWith(tool)).findFirst(); } /** @@ -322,11 +320,11 @@ public static boolean systemdSystemUnitExists(String unitName) { future.get(10, TimeUnit.SECONDS); return exitCode < 4; } catch (IOException | ExecutionException | TimeoutException e) { - logger.error(String.format("Cannot check %s unit existence", unitName), e); + logger.error("Cannot check {} unit existence", unitName, e); return false; } catch (InterruptedException e1) { Thread.currentThread().interrupt(); - logger.error(String.format("Cannot check %s unit existence", unitName), e1); + logger.error("Cannot check {} unit existence", unitName, e1); return false; } } From 1fae8f527d10da3e7d33c50cda8947352da7e247 Mon Sep 17 00:00:00 2001 From: pierantoniomerlino Date: Fri, 22 Nov 2024 13:23:42 +0100 Subject: [PATCH 11/13] Simplified command process Signed-off-by: pierantoniomerlino --- .../kura/linux/net/util/LinuxNetworkUtil.java | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java index fdff77f8f23..801f5c27357 100644 --- a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java +++ b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java @@ -28,12 +28,6 @@ import java.util.Optional; import java.util.Set; import java.util.StringTokenizer; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import java.util.function.Consumer; import org.apache.commons.io.Charsets; @@ -312,14 +306,9 @@ public static boolean systemdSystemUnitExists(String unitName) { Process process; try { process = PROCESS_BUILDER.start(); - StreamGobbler streamGobbler = new StreamGobbler(process.getInputStream(), logger::debug); - ExecutorService executor = Executors.newSingleThreadExecutor(); - Future future = executor.submit(streamGobbler); - int exitCode = process.waitFor(); - future.get(10, TimeUnit.SECONDS); return exitCode < 4; - } catch (IOException | ExecutionException | TimeoutException e) { + } catch (IOException e) { logger.error("Cannot check {} unit existence", unitName, e); return false; } catch (InterruptedException e1) { From a2837bc72040ba37cadf7c44976088a9fb16582d Mon Sep 17 00:00:00 2001 From: pierantoniomerlino Date: Fri, 22 Nov 2024 16:35:38 +0100 Subject: [PATCH 12/13] Removed useless inner class Signed-off-by: pierantoniomerlino --- .../kura/linux/net/util/LinuxNetworkUtil.java | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java index 801f5c27357..cafdcbd7125 100644 --- a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java +++ b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/util/LinuxNetworkUtil.java @@ -17,8 +17,6 @@ import java.io.File; import java.io.FileReader; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collection; @@ -28,7 +26,6 @@ import java.util.Optional; import java.util.Set; import java.util.StringTokenizer; -import java.util.function.Consumer; import org.apache.commons.io.Charsets; import org.eclipse.kura.KuraErrorCode; @@ -1214,20 +1211,4 @@ private CommandStatus executeCommand(String[] commandString) { return this.executorService.execute(command); } - private static class StreamGobbler implements Runnable { - - private final InputStream inputStream; - private final Consumer consumer; - - public StreamGobbler(InputStream inputStream, Consumer consumer) { - this.inputStream = inputStream; - this.consumer = consumer; - } - - @Override - public void run() { - new BufferedReader(new InputStreamReader(this.inputStream)).lines().forEach(this.consumer); - } - } - } From 916fa25f45388755724966635cd23f31d73bca4e Mon Sep 17 00:00:00 2001 From: pierantoniomerlino Date: Fri, 22 Nov 2024 16:54:58 +0100 Subject: [PATCH 13/13] Improved logging Signed-off-by: pierantoniomerlino --- .../linux/net/dhcp/server/DnsmasqTool.java | 26 ++++++++++++------- .../linux/net/dhcp/server/DhcpdToolTest.java | 13 ++++++---- .../net/dhcp/server/DnsmasqToolTest.java | 16 +++++++----- 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/dhcp/server/DnsmasqTool.java b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/dhcp/server/DnsmasqTool.java index 49da7a1a9c6..6f62abdecba 100644 --- a/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/dhcp/server/DnsmasqTool.java +++ b/kura/org.eclipse.kura.linux.net/src/main/java/org/eclipse/kura/linux/net/dhcp/server/DnsmasqTool.java @@ -12,6 +12,7 @@ *******************************************************************************/ package org.eclipse.kura.linux.net.dhcp.server; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -42,10 +43,10 @@ public class DnsmasqTool implements DhcpLinuxTool { private String globalConfigFilename = "/etc/dnsmasq.d/dnsmasq-globals.conf"; private static final String GLOBAL_CONFIGURATION = "port=0\nbind-dynamic\n"; - static final Command IS_ACTIVE_COMMAND = new Command( - new String[] { "systemctl", "is-active", "--quiet", DhcpServerTool.DNSMASQ.getValue() }); - static final Command RESTART_COMMAND = new Command( - new String[] { "systemctl", "restart", DhcpServerTool.DNSMASQ.getValue() }); + static final String[] IS_ACTIVE_COMMANDLINE = new String[] { "systemctl", "is-active", "--quiet", + DhcpServerTool.DNSMASQ.getValue() }; + static final String[] RESTART_COMMANDLINE = new String[] { "systemctl", "restart", + DhcpServerTool.DNSMASQ.getValue() }; private CommandExecutorService executorService; private Map configsLastHash = Collections.synchronizedMap(new HashMap<>()); @@ -57,7 +58,7 @@ public DnsmasqTool(CommandExecutorService service) { @Override public boolean isRunning(String interfaceName) throws KuraProcessExecutionErrorException { - CommandStatus status = this.executorService.execute(IS_ACTIVE_COMMAND); + CommandStatus status = this.executorService.execute(new Command(IS_ACTIVE_COMMANDLINE)); boolean isRunning; try { @@ -85,12 +86,19 @@ public CommandStatus startInterface(String interfaceName) throws KuraProcessExec "Failed to start DHCP server for interface: " + interfaceName); } - CommandStatus restartStatus = this.executorService.execute(RESTART_COMMAND); + Command restartCommand = new Command(RESTART_COMMANDLINE); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayOutputStream err = new ByteArrayOutputStream(); + restartCommand.setErrorStream(err); + restartCommand.setOutputStream(out); + CommandStatus restartStatus = this.executorService.execute(restartCommand); if (!restartStatus.getExitStatus().isSuccessful()) { - logger.error("dnsmasq Systemd unit startup failed. Is dnsmasq package installed on the system?"); + logger.error("dnsmasq Systemd unit startup failed. Check if the tool is properly installed on the system."); + logger.error("dnsmasq stderr {}", new String(err.toByteArray(), StandardCharsets.UTF_8)); + logger.error("dnsmasq stdout {}", new String(out.toByteArray(), StandardCharsets.UTF_8)); removeInterfaceConfig(interfaceName); - restartStatus = this.executorService.execute(RESTART_COMMAND); + restartStatus = this.executorService.execute(restartCommand); } return restartStatus; @@ -101,7 +109,7 @@ public boolean disableInterface(String interfaceName) throws KuraProcessExecutio boolean isInterfaceDisabled = true; if (removeInterfaceConfig(interfaceName)) { - CommandStatus status = this.executorService.execute(RESTART_COMMAND); + CommandStatus status = this.executorService.execute(new Command(RESTART_COMMANDLINE)); isInterfaceDisabled = status.getExitStatus().isSuccessful(); } diff --git a/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/dhcp/server/DhcpdToolTest.java b/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/dhcp/server/DhcpdToolTest.java index 601714f3fe3..8e1d3795ed4 100644 --- a/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/dhcp/server/DhcpdToolTest.java +++ b/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/dhcp/server/DhcpdToolTest.java @@ -29,6 +29,7 @@ import java.util.Map; import org.eclipse.kura.KuraProcessExecutionErrorException; +import org.eclipse.kura.executor.Command; import org.eclipse.kura.executor.CommandExecutorService; import org.eclipse.kura.executor.CommandStatus; import org.eclipse.kura.executor.ExitStatus; @@ -160,7 +161,7 @@ public void shouldReturnFalseWhenDisablingInterfaceandNoRunningTool() throws Exc thenDisableInterfaceReturned(false); } - + /* * Steps */ @@ -183,9 +184,10 @@ public int getExitCode() { public boolean isSuccessful() { return isSuccessful; } - + }; - CommandStatus returnedStatus = new CommandStatus(DnsmasqTool.IS_ACTIVE_COMMAND, returnedExitStatus); + CommandStatus returnedStatus = new CommandStatus(new Command(DnsmasqTool.IS_ACTIVE_COMMANDLINE), + returnedExitStatus); when(this.mockExecutor.execute(any())).thenReturn(returnedStatus); when(this.mockExecutor.isRunning(any(String[].class))).thenReturn(isSuccessful); @@ -197,7 +199,8 @@ private void givenDhcpdTool(DhcpServerTool tool) { private void givenDhcpServerManagerReturn(String interfaceName, String returnedConfigFilename) { mockServerManager = mockStatic(DhcpServerManager.class); - mockServerManager.when(() -> DhcpServerManager.getPidFilename(interfaceName)).thenReturn(returnedConfigFilename); + mockServerManager.when(() -> DhcpServerManager.getPidFilename(interfaceName)) + .thenReturn(returnedConfigFilename); } private void givenConfigFile(String filename) throws IOException { @@ -212,7 +215,7 @@ private void givenRunningPids() { public int getPid() { return 1234; } - + }); when(this.mockExecutor.getPids(any())).thenReturn(this.runningPids); diff --git a/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/dhcp/server/DnsmasqToolTest.java b/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/dhcp/server/DnsmasqToolTest.java index 2e8be8ead44..077fb8e257c 100644 --- a/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/dhcp/server/DnsmasqToolTest.java +++ b/kura/test/org.eclipse.kura.linux.net.test/src/test/java/org/eclipse/kura/linux/net/dhcp/server/DnsmasqToolTest.java @@ -24,6 +24,7 @@ import java.io.IOException; import org.eclipse.kura.KuraProcessExecutionErrorException; +import org.eclipse.kura.executor.Command; import org.eclipse.kura.executor.CommandExecutorService; import org.eclipse.kura.executor.CommandStatus; import org.eclipse.kura.executor.ExitStatus; @@ -47,7 +48,7 @@ public class DnsmasqToolTest { private CommandStatus startInterfaceStatus; private boolean interfaceDisabled; private Exception occurredException; - + /* * Scenarios */ @@ -151,9 +152,10 @@ public int getExitCode() { public boolean isSuccessful() { return isSuccessful; } - + }; - CommandStatus returnedStatus = new CommandStatus(DnsmasqTool.IS_ACTIVE_COMMAND, returnedExitStatus); + CommandStatus returnedStatus = new CommandStatus(new Command(DnsmasqTool.IS_ACTIVE_COMMANDLINE), + returnedExitStatus); when(this.mockExecutor.execute(any())).thenReturn(returnedStatus); } @@ -166,13 +168,15 @@ private void givenDhcpServerManagerReturn(String interfaceName, String returnedC private void givenConfigFile(String filename) throws IOException { try { this.tmpFolder.newFolder("etc", "dnsmasq.d"); - } catch (Exception e) {} + } catch (Exception e) { + } this.tmpConfigFile = this.tmpFolder.newFile(filename); } private void givenDnsmasqTool() throws Exception { this.tool = new DnsmasqTool(this.mockExecutor); - this.tool.setDnsmasqGlobalConfigFile(this.tmpConfigFile.getAbsoluteFile().getParent() + "/dnsmasq-globals.conf"); + this.tool + .setDnsmasqGlobalConfigFile(this.tmpConfigFile.getAbsoluteFile().getParent() + "/dnsmasq-globals.conf"); } private void givenStartInterface(String interfaceName) throws KuraProcessExecutionErrorException { @@ -196,7 +200,7 @@ private void whenDisableInterface(String interfaceName) throws KuraProcessExecut this.interfaceDisabled = this.tool.disableInterface(interfaceName); } catch (Exception e) { this.occurredException = e; - } + } } /*