X-Git-Url: http://git.liburcu.org/?a=blobdiff_plain;f=liblttng-ust-java-agent%2Fjava%2Flttng-ust-agent-common%2Forg%2Flttng%2Fust%2Fagent%2Fclient%2FLttngTcpSessiondClient.java;h=c63850050444ad42a47a8dccce94b3b30f2b638e;hb=dcd9a9d798f85ccee281d66f85ff4a9b5520a088;hp=d8788d320c416a9b9538bed603f7a9b45a8ca4cf;hpb=3165c2f51abe3093f4c5512b499e33cb380b387d;p=lttng-ust.git diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/LttngTcpSessiondClient.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/LttngTcpSessiondClient.java index d8788d32..c6385005 100644 --- a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/LttngTcpSessiondClient.java +++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/LttngTcpSessiondClient.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2015-2016 EfficiOS Inc., Alexandre Montplaisir * Copyright (C) 2013 - David Goulet * * This library is free software; you can redistribute it and/or modify it @@ -20,17 +21,21 @@ package org.lttng.ust.agent.client; import java.io.BufferedReader; import java.io.DataInputStream; import java.io.DataOutputStream; +import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.FileReader; import java.io.IOException; +import java.io.InputStreamReader; import java.lang.management.ManagementFactory; import java.net.Socket; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.nio.charset.Charset; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import org.lttng.ust.agent.utils.LttngUstAgentLogger; + /** * Client for agents to connect to a local session daemon, using a TCP socket. * @@ -41,9 +46,10 @@ public class LttngTcpSessiondClient implements Runnable { private static final String SESSION_HOST = "127.0.0.1"; private static final String ROOT_PORT_FILE = "/var/run/lttng/agent.port"; private static final String USER_PORT_FILE = "/.lttng/agent.port"; + private static final Charset PORT_FILE_ENCODING = Charset.forName("UTF-8"); - private static int protocolMajorVersion = 1; - private static int protocolMinorVersion = 0; + private static final int PROTOCOL_MAJOR_VERSION = 2; + private static final int PROTOCOL_MINOR_VERSION = 0; /** Command header from the session deamon. */ private final CountDownLatch registrationLatch = new CountDownLatch(1); @@ -108,12 +114,14 @@ public class LttngTcpSessiondClient implements Runnable { /* * Connect to the session daemon before anything else. */ + log("Connecting to sessiond"); connectToSessiond(); /* * Register to the session daemon as the Java component of the * UST application. */ + log("Registering to sessiond"); registerToSessiond(); /* @@ -121,14 +129,35 @@ public class LttngTcpSessiondClient implements Runnable { * session daemon. This will return if and only if there is a * fatal error or the socket closes. */ + log("Waiting on sessiond commands..."); handleSessiondCmd(); } catch (UnknownHostException uhe) { uhe.printStackTrace(); + /* + * Terminate agent thread. + */ + close(); } catch (IOException ioe) { + /* + * I/O exception may have been triggered by a session daemon + * closing the socket. Close our own socket and + * retry connecting after a delay. + */ try { + if (this.sessiondSock != null) { + this.sessiondSock.close(); + } Thread.sleep(3000); } catch (InterruptedException e) { - e.printStackTrace(); + /* + * Retry immediately if sleep is interrupted. + */ + } catch (IOException closeioe) { + closeioe.printStackTrace(); + /* + * Terminate agent thread. + */ + close(); } } } @@ -138,6 +167,7 @@ public class LttngTcpSessiondClient implements Runnable { * Dispose this client and close any socket connection it may hold. */ public void close() { + log("Closing client"); this.quit = true; try { @@ -150,29 +180,42 @@ public class LttngTcpSessiondClient implements Runnable { } private void connectToSessiond() throws IOException { - int port; + int rootPort = getPortFromFile(ROOT_PORT_FILE); + int userPort = getPortFromFile(getHomePath() + USER_PORT_FILE); + + /* + * Check for the edge case of both files existing but pointing to the + * same port. In this case, let the root client handle it. + */ + if ((rootPort != 0) && (rootPort == userPort) && (!isRoot)) { + log("User and root config files both point to port " + rootPort + + ". Letting the root client handle it."); + throw new IOException(); + } - if (this.isRoot) { - port = getPortFromFile(ROOT_PORT_FILE); - if (port == 0) { - /* No session daemon available. Stop and retry later. */ - throw new IOException(); - } - } else { - port = getPortFromFile(getHomePath() + USER_PORT_FILE); - if (port == 0) { - /* No session daemon available. Stop and retry later. */ - throw new IOException(); - } + int portToUse = (isRoot ? rootPort : userPort); + + if (portToUse == 0) { + /* No session daemon available. Stop and retry later. */ + throw new IOException(); } - this.sessiondSock = new Socket(SESSION_HOST, port); + this.sessiondSock = new Socket(SESSION_HOST, portToUse); this.inFromSessiond = new DataInputStream(sessiondSock.getInputStream()); this.outToSessiond = new DataOutputStream(sessiondSock.getOutputStream()); } private static String getHomePath() { - return System.getProperty("user.home"); + /* + * The environment variable LTTNG_HOME overrides HOME if + * defined. + */ + String homePath = System.getenv("LTTNG_HOME"); + + if (homePath == null) { + homePath = System.getProperty("user.home"); + } + return homePath; } /** @@ -181,27 +224,34 @@ public class LttngTcpSessiondClient implements Runnable { * @return port value if found else 0. */ private static int getPortFromFile(String path) throws IOException { - int port; BufferedReader br = null; try { - br = new BufferedReader(new FileReader(path)); + br = new BufferedReader(new InputStreamReader(new FileInputStream(path), PORT_FILE_ENCODING)); String line = br.readLine(); - port = Integer.parseInt(line, 10); + if (line == null) { + /* File exists but is empty. */ + return 0; + } + + int port = Integer.parseInt(line, 10); if (port < 0 || port > 65535) { /* Invalid value. Ignore. */ port = 0; } + return port; + + } catch (NumberFormatException e) { + /* File contained something that was not a number. */ + return 0; } catch (FileNotFoundException e) { /* No port available. */ - port = 0; + return 0; } finally { if (br != null) { br.close(); } } - - return port; } private void registerToSessiond() throws IOException { @@ -211,8 +261,8 @@ public class LttngTcpSessiondClient implements Runnable { buf.putInt(domainValue); buf.putInt(Integer.parseInt(pid)); - buf.putInt(protocolMajorVersion); - buf.putInt(protocolMinorVersion); + buf.putInt(PROTOCOL_MAJOR_VERSION); + buf.putInt(PROTOCOL_MINOR_VERSION); this.outToSessiond.write(data, 0, data.length); this.outToSessiond.flush(); } @@ -224,7 +274,7 @@ public class LttngTcpSessiondClient implements Runnable { /* Data read from the socket */ byte inputData[] = null; /* Reply data written to the socket, sent to the sessiond */ - byte responseData[] = null; + LttngAgentResponse response; while (true) { /* Get header from session daemon. */ @@ -246,50 +296,83 @@ public class LttngTcpSessiondClient implements Runnable { * We don't send any reply to the registration done command. * This just marks the end of the initial session setup. */ + log("Registration done"); continue; } case CMD_LIST: { - ISessiondCommand listLoggerCmd = new SessiondListLoggersCommand(); - LttngAgentResponse response = listLoggerCmd.execute(logAgent); - responseData = response.getBytes(); + SessiondCommand listLoggerCmd = new SessiondListLoggersCommand(); + response = listLoggerCmd.execute(logAgent); + log("Received list loggers command"); break; } - case CMD_ENABLE: + case CMD_EVENT_ENABLE: { if (inputData == null) { /* Invalid command */ - responseData = LttngAgentResponse.FAILURE_RESPONSE.getBytes(); + response = LttngAgentResponse.FAILURE_RESPONSE; break; } - ISessiondCommand enableCmd = new SessiondEnableEventCommand(inputData); - LttngAgentResponse response = enableCmd.execute(logAgent); - responseData = response.getBytes(); + SessiondCommand enableEventCmd = new SessiondEnableEventCommand(inputData); + response = enableEventCmd.execute(logAgent); + log("Received enable event command: " + enableEventCmd.toString()); break; } - case CMD_DISABLE: + case CMD_EVENT_DISABLE: { if (inputData == null) { /* Invalid command */ - responseData = LttngAgentResponse.FAILURE_RESPONSE.getBytes(); + response = LttngAgentResponse.FAILURE_RESPONSE; break; } - ISessiondCommand disableCmd = new SessiondDisableEventCommand(inputData); - LttngAgentResponse response = disableCmd.execute(logAgent); - responseData = response.getBytes(); + SessiondCommand disableEventCmd = new SessiondDisableEventCommand(inputData); + response = disableEventCmd.execute(logAgent); + log("Received disable event command: " + disableEventCmd.toString()); + break; + } + case CMD_APP_CTX_ENABLE: + { + if (inputData == null) { + /* This commands expects a payload, invalid command */ + response = LttngAgentResponse.FAILURE_RESPONSE; + break; + } + SessiondCommand enableAppCtxCmd = new SessiondEnableAppContextCommand(inputData); + response = enableAppCtxCmd.execute(logAgent); + log("Received enable app-context command"); + break; + } + case CMD_APP_CTX_DISABLE: + { + if (inputData == null) { + /* This commands expects a payload, invalid command */ + response = LttngAgentResponse.FAILURE_RESPONSE; + break; + } + SessiondCommand disableAppCtxCmd = new SessiondDisableAppContextCommand(inputData); + response = disableAppCtxCmd.execute(logAgent); + log("Received disable app-context command"); break; } default: { /* Unknown command, send empty reply */ - responseData = new byte[4]; - ByteBuffer buf = ByteBuffer.wrap(responseData); - buf.order(ByteOrder.BIG_ENDIAN); + response = null; + log("Received unknown command, ignoring"); break; } } /* Send response to the session daemon. */ + byte[] responseData; + if (response == null) { + responseData = new byte[4]; + ByteBuffer buf = ByteBuffer.wrap(responseData); + buf.order(ByteOrder.BIG_ENDIAN); + } else { + log("Sending response: " + response.toString()); + responseData = response.getBytes(); + } this.outToSessiond.write(responseData, 0, responseData.length); this.outToSessiond.flush(); } @@ -301,10 +384,17 @@ public class LttngTcpSessiondClient implements Runnable { */ private SessiondCommandHeader recvHeader() throws IOException { byte data[] = new byte[SessiondCommandHeader.HEADER_SIZE]; + int bytesLeft = data.length; + int bytesOffset = 0; - int readLen = this.inFromSessiond.read(data, 0, data.length); - if (readLen != data.length) { - throw new IOException(); + while (bytesLeft > 0) { + int bytesRead = this.inFromSessiond.read(data, bytesOffset, bytesLeft); + + if (bytesRead < 0) { + throw new IOException(); + } + bytesLeft -= bytesRead; + bytesOffset += bytesRead; } return new SessiondCommandHeader(data); } @@ -318,14 +408,32 @@ public class LttngTcpSessiondClient implements Runnable { */ private byte[] recvPayload(SessiondCommandHeader headerCmd) throws IOException { byte payload[] = new byte[(int) headerCmd.getDataSize()]; + int bytesLeft = payload.length; + int bytesOffset = 0; /* Failsafe check so we don't waste our time reading 0 bytes. */ - if (payload.length == 0) { + if (bytesLeft == 0) { return null; } - this.inFromSessiond.read(payload, 0, payload.length); + while (bytesLeft > 0) { + int bytesRead = inFromSessiond.read(payload, bytesOffset, bytesLeft); + + if (bytesRead < 0) { + throw new IOException(); + } + bytesLeft -= bytesRead; + bytesOffset += bytesRead; + } return payload; } + /** + * Wrapper for this class's logging, adds the connection's characteristics + * to help differentiate between multiple TCP clients. + */ + private void log(String message) { + LttngUstAgentLogger.log(getClass(), + "(root=" + isRoot + ", domain=" + domainValue + ") " + message); + } }