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