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
.jul
;
20 import java
.util
.concurrent
.Semaphore
;
21 import java
.nio
.ByteBuffer
;
22 import java
.nio
.ByteOrder
;
23 import java
.lang
.Integer
;
24 import java
.io
.IOException
;
25 import java
.io
.BufferedOutputStream
;
26 import java
.io
.BufferedReader
;
27 import java
.io
.ByteArrayOutputStream
;
28 import java
.io
.DataOutputStream
;
29 import java
.io
.DataInputStream
;
30 import java
.io
.FileReader
;
31 import java
.io
.FileNotFoundException
;
33 import java
.lang
.management
.ManagementFactory
;
34 import java
.util
.ArrayList
;
35 import java
.util
.HashMap
;
36 import java
.util
.HashSet
;
38 import java
.util
.Iterator
;
39 import java
.util
.List
;
40 import java
.util
.Enumeration
;
42 import java
.util
.Timer
;
43 import java
.util
.TimerTask
;
44 import java
.util
.logging
.Logger
;
45 import java
.util
.Collections
;
47 class USTRegisterMsg
{
48 public static int pid
;
51 public class LTTngTCPSessiondClient
{
52 /* Command header from the session deamon. */
53 private LTTngSessiondCmd2_4
.sessiond_hdr headerCmd
=
54 new LTTngSessiondCmd2_4
.sessiond_hdr();
56 private final String sessiondHost
;
57 private Socket sessiondSock
;
58 private boolean quit
= false;
60 private DataInputStream inFromSessiond
;
61 private DataOutputStream outToSessiond
;
63 private LTTngLogHandler handler
;
65 private Semaphore registerSem
;
67 private Timer eventTimer
;
70 * Indexed by event name but can contains duplicates since multiple
71 * sessions can enable the same event with or without different loglevels.
73 private Map
<String
, ArrayList
<LTTngEvent
>> eventMap
=
74 Collections
.synchronizedMap(
75 new HashMap
<String
, ArrayList
<LTTngEvent
>>());
77 private Set
<LTTngEvent
> wildCardSet
=
78 Collections
.synchronizedSet(new HashSet
<LTTngEvent
>());
80 /* Timer delay at each 5 seconds. */
81 private final static long timerDelay
= 5 * 1000;
82 private static boolean timerInitialized
;
84 private static final String rootPortFile
= "/var/run/lttng/jul.port";
85 private static final String userPortFile
= "/.lttng/jul.port";
87 /* Indicate if we've already release the semaphore. */
88 private boolean sem_posted
= false;
90 public LTTngTCPSessiondClient(String host
, Semaphore sem
) {
91 this.sessiondHost
= host
;
92 this.registerSem
= sem
;
93 this.eventTimer
= new Timer();
94 this.timerInitialized
= false;
97 private void setupEventTimer() {
98 if (this.timerInitialized
) {
102 this.eventTimer
.scheduleAtFixedRate(new TimerTask() {
105 LTTngSessiondCmd2_4
.sessiond_enable_handler enableCmd
= new
106 LTTngSessiondCmd2_4
.sessiond_enable_handler();
108 synchronized (eventMap
) {
110 Enumeration loggers
= handler
.logManager
.getLoggerNames();
113 * Create an event for each logger found and attach it to the
116 while (loggers
.hasMoreElements()) {
117 ArrayList
<LTTngEvent
> bucket
;
119 loggerName
= loggers
.nextElement().toString();
121 /* Logger is already enabled or end of list, skip it. */
122 if (handler
.exists(loggerName
) == true ||
123 loggerName
.equals("")) {
127 bucket
= eventMap
.get(loggerName
);
128 if (bucket
== null) {
129 /* No event(s) exist for this logger. */
133 for (LTTngEvent event
: bucket
) {
134 enableCmd
.name
= event
.name
;
135 enableCmd
.lttngLogLevel
= event
.logLevel
.level
;
136 enableCmd
.lttngLogLevelType
= event
.logLevel
.type
;
138 /* Event exist so pass null here. */
139 enableCmd
.execute(handler
, null, wildCardSet
);
144 /* Handle wild cards. */
145 synchronized (wildCardSet
) {
146 Map
<String
, ArrayList
<LTTngEvent
>> modifiedEvents
=
147 new HashMap
<String
, ArrayList
<LTTngEvent
>>();
148 Set
<LTTngEvent
> tmpSet
= new HashSet
<LTTngEvent
>();
149 Iterator
<LTTngEvent
> it
= wildCardSet
.iterator();
151 while (it
.hasNext()) {
152 LTTngEvent event
= it
.next();
154 /* Only support * for now. */
155 if (event
.name
.equals("*")) {
156 enableCmd
.name
= event
.name
;
157 enableCmd
.lttngLogLevel
= event
.logLevel
.level
;
158 enableCmd
.lttngLogLevelType
= event
.logLevel
.type
;
160 /* That might create a new event so pass the map. */
161 enableCmd
.execute(handler
, modifiedEvents
, tmpSet
);
164 eventMap
.putAll(modifiedEvents
);
167 }, this.timerDelay
, this.timerDelay
);
169 this.timerInitialized
= true;
173 * Try to release the registerSem if it's not already done.
175 private void tryReleaseSem()
177 /* Release semaphore so we unblock the agent. */
178 if (!this.sem_posted
) {
179 this.registerSem
.release();
180 this.sem_posted
= true;
185 * Cleanup Agent state.
187 private void cleanupState() {
190 if (this.handler
!= null) {
191 this.handler
.clear();
195 public void init(LTTngLogHandler handler
) throws InterruptedException
{
196 this.handler
= handler
;
203 /* Cleanup Agent state before trying to connect or reconnect. */
209 * Connect to the session daemon before anything else.
214 * Register to the session daemon as the Java component of the
217 registerToSessiond();
222 * Block on socket receive and wait for command from the
223 * session daemon. This will return if and only if there is a
224 * fatal error or the socket closes.
227 } catch (UnknownHostException uhe
) {
229 System
.out
.println(uhe
);
230 } catch (IOException ioe
) {
233 } catch (Exception e
) {
240 public void destroy() {
242 this.eventTimer
.cancel();
245 if (this.sessiondSock
!= null) {
246 this.sessiondSock
.close();
248 } catch (Exception e
) {
254 * Receive header data from the session daemon using the LTTng command
255 * static buffer of the right size.
257 private void recvHeader() throws Exception
{
259 byte data
[] = new byte[this.headerCmd
.SIZE
];
261 read_len
= this.inFromSessiond
.read(data
, 0, data
.length
);
262 if (read_len
!= data
.length
) {
263 throw new IOException();
265 this.headerCmd
.populate(data
);
269 * Receive payload from the session daemon. This MUST be done after a
270 * recvHeader() so the header value of a command are known.
272 * The caller SHOULD use isPayload() before which returns true if a payload
273 * is expected after the header.
275 private byte[] recvPayload() throws Exception
{
276 byte payload
[] = new byte[(int) this.headerCmd
.data_size
];
278 /* Failsafe check so we don't waste our time reading 0 bytes. */
279 if (payload
.length
== 0) {
283 this.inFromSessiond
.read(payload
, 0, payload
.length
);
288 * Handle session command from the session daemon.
290 private void handleSessiondCmd() throws Exception
{
295 /* Get header from session daemon. */
298 if (headerCmd
.data_size
> 0) {
299 data
= recvPayload();
302 switch (headerCmd
.cmd
) {
306 * Release semaphore so meaning registration is done and we
307 * can proceed to continue tracing.
311 * We don't send any reply to the registration done command.
312 * This just marks the end of the initial session setup.
318 LTTngSessiondCmd2_4
.sessiond_list_logger listLoggerCmd
=
319 new LTTngSessiondCmd2_4
.sessiond_list_logger();
320 listLoggerCmd
.execute(this.handler
);
321 data
= listLoggerCmd
.getBytes();
327 LTTngSessiondCmd2_4
.sessiond_enable_handler enableCmd
=
328 new LTTngSessiondCmd2_4
.sessiond_enable_handler();
330 enableCmd
.code
= LTTngSessiondCmd2_4
.lttng_jul_ret_code
.CODE_INVALID_CMD
;
333 enableCmd
.populate(data
);
334 enableCmd
.execute(this.handler
, this.eventMap
, this.wildCardSet
);
335 data
= enableCmd
.getBytes();
340 LTTngSessiondCmd2_4
.sessiond_disable_handler disableCmd
=
341 new LTTngSessiondCmd2_4
.sessiond_disable_handler();
343 disableCmd
.code
= LTTngSessiondCmd2_4
.lttng_jul_ret_code
.CODE_INVALID_CMD
;
346 disableCmd
.populate(data
);
347 disableCmd
.execute(this.handler
, this.eventMap
, this.wildCardSet
);
348 data
= disableCmd
.getBytes();
354 ByteBuffer buf
= ByteBuffer
.wrap(data
);
355 buf
.order(ByteOrder
.BIG_ENDIAN
);
356 LTTngSessiondCmd2_4
.lttng_jul_ret_code code
=
357 LTTngSessiondCmd2_4
.lttng_jul_ret_code
.CODE_INVALID_CMD
;
358 buf
.putInt(code
.getCode());
363 /* Send payload to session daemon. */
364 this.outToSessiond
.write(data
, 0, data
.length
);
365 this.outToSessiond
.flush();
369 private String
getHomePath() {
370 return System
.getProperty("user.home");
374 * Read port number from file created by the session daemon.
376 * @return port value if found else 0.
378 private int getPortFromFile(String path
) throws IOException
{
383 br
= new BufferedReader(new FileReader(path
));
384 String line
= br
.readLine();
385 port
= Integer
.parseInt(line
, 10);
386 if (port
< 0 || port
> 65535) {
387 /* Invalid value. Ignore. */
391 } catch (FileNotFoundException e
) {
392 /* No port available. */
399 private void connectToSessiond() throws Exception
{
402 if (this.handler
.is_root
== 1) {
403 port
= getPortFromFile(rootPortFile
);
405 /* No session daemon available. Stop and retry later. */
406 throw new IOException();
409 port
= getPortFromFile(getHomePath() + userPortFile
);
411 /* No session daemon available. Stop and retry later. */
412 throw new IOException();
416 this.sessiondSock
= new Socket(this.sessiondHost
, port
);
417 this.inFromSessiond
= new DataInputStream(
418 sessiondSock
.getInputStream());
419 this.outToSessiond
= new DataOutputStream(
420 sessiondSock
.getOutputStream());
423 private void registerToSessiond() throws Exception
{
424 byte data
[] = new byte[4];
425 ByteBuffer buf
= ByteBuffer
.wrap(data
);
426 String pid
= ManagementFactory
.getRuntimeMXBean().getName().split("@")[0];
428 buf
.putInt(Integer
.parseInt(pid
));
429 this.outToSessiond
.write(data
, 0, data
.length
);
430 this.outToSessiond
.flush();