2 * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
4 * This library is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License, version 2.1 only,
6 * as published by the Free Software Foundation.
8 * This library is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library; if not, write to the Free Software Foundation,
15 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 package org
.lttng
.ust
.agent
;
20 import java
.io
.BufferedReader
;
21 import java
.io
.DataInputStream
;
22 import java
.io
.DataOutputStream
;
23 import java
.io
.FileNotFoundException
;
24 import java
.io
.FileReader
;
25 import java
.io
.IOException
;
26 import java
.lang
.management
.ManagementFactory
;
27 import java
.net
.Socket
;
28 import java
.net
.UnknownHostException
;
29 import java
.nio
.ByteBuffer
;
30 import java
.nio
.ByteOrder
;
31 import java
.util
.concurrent
.Semaphore
;
33 class LTTngTCPSessiondClient
implements Runnable
{
35 private static final String SESSION_HOST
= "127.0.0.1";
36 private static final String ROOT_PORT_FILE
= "/var/run/lttng/agent.port";
37 private static final String USER_PORT_FILE
= "/.lttng/agent.port";
39 private static Integer protocolMajorVersion
= 1;
40 private static Integer protocolMinorVersion
= 0;
42 /* Command header from the session deamon. */
43 private LTTngSessiondCmd2_6
.sessiond_hdr headerCmd
=
44 new LTTngSessiondCmd2_6
.sessiond_hdr();
46 private Socket sessiondSock
;
47 private volatile boolean quit
= false;
49 private DataInputStream inFromSessiond
;
50 private DataOutputStream outToSessiond
;
52 private LogFramework log
;
54 private Semaphore registerSem
;
57 private LTTngAgent
.Domain agentDomain
;
59 /* Indicate if we've already released the semaphore. */
60 private boolean semPosted
= false;
62 public LTTngTCPSessiondClient(LTTngAgent
.Domain domain
, LogFramework log
, Semaphore sem
) {
63 this.agentDomain
= domain
;
65 this.registerSem
= sem
;
69 * Try to release the registerSem if it's not already done.
71 private void tryReleaseSem() {
72 /* Release semaphore so we unblock the agent. */
73 if (!this.semPosted
) {
74 this.registerSem
.release();
75 this.semPosted
= true;
86 /* Cleanup Agent state before trying to connect or reconnect. */
92 * Connect to the session daemon before anything else.
97 * Register to the session daemon as the Java component of the
100 registerToSessiond();
103 * Block on socket receive and wait for command from the
104 * session daemon. This will return if and only if there is a
105 * fatal error or the socket closes.
108 } catch (UnknownHostException uhe
) {
110 System
.out
.println(uhe
);
111 } catch (IOException ioe
) {
115 } catch (InterruptedException e
) {
118 } catch (Exception e
) {
125 public void destroy() {
129 if (this.sessiondSock
!= null) {
130 this.sessiondSock
.close();
132 } catch (Exception e
) {
138 * Receive header data from the session daemon using the LTTng command
139 * static buffer of the right size.
141 private void recvHeader() throws Exception
{
142 byte data
[] = new byte[LTTngSessiondCmd2_6
.sessiond_hdr
.SIZE
];
144 int readLen
= this.inFromSessiond
.read(data
, 0, data
.length
);
145 if (readLen
!= data
.length
) {
146 throw new IOException();
148 this.headerCmd
.populate(data
);
152 * Receive payload from the session daemon. This MUST be done after a
153 * recvHeader() so the header value of a command are known.
155 * The caller SHOULD use isPayload() before which returns true if a payload
156 * is expected after the header.
158 private byte[] recvPayload() throws Exception
{
159 byte payload
[] = new byte[(int) this.headerCmd
.dataSize
];
161 /* Failsafe check so we don't waste our time reading 0 bytes. */
162 if (payload
.length
== 0) {
166 this.inFromSessiond
.read(payload
, 0, payload
.length
);
171 * Handle session command from the session daemon.
173 private void handleSessiondCmd() throws Exception
{
177 /* Get header from session daemon. */
180 if (headerCmd
.dataSize
> 0) {
181 data
= recvPayload();
184 switch (headerCmd
.cmd
) {
188 * Release semaphore so meaning registration is done and we
189 * can proceed to continue tracing.
193 * We don't send any reply to the registration done command.
194 * This just marks the end of the initial session setup.
200 LTTngSessiondCmd2_6
.sessiond_list_logger listLoggerCmd
=
201 new LTTngSessiondCmd2_6
.sessiond_list_logger();
202 listLoggerCmd
.execute(this.log
);
203 data
= listLoggerCmd
.getBytes();
208 LTTngSessiondCmd2_6
.sessiond_enable_handler enableCmd
=
209 new LTTngSessiondCmd2_6
.sessiond_enable_handler();
211 enableCmd
.code
= LTTngSessiondCmd2_6
.lttng_agent_ret_code
.CODE_INVALID_CMD
;
214 enableCmd
.populate(data
);
215 enableCmd
.execute(this.log
);
216 data
= enableCmd
.getBytes();
221 LTTngSessiondCmd2_6
.sessiond_disable_handler disableCmd
=
222 new LTTngSessiondCmd2_6
.sessiond_disable_handler();
224 disableCmd
.code
= LTTngSessiondCmd2_6
.lttng_agent_ret_code
.CODE_INVALID_CMD
;
227 disableCmd
.populate(data
);
228 disableCmd
.execute(this.log
);
229 data
= disableCmd
.getBytes();
235 ByteBuffer buf
= ByteBuffer
.wrap(data
);
236 buf
.order(ByteOrder
.BIG_ENDIAN
);
241 /* Send payload to session daemon. */
242 this.outToSessiond
.write(data
, 0, data
.length
);
243 this.outToSessiond
.flush();
247 private static String
getHomePath() {
248 return System
.getProperty("user.home");
252 * Read port number from file created by the session daemon.
254 * @return port value if found else 0.
256 private static int getPortFromFile(String path
) throws IOException
{
261 br
= new BufferedReader(new FileReader(path
));
262 String line
= br
.readLine();
263 port
= Integer
.parseInt(line
, 10);
264 if (port
< 0 || port
> 65535) {
265 /* Invalid value. Ignore. */
269 } catch (FileNotFoundException e
) {
270 /* No port available. */
277 private void connectToSessiond() throws Exception
{
280 if (this.log
.isRoot()) {
281 port
= getPortFromFile(ROOT_PORT_FILE
);
283 /* No session daemon available. Stop and retry later. */
284 throw new IOException();
287 port
= getPortFromFile(getHomePath() + USER_PORT_FILE
);
289 /* No session daemon available. Stop and retry later. */
290 throw new IOException();
294 this.sessiondSock
= new Socket(SESSION_HOST
, port
);
295 this.inFromSessiond
= new DataInputStream(sessiondSock
.getInputStream());
296 this.outToSessiond
= new DataOutputStream(sessiondSock
.getOutputStream());
299 private void registerToSessiond() throws Exception
{
300 byte data
[] = new byte[16];
301 ByteBuffer buf
= ByteBuffer
.wrap(data
);
302 String pid
= ManagementFactory
.getRuntimeMXBean().getName().split("@")[0];
304 buf
.putInt(this.agentDomain
.value());
305 buf
.putInt(Integer
.parseInt(pid
));
306 buf
.putInt(protocolMajorVersion
);
307 buf
.putInt(protocolMinorVersion
);
308 this.outToSessiond
.write(data
, 0, data
.length
);
309 this.outToSessiond
.flush();