Mercurial > hg > thermostat
view agent/ipc/server/src/main/java/com/redhat/thermostat/agent/ipc/server/internal/IPCConfigurationWriter.java @ 2589:a6ba41a449c8
[PATCH] Windows Named Pipes - preliminary implementation
A preliminary implementation of windows named pipes.
Does not set windoes named pipes as default IPC; remains TCP
reviewed-by: aazores, ebaron
review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-February/022126.html
author | Simon Tooke <stooke@redhat.com> |
---|---|
date | Fri, 10 Feb 2017 15:52:51 -0500 |
parents | cd5b08c32052 |
children | 2885a4a290d0 |
line wrap: on
line source
/* * Copyright 2012-2017 Red Hat, Inc. * * This file is part of Thermostat. * * Thermostat is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2, or (at your * option) any later version. * * Thermostat is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Thermostat; see the file COPYING. If not see * <http://www.gnu.org/licenses/>. * * Linking this code with other modules is making a combined work * based on this code. Thus, the terms and conditions of the GNU * General Public License cover the whole combination. * * As a special exception, the copyright holders of this code give * you permission to link this code with independent modules to * produce an executable, regardless of the license terms of these * independent modules, and to copy and distribute the resulting * executable under terms of your choice, provided that you also * meet, for each linked independent module, the terms and conditions * of the license of that module. An independent module is a module * which is not derived from or based on this code. If you modify * this code, you may extend this exception to your version of the * library, but you are not obligated to do so. If you do not wish * to do so, delete this exception statement from your version. */ package com.redhat.thermostat.agent.ipc.server.internal; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.util.Properties; import java.util.UUID; import com.redhat.thermostat.agent.ipc.common.internal.IPCType; import com.redhat.thermostat.common.portability.PortableProcessImpl; import com.redhat.thermostat.shared.config.OS; class IPCConfigurationWriter { static final String PROP_IPC_TYPE = "type"; private static final String PROP_UNIX_SOCKET_DIR = "unixsocket.dir"; private static final String PROP_WINPIPE_ID = "winpipe.id"; private static final String PROP_TCP_SOCKET_SUFFIX= ".tcpsocket.port"; // suggest some default values for TCP sockets - test this range for unused sockets private static final int TEST_SOCKET_LOW = 51200; private static final int TEST_SOCKET_HIGH = 55000; private static final String COMMENTS = " Configuration for Inter-process Communication (IPC) used in the Thermostat agent.\n" + " The agent is configured to use Unix sockets for IPC by default on Linux,\n" + " and TCP sockets by default on Windows.\n" + " The options below can be set to modify the defaults used by the agent:\n\n" + " Transport type (one of unixsocket, tcpsocket, winpipes).\n" + " On Linux, unixsocket or tcpsocket are valid choices." + " On Windows, winpipes or tcpsocket are valid choices.\n" + PROP_IPC_TYPE + "=transporttype\n\n" + " Directory where Unix sockets are created, which may be deleted if it already exists.\n" + PROP_UNIX_SOCKET_DIR + "=/path/to/unix/sockets\n\n" + " Prefix for thermostat-related named pipes - should be different for every installation.\n" + PROP_WINPIPE_ID + "=XXXXXX\n\n" + " TCP socket port numbers for various services.\n" + "command-channel" + PROP_TCP_SOCKET_SUFFIX + "=NNNN\n" + "agent-proxy" + PROP_TCP_SOCKET_SUFFIX + "=MMMM\n\n"; private final File configFile; private final PropertiesHelper helper; IPCConfigurationWriter(File configFile) { this(configFile, new PropertiesHelper()); } IPCConfigurationWriter(File configFile, PropertiesHelper helper) { this.configFile = configFile; this.helper = helper; } void write() throws IOException { // Write defaults to config file if (!configFile.createNewFile()) { throw new IOException("IPC configuration file '" + configFile + "' already exists"); } Properties props = helper.createProperties(); props.setProperty(PROP_IPC_TYPE, OS.IS_UNIX ? IPCType.UNIX_SOCKET.getConfigValue() : IPCType.TCP_SOCKET.getConfigValue()); // unix socket will work without configuration (creates sockets in tmp directory // but tcpsocket always needs ports predefined (in the future, should support service discovery) // windows named pipes will use a random string for different Thermostat installations // Windows named pipes id // will be combined with other strings to make a full pipe name // for example, winpipe.id=abc345 may five a pipe name such as \\.\thermostat-abc345-command-channel { final String randString = makeThermostatId(); props.setProperty(PROP_WINPIPE_ID, randString); } // TCP properties (will be ignored if a different transpot is selected) // this implementation is flawed; // the unused ports might be in used by a process that simply wasn't running at thermostat setup time. { int cmdPort = findUnusedTCPSocket(TEST_SOCKET_LOW, TEST_SOCKET_HIGH); int aport = cmdPort == 0 ? 0 : findUnusedTCPSocket(cmdPort + 1, TEST_SOCKET_HIGH); props.setProperty("command-channel" + PROP_TCP_SOCKET_SUFFIX, Integer.toString(cmdPort)); props.setProperty("agent-proxy" + PROP_TCP_SOCKET_SUFFIX, Integer.toString(aport)); // write a property for each user on the system - currently only the current user // note: this is required for UNIX too if (OS.IS_WINDOWS) { int uport = aport == 0 ? 0 : findUnusedTCPSocket(aport + 1, TEST_SOCKET_HIGH); int uid = helper.getCurrentUid(); props.setProperty("agent-proxy-" + uid + PROP_TCP_SOCKET_SUFFIX, Integer.toString(uport)); } } try (FileOutputStream fos = helper.createStream(configFile)) { props.store(fos, COMMENTS); } } private static String makeThermostatId() { return UUID.randomUUID().toString(); } private static int findUnusedTCPSocket(int lowPort, int highPort) { for (int port=lowPort; port<=highPort; port++) { if (isTCPPortAvailable(port)) { return port; } } return 0; } private static boolean isTCPPortAvailable(int tcpport) { try { final ServerSocket socket = new ServerSocket(tcpport); socket.close(); return true; } catch (IOException e) { // socket already in use } return false; } // For testing purposes static class PropertiesHelper { FileOutputStream createStream(File configFile) throws IOException { return new FileOutputStream(configFile); } Properties createProperties() { return new Properties(); } int getCurrentUid() { return PortableProcessImpl.getInstance().getUid(0); // if pid=0, gets uid of current process } } }