Fix: JUL agent connect to user and root sessiond
[lttng-ust.git] / liblttng-ust-jul / org / lttng / ust / jul / LTTngTCPSessiondClient.java
CommitLineData
43e5396b
DG
1/*
2 * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
3 *
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.
7 *
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
11 * for more details.
12 *
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
16 */
17
18package org.lttng.ust.jul;
19
20import java.util.concurrent.Semaphore;
21import java.nio.ByteBuffer;
22import java.nio.ByteOrder;
23import java.lang.Integer;
24import java.io.IOException;
25import java.io.BufferedOutputStream;
26import java.io.ByteArrayOutputStream;
27import java.io.DataOutputStream;
28import java.io.DataInputStream;
29import java.net.*;
30import java.lang.management.ManagementFactory;
31import java.util.ArrayList;
529e6def 32import java.util.HashMap;
e614d916
JG
33import java.util.HashSet;
34import java.util.Iterator;
43e5396b 35import java.util.List;
e614d916 36import java.util.Set;
43e5396b
DG
37import java.util.Timer;
38import java.util.TimerTask;
529e6def 39import java.util.logging.Logger;
e614d916 40import java.util.Collections;
43e5396b
DG
41
42class USTRegisterMsg {
43 public static int pid;
44}
45
46public class LTTngTCPSessiondClient {
47 /* Command header from the session deamon. */
48 private LTTngSessiondCmd2_4.sessiond_hdr headerCmd =
49 new LTTngSessiondCmd2_4.sessiond_hdr();
50
51 private final String sessiondHost;
52 private final int sessiondPort;
53 private Socket sessiondSock;
54 private boolean quit = false;
55
56 private DataInputStream inFromSessiond;
57 private DataOutputStream outToSessiond;
58
59 private LTTngLogHandler handler;
60
61 private Semaphore registerSem;
62
63 private Timer eventTimer;
e614d916
JG
64 private Set<LTTngEvent> enabledEventSet =
65 Collections.synchronizedSet(new HashSet<LTTngEvent>());
529e6def
DG
66 /*
67 * Map of Logger objects that have been enabled. They are indexed by name.
68 */
69 private HashMap<String, Logger> enabledLoggers = new HashMap<String, Logger>();
43e5396b
DG
70 /* Timer delay at each 5 seconds. */
71 private final static long timerDelay = 5 * 1000;
72 private static boolean timerInitialized;
73
74 public LTTngTCPSessiondClient(String host, int port, Semaphore sem) {
75 this.sessiondHost = host;
76 this.sessiondPort = port;
77 this.registerSem = sem;
78 this.eventTimer = new Timer();
79 this.timerInitialized = false;
80 }
81
82 private void setupEventTimer() {
83 if (this.timerInitialized) {
84 return;
85 }
86
87 this.eventTimer.scheduleAtFixedRate(new TimerTask() {
88 @Override
89 public void run() {
e614d916
JG
90 synchronized (enabledEventSet) {
91 LTTngSessiondCmd2_4.sessiond_enable_handler enableCmd = new
92 LTTngSessiondCmd2_4.sessiond_enable_handler();
529e6def 93 /*
e614d916
JG
94 * Modifying events in a Set will raise a
95 * ConcurrentModificationException. Thus, we remove an event
96 * and add its modified version to modifiedEvents when a
97 * modification is necessary.
529e6def 98 */
e614d916
JG
99 Set<LTTngEvent> modifiedEvents = new HashSet<LTTngEvent>();
100 Iterator<LTTngEvent> it = enabledEventSet.iterator();
5b5ffa03 101
e614d916
JG
102 while (it.hasNext()) {
103 int ret;
104 Logger logger;
105 LTTngEvent event = it.next();
5b5ffa03 106
5b5ffa03 107 /*
e614d916
JG
108 * Check if this Logger name has been enabled already. Note
109 * that in the case of "*", it's never added in that hash
110 * table thus the enable command does a lookup for each
111 * logger name in that hash table for the * case in order
112 * to make sure we don't enable twice the same logger
113 * because JUL apparently accepts that the *same*
114 * LogHandler can be added twice on a Logger object...
115 * don't ask...
5b5ffa03 116 */
e614d916
JG
117 logger = enabledLoggers.get(event.name);
118 if (logger != null) {
119 continue;
120 }
529e6def 121
e614d916
JG
122 /*
123 * Set to one means that the enable all event has been seen
124 * thus event from that point on must use loglevel for all
125 * events. Else the object has its own loglevel.
126 */
127 if (handler.logLevelUseAll == 1) {
128 it.remove();
129 event.logLevel.level = handler.logLevelAll;
130 event.logLevel.type = handler.logLevelTypeAll;
131 modifiedEvents.add(event);
132 }
133
134 /*
135 * The all event is a special case since we have to iterate
136 * over every Logger to see which one was not enabled.
137 */
138 if (event.name.equals("*")) {
139 enableCmd.name = event.name;
140 enableCmd.lttngLogLevel = event.logLevel.level;
141 enableCmd.lttngLogLevelType = event.logLevel.type;
142 /*
143 * The return value is irrelevant since the * event is
144 * always kept in the set.
145 */
146 enableCmd.execute(handler, enabledLoggers);
147 continue;
148 }
149
150 ret = enableCmd.enableLogger(handler, event, enabledLoggers);
151 if (ret == 1) {
152 /* Enabled so remove the event from the set. */
153 if (!modifiedEvents.remove(event)) {
154 /*
155 * event can only be present in one of
156 * the sets.
157 */
158 it.remove();
159 }
160 }
43e5396b 161 }
e614d916 162 enabledEventSet.addAll(modifiedEvents);
43e5396b 163 }
e614d916 164
43e5396b
DG
165 }
166 }, this.timerDelay, this.timerDelay);
167
168 this.timerInitialized = true;
169 }
170
171 public void init(LTTngLogHandler handler) throws InterruptedException {
172 this.handler = handler;
173
174 for (;;) {
175 if (this.quit) {
176 break;
177 }
178
179 try {
180
181 /*
182 * Connect to the session daemon before anything else.
183 */
184 connectToSessiond();
185
186 /*
187 * Register to the session daemon as the Java component of the
188 * UST application.
189 */
190 registerToSessiond();
43e5396b
DG
191
192 setupEventTimer();
193
194 /*
195 * Block on socket receive and wait for command from the
196 * session daemon. This will return if and only if there is a
197 * fatal error or the socket closes.
198 */
199 handleSessiondCmd();
200 } catch (UnknownHostException uhe) {
201 this.registerSem.release();
202 System.out.println(uhe);
203 } catch (IOException ioe) {
204 this.registerSem.release();
205 Thread.sleep(3000);
206 } catch (Exception e) {
207 this.registerSem.release();
208 e.printStackTrace();
209 }
210 }
211 }
212
213 public void destroy() {
214 this.quit = true;
215 this.eventTimer.cancel();
216
217 try {
218 if (this.sessiondSock != null) {
219 this.sessiondSock.close();
220 }
221 } catch (Exception e) {
222 e.printStackTrace();
223 }
224 }
225
226 /*
227 * Receive header data from the session daemon using the LTTng command
228 * static buffer of the right size.
229 */
230 private void recvHeader() throws Exception {
231 int read_len;
232 byte data[] = new byte[this.headerCmd.SIZE];
233
234 read_len = this.inFromSessiond.read(data, 0, data.length);
235 if (read_len != data.length) {
236 throw new IOException();
237 }
238 this.headerCmd.populate(data);
239 }
240
241 /*
242 * Receive payload from the session daemon. This MUST be done after a
243 * recvHeader() so the header value of a command are known.
244 *
245 * The caller SHOULD use isPayload() before which returns true if a payload
246 * is expected after the header.
247 */
248 private byte[] recvPayload() throws Exception {
249 byte payload[] = new byte[(int) this.headerCmd.data_size];
250
251 /* Failsafe check so we don't waste our time reading 0 bytes. */
252 if (payload.length == 0) {
253 return null;
254 }
255
256 this.inFromSessiond.read(payload, 0, payload.length);
257 return payload;
258 }
259
260 /*
261 * Handle session command from the session daemon.
262 */
263 private void handleSessiondCmd() throws Exception {
264 int ret_code;
265 byte data[] = null;
266
267 while (true) {
268 /* Get header from session daemon. */
269 recvHeader();
270
271 if (headerCmd.data_size > 0) {
272 data = recvPayload();
273 }
274
275 switch (headerCmd.cmd) {
f08bb871
DG
276 case CMD_REG_DONE:
277 {
278 /*
279 * Release semaphore so meaning registration is done and we
280 * can proceed to continue tracing.
281 */
282 this.registerSem.release();
9aabed2d
DG
283 /*
284 * We don't send any reply to the registration done command.
285 * This just marks the end of the initial session setup.
286 */
287 continue;
f08bb871 288 }
43e5396b
DG
289 case CMD_LIST:
290 {
291 LTTngSessiondCmd2_4.sessiond_list_logger listLoggerCmd =
292 new LTTngSessiondCmd2_4.sessiond_list_logger();
293 listLoggerCmd.execute(this.handler);
294 data = listLoggerCmd.getBytes();
295 break;
296 }
297 case CMD_ENABLE:
298 {
5b5ffa03 299 LTTngEvent event;
43e5396b
DG
300 LTTngSessiondCmd2_4.sessiond_enable_handler enableCmd =
301 new LTTngSessiondCmd2_4.sessiond_enable_handler();
302 if (data == null) {
303 enableCmd.code = LTTngSessiondCmd2_4.lttng_jul_ret_code.CODE_INVALID_CMD;
304 break;
305 }
306 enableCmd.populate(data);
5b5ffa03
DG
307 event = enableCmd.execute(this.handler, this.enabledLoggers);
308 if (event != null) {
43e5396b 309 /*
e614d916 310 * Add the event to the set so it can be enabled if
43e5396b
DG
311 * the logger appears at some point in time.
312 */
e614d916 313 enabledEventSet.add(event);
43e5396b
DG
314 }
315 data = enableCmd.getBytes();
316 break;
317 }
318 case CMD_DISABLE:
319 {
320 LTTngSessiondCmd2_4.sessiond_disable_handler disableCmd =
321 new LTTngSessiondCmd2_4.sessiond_disable_handler();
322 if (data == null) {
323 disableCmd.code = LTTngSessiondCmd2_4.lttng_jul_ret_code.CODE_INVALID_CMD;
324 break;
325 }
326 disableCmd.populate(data);
327 disableCmd.execute(this.handler);
328 data = disableCmd.getBytes();
329 break;
330 }
331 default:
332 {
333 data = new byte[4];
334 ByteBuffer buf = ByteBuffer.wrap(data);
335 buf.order(ByteOrder.BIG_ENDIAN);
336 LTTngSessiondCmd2_4.lttng_jul_ret_code code =
337 LTTngSessiondCmd2_4.lttng_jul_ret_code.CODE_INVALID_CMD;
338 buf.putInt(code.getCode());
339 break;
340 }
341 }
342
343 /* Send payload to session daemon. */
344 this.outToSessiond.write(data, 0, data.length);
345 this.outToSessiond.flush();
346 }
347 }
348
349 private void connectToSessiond() throws Exception {
350 this.sessiondSock = new Socket(this.sessiondHost, this.sessiondPort);
351 this.inFromSessiond = new DataInputStream(
352 sessiondSock.getInputStream());
353 this.outToSessiond = new DataOutputStream(
354 sessiondSock.getOutputStream());
355 }
356
357 private void registerToSessiond() throws Exception {
358 byte data[] = new byte[4];
359 ByteBuffer buf = ByteBuffer.wrap(data);
360 String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
361
362 buf.putInt(Integer.parseInt(pid));
363 this.outToSessiond.write(data, 0, data.length);
364 this.outToSessiond.flush();
365 }
366}
This page took 0.037921 seconds and 4 git commands to generate.