a2d12fc3008d869a9027b03b0e22244f9a92be98
[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.Map;
38 import java.util.Iterator;
39 import java.util.List;
40 import java.util.Enumeration;
41 import java.util.Set;
42 import java.util.Timer;
43 import java.util.TimerTask;
44 import java.util.logging.Logger;
45 import java.util.Collections;
46
47 class USTRegisterMsg {
48 public static int pid;
49 }
50
51 public class LTTngTCPSessiondClient {
52 /* Command header from the session deamon. */
53 private LTTngSessiondCmd2_4.sessiond_hdr headerCmd =
54 new LTTngSessiondCmd2_4.sessiond_hdr();
55
56 private final String sessiondHost;
57 private Socket sessiondSock;
58 private boolean quit = false;
59
60 private DataInputStream inFromSessiond;
61 private DataOutputStream outToSessiond;
62
63 private LTTngLogHandler handler;
64
65 private Semaphore registerSem;
66
67 private Timer eventTimer;
68
69 /*
70 * Indexed by event name but can contains duplicates since multiple
71 * sessions can enable the same event with or without different loglevels.
72 */
73 private Map<String, ArrayList<LTTngEvent>> eventMap =
74 Collections.synchronizedMap(
75 new HashMap<String, ArrayList<LTTngEvent>>());
76
77 private Set<LTTngEvent> wildCardSet =
78 Collections.synchronizedSet(new HashSet<LTTngEvent>());
79
80 /* Timer delay at each 5 seconds. */
81 private final static long timerDelay = 5 * 1000;
82 private static boolean timerInitialized;
83
84 private static final String rootPortFile = "/var/run/lttng/jul.port";
85 private static final String userPortFile = "/.lttng/jul.port";
86
87 /* Indicate if we've already release the semaphore. */
88 private boolean sem_posted = false;
89
90 public LTTngTCPSessiondClient(String host, Semaphore sem) {
91 this.sessiondHost = host;
92 this.registerSem = sem;
93 this.eventTimer = new Timer();
94 this.timerInitialized = false;
95 }
96
97 private void setupEventTimer() {
98 if (this.timerInitialized) {
99 return;
100 }
101
102 this.eventTimer.scheduleAtFixedRate(new TimerTask() {
103 @Override
104 public void run() {
105 LTTngSessiondCmd2_4.sessiond_enable_handler enableCmd = new
106 LTTngSessiondCmd2_4.sessiond_enable_handler();
107
108 synchronized (eventMap) {
109 String loggerName;
110 Enumeration loggers = handler.logManager.getLoggerNames();
111
112 /*
113 * Create an event for each logger found and attach it to the
114 * handler.
115 */
116 while (loggers.hasMoreElements()) {
117 ArrayList<LTTngEvent> bucket;
118
119 loggerName = loggers.nextElement().toString();
120
121 /* Logger is already enabled or end of list, skip it. */
122 if (handler.exists(loggerName) == true ||
123 loggerName.equals("")) {
124 continue;
125 }
126
127 bucket = eventMap.get(loggerName);
128 if (bucket == null) {
129 /* No event(s) exist for this logger. */
130 continue;
131 }
132
133 for (LTTngEvent event : bucket) {
134 enableCmd.name = event.name;
135 enableCmd.lttngLogLevel = event.logLevel.level;
136 enableCmd.lttngLogLevelType = event.logLevel.type;
137
138 /* Event exist so pass null here. */
139 enableCmd.execute(handler, null, wildCardSet);
140 }
141 }
142 }
143
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();
150
151 while (it.hasNext()) {
152 LTTngEvent event = it.next();
153
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;
159
160 /* That might create a new event so pass the map. */
161 enableCmd.execute(handler, modifiedEvents, tmpSet);
162 }
163 }
164 eventMap.putAll(modifiedEvents);
165 }
166 }
167 }, this.timerDelay, this.timerDelay);
168
169 this.timerInitialized = true;
170 }
171
172 /*
173 * Try to release the registerSem if it's not already done.
174 */
175 private void tryReleaseSem()
176 {
177 /* Release semaphore so we unblock the agent. */
178 if (!this.sem_posted) {
179 this.registerSem.release();
180 this.sem_posted = true;
181 }
182 }
183
184 /*
185 * Cleanup Agent state.
186 */
187 private void cleanupState() {
188 eventMap.clear();
189 wildCardSet.clear();
190 if (this.handler != null) {
191 this.handler.clear();
192 }
193 }
194
195 public void init(LTTngLogHandler handler) throws InterruptedException {
196 this.handler = handler;
197
198 for (;;) {
199 if (this.quit) {
200 break;
201 }
202
203 /* Cleanup Agent state before trying to connect or reconnect. */
204 cleanupState();
205
206 try {
207
208 /*
209 * Connect to the session daemon before anything else.
210 */
211 connectToSessiond();
212
213 /*
214 * Register to the session daemon as the Java component of the
215 * UST application.
216 */
217 registerToSessiond();
218
219 setupEventTimer();
220
221 /*
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.
225 */
226 handleSessiondCmd();
227 } catch (UnknownHostException uhe) {
228 tryReleaseSem();
229 System.out.println(uhe);
230 } catch (IOException ioe) {
231 tryReleaseSem();
232 Thread.sleep(3000);
233 } catch (Exception e) {
234 tryReleaseSem();
235 e.printStackTrace();
236 }
237 }
238 }
239
240 public void destroy() {
241 this.quit = true;
242 this.eventTimer.cancel();
243
244 try {
245 if (this.sessiondSock != null) {
246 this.sessiondSock.close();
247 }
248 } catch (Exception e) {
249 e.printStackTrace();
250 }
251 }
252
253 /*
254 * Receive header data from the session daemon using the LTTng command
255 * static buffer of the right size.
256 */
257 private void recvHeader() throws Exception {
258 int read_len;
259 byte data[] = new byte[this.headerCmd.SIZE];
260
261 read_len = this.inFromSessiond.read(data, 0, data.length);
262 if (read_len != data.length) {
263 throw new IOException();
264 }
265 this.headerCmd.populate(data);
266 }
267
268 /*
269 * Receive payload from the session daemon. This MUST be done after a
270 * recvHeader() so the header value of a command are known.
271 *
272 * The caller SHOULD use isPayload() before which returns true if a payload
273 * is expected after the header.
274 */
275 private byte[] recvPayload() throws Exception {
276 byte payload[] = new byte[(int) this.headerCmd.data_size];
277
278 /* Failsafe check so we don't waste our time reading 0 bytes. */
279 if (payload.length == 0) {
280 return null;
281 }
282
283 this.inFromSessiond.read(payload, 0, payload.length);
284 return payload;
285 }
286
287 /*
288 * Handle session command from the session daemon.
289 */
290 private void handleSessiondCmd() throws Exception {
291 int ret_code;
292 byte data[] = null;
293
294 while (true) {
295 /* Get header from session daemon. */
296 recvHeader();
297
298 if (headerCmd.data_size > 0) {
299 data = recvPayload();
300 }
301
302 switch (headerCmd.cmd) {
303 case CMD_REG_DONE:
304 {
305 /*
306 * Release semaphore so meaning registration is done and we
307 * can proceed to continue tracing.
308 */
309 tryReleaseSem();
310 /*
311 * We don't send any reply to the registration done command.
312 * This just marks the end of the initial session setup.
313 */
314 continue;
315 }
316 case CMD_LIST:
317 {
318 LTTngSessiondCmd2_4.sessiond_list_logger listLoggerCmd =
319 new LTTngSessiondCmd2_4.sessiond_list_logger();
320 listLoggerCmd.execute(this.handler);
321 data = listLoggerCmd.getBytes();
322 break;
323 }
324 case CMD_ENABLE:
325 {
326 LTTngEvent event;
327 LTTngSessiondCmd2_4.sessiond_enable_handler enableCmd =
328 new LTTngSessiondCmd2_4.sessiond_enable_handler();
329 if (data == null) {
330 enableCmd.code = LTTngSessiondCmd2_4.lttng_jul_ret_code.CODE_INVALID_CMD;
331 break;
332 }
333 enableCmd.populate(data);
334 enableCmd.execute(this.handler, this.eventMap, this.wildCardSet);
335 data = enableCmd.getBytes();
336 break;
337 }
338 case CMD_DISABLE:
339 {
340 LTTngSessiondCmd2_4.sessiond_disable_handler disableCmd =
341 new LTTngSessiondCmd2_4.sessiond_disable_handler();
342 if (data == null) {
343 disableCmd.code = LTTngSessiondCmd2_4.lttng_jul_ret_code.CODE_INVALID_CMD;
344 break;
345 }
346 disableCmd.populate(data);
347 disableCmd.execute(this.handler, this.eventMap, this.wildCardSet);
348 data = disableCmd.getBytes();
349 break;
350 }
351 default:
352 {
353 data = new byte[4];
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());
359 break;
360 }
361 }
362
363 /* Send payload to session daemon. */
364 this.outToSessiond.write(data, 0, data.length);
365 this.outToSessiond.flush();
366 }
367 }
368
369 private String getHomePath() {
370 return System.getProperty("user.home");
371 }
372
373 /**
374 * Read port number from file created by the session daemon.
375 *
376 * @return port value if found else 0.
377 */
378 private int getPortFromFile(String path) throws IOException {
379 int port;
380 BufferedReader br;
381
382 try {
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. */
388 port = 0;
389 }
390 br.close();
391 } catch (FileNotFoundException e) {
392 /* No port available. */
393 port = 0;
394 }
395
396 return port;
397 }
398
399 private void connectToSessiond() throws Exception {
400 int port;
401
402 if (this.handler.is_root == 1) {
403 port = getPortFromFile(rootPortFile);
404 if (port == 0) {
405 /* No session daemon available. Stop and retry later. */
406 throw new IOException();
407 }
408 } else {
409 port = getPortFromFile(getHomePath() + userPortFile);
410 if (port == 0) {
411 /* No session daemon available. Stop and retry later. */
412 throw new IOException();
413 }
414 }
415
416 this.sessiondSock = new Socket(this.sessiondHost, port);
417 this.inFromSessiond = new DataInputStream(
418 sessiondSock.getInputStream());
419 this.outToSessiond = new DataOutputStream(
420 sessiondSock.getOutputStream());
421 }
422
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];
427
428 buf.putInt(Integer.parseInt(pid));
429 this.outToSessiond.write(data, 0, data.length);
430 this.outToSessiond.flush();
431 }
432 }
This page took 0.038045 seconds and 3 git commands to generate.