Add jul-app ABI/API and handle registration
[lttng-tools.git] / src / bin / lttng-sessiond / jul.c
... / ...
CommitLineData
1/*
2 * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License, version 2 only, as
6 * published by the Free Software Foundation.
7 *
8 * This program 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 General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License along with
14 * this program; if not, write to the Free Software Foundation, Inc., 51
15 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 */
17
18#define _GNU_SOURCE
19#include <assert.h>
20#include <urcu/uatomic.h>
21
22#include <common/common.h>
23#include <common/sessiond-comm/jul.h>
24
25#include "jul.h"
26#include "ust-app.h"
27#include "utils.h"
28
29/*
30 * URCU intermediate call to complete destroy a JUL event.
31 */
32static void destroy_event_jul_rcu(struct rcu_head *head)
33{
34 struct lttng_ht_node_str *node =
35 caa_container_of(head, struct lttng_ht_node_str, head);
36 struct jul_event *event =
37 caa_container_of(node, struct jul_event, node);
38
39 free(event);
40}
41
42/*
43 * URCU intermediate call to complete destroy a JUL event.
44 */
45static void destroy_app_jul_rcu(struct rcu_head *head)
46{
47 struct lttng_ht_node_ulong *node =
48 caa_container_of(head, struct lttng_ht_node_ulong, head);
49 struct jul_app *app =
50 caa_container_of(node, struct jul_app, node);
51
52 free(app);
53}
54
55/*
56 * Communication with Java agent call. Send the message header to the given
57 * socket all in big endian.
58 *
59 * Return 0 on success or else a negative errno message of sendmsg() op.
60 */
61static int send_header(struct lttcomm_sock *sock, uint64_t data_size,
62 uint32_t cmd, uint32_t cmd_version)
63{
64 int ret;
65 ssize_t size;
66 struct lttcomm_jul_hdr msg;
67
68 assert(sock);
69
70 msg.data_size = htobe64(data_size);
71 msg.cmd = htobe32(cmd);
72 msg.cmd_version = htobe32(cmd_version);
73
74 size = sock->ops->sendmsg(sock, &msg, sizeof(msg), 0);
75 if (size < sizeof(msg)) {
76 ret = -errno;
77 goto error;
78 }
79 ret = 0;
80
81error:
82 return ret;
83}
84
85/*
86 * Communication call with the Java agent. Send the payload to the given
87 * socket. The header MUST be sent prior to this call.
88 *
89 * Return 0 on success or else a negative errno value of sendmsg() op.
90 */
91static int send_payload(struct lttcomm_sock *sock, void *data,
92 size_t size)
93{
94 int ret;
95 ssize_t len;
96
97 assert(sock);
98 assert(data);
99
100 len = sock->ops->sendmsg(sock, data, size, 0);
101 if (len < size) {
102 ret = -errno;
103 goto error;
104 }
105 ret = 0;
106
107error:
108 return ret;
109}
110
111/*
112 * Communication call with the Java agent. Receive reply from the agent using
113 * the given socket.
114 *
115 * Return 0 on success or else a negative errno value from recvmsg() op.
116 */
117static int recv_reply(struct lttcomm_sock *sock, void *buf, size_t size)
118{
119 int ret;
120 ssize_t len;
121
122 assert(sock);
123 assert(buf);
124
125 len = sock->ops->recvmsg(sock, buf, size, 0);
126 if (len < size) {
127 ret = -errno;
128 goto error;
129 }
130 ret = 0;
131
132error:
133 return ret;
134}
135
136/*
137 * Internal enable JUL event call on a JUL application. This function
138 * communicates with the Java agent to enable a given event (Logger name).
139 *
140 * Return LTTNG_OK on success or else a LTTNG_ERR* code.
141 */
142static int enable_event(struct jul_app *app, struct jul_event *event)
143{
144 int ret;
145 uint64_t data_size;
146 struct lttcomm_jul_enable msg;
147 struct lttcomm_jul_generic_reply reply;
148
149 assert(app);
150 assert(app->sock);
151 assert(event);
152
153 DBG2("JUL enabling event %s for app pid: %d and socket %d", event->name,
154 app->pid, app->sock->fd);
155
156 data_size = sizeof(msg);
157
158 ret = send_header(app->sock, data_size, JUL_CMD_ENABLE, 0);
159 if (ret < 0) {
160 goto error_io;
161 }
162
163 strncpy(msg.name, event->name, sizeof(msg.name));
164 ret = send_payload(app->sock, &msg, sizeof(msg));
165 if (ret < 0) {
166 goto error_io;
167 }
168
169 ret = recv_reply(app->sock, &reply, sizeof(reply));
170 if (ret < 0) {
171 goto error_io;
172 }
173
174 switch (be32toh(reply.ret_code)) {
175 case JUL_RET_CODE_SUCCESS:
176 break;
177 case JUL_RET_CODE_UNKNOWN_NAME:
178 ret = LTTNG_ERR_UST_EVENT_NOT_FOUND;
179 goto error;
180 default:
181 ERR("Java agent returned an unknown code: %" PRIu32,
182 be32toh(reply.ret_code));
183 ret = LTTNG_ERR_FATAL;
184 goto error;
185 }
186
187 return LTTNG_OK;
188
189error_io:
190 ret = LTTNG_ERR_UST_ENABLE_FAIL;
191error:
192 return ret;
193}
194
195/*
196 * Internal disable JUL event call on a JUL application. This function
197 * communicates with the Java agent to disable a given event (Logger name).
198 *
199 * Return LTTNG_OK on success or else a LTTNG_ERR* code.
200 */
201static int disable_event(struct jul_app *app, struct jul_event *event)
202{
203 int ret;
204 uint64_t data_size;
205 struct lttcomm_jul_disable msg;
206 struct lttcomm_jul_generic_reply reply;
207
208 assert(app);
209 assert(app->sock);
210 assert(event);
211
212 DBG2("JUL disabling event %s for app pid: %d and socket %d", event->name,
213 app->pid, app->sock->fd);
214
215 data_size = sizeof(msg);
216
217 ret = send_header(app->sock, data_size, JUL_CMD_DISABLE, 0);
218 if (ret < 0) {
219 goto error_io;
220 }
221
222 strncpy(msg.name, event->name, sizeof(msg.name));
223 ret = send_payload(app->sock, &msg, sizeof(msg));
224 if (ret < 0) {
225 goto error_io;
226 }
227
228 ret = recv_reply(app->sock, &reply, sizeof(reply));
229 if (ret < 0) {
230 goto error_io;
231 }
232
233 switch (be32toh(reply.ret_code)) {
234 case JUL_RET_CODE_SUCCESS:
235 break;
236 case JUL_RET_CODE_UNKNOWN_NAME:
237 ret = LTTNG_ERR_UST_EVENT_NOT_FOUND;
238 goto error;
239 default:
240 ERR("Java agent returned an unknown code: %" PRIu32,
241 be32toh(reply.ret_code));
242 ret = LTTNG_ERR_FATAL;
243 goto error;
244 }
245
246 return LTTNG_OK;
247
248error_io:
249 ret = LTTNG_ERR_UST_DISABLE_FAIL;
250error:
251 return ret;
252}
253
254/*
255 * Enable JUL event on every JUL applications registered with the session
256 * daemon.
257 *
258 * Return LTTNG_OK on success or else a LTTNG_ERR* code.
259 */
260int jul_enable_event(struct jul_event *event)
261{
262 int ret;
263 struct jul_app *app;
264 struct lttng_ht_iter iter;
265
266 assert(event);
267
268 rcu_read_lock();
269
270 cds_lfht_for_each_entry(jul_apps_ht_by_sock->ht, &iter.iter, app,
271 node.node) {
272 /* Enable event on JUL application through TCP socket. */
273 ret = enable_event(app, event);
274 if (ret != LTTNG_OK) {
275 goto error;
276 }
277 event->enabled = 1;
278 }
279
280 ret = LTTNG_OK;
281
282error:
283 rcu_read_unlock();
284 return ret;
285}
286
287/*
288 * Disable JUL event on every JUL applications registered with the session
289 * daemon.
290 *
291 * Return LTTNG_OK on success or else a LTTNG_ERR* code.
292 */
293int jul_disable_event(struct jul_event *event)
294{
295 int ret;
296 struct jul_app *app;
297 struct lttng_ht_iter iter;
298
299 assert(event);
300
301 rcu_read_lock();
302
303 cds_lfht_for_each_entry(jul_apps_ht_by_sock->ht, &iter.iter, app,
304 node.node) {
305 /* Enable event on JUL application through TCP socket. */
306 ret = disable_event(app, event);
307 if (ret != LTTNG_OK) {
308 goto error;
309 }
310 event->enabled = 0;
311 }
312
313 ret = LTTNG_OK;
314
315error:
316 rcu_read_unlock();
317 return ret;
318}
319
320/*
321 * Ask every java agent for the list of possible event (logger name). Events is
322 * allocated with the events of every JUL application.
323 *
324 * Return the number of events or else a negative value.
325 */
326int jul_list_events(struct lttng_event **events)
327{
328 int ret;
329 size_t nbmem, count = 0;
330 struct jul_app *app;
331 struct lttng_event *tmp_events;
332 struct lttng_ht_iter iter;
333
334 assert(events);
335
336 nbmem = UST_APP_EVENT_LIST_SIZE;
337 tmp_events = zmalloc(nbmem * sizeof(*tmp_events));
338 if (!tmp_events) {
339 PERROR("zmalloc jul list events");
340 ret = -ENOMEM;
341 goto error;
342 }
343
344 rcu_read_lock();
345 cds_lfht_for_each_entry(jul_apps_ht_by_sock->ht, &iter.iter, app,
346 node.node) {
347 ssize_t nb_ev;
348 struct lttng_event *jul_events;
349
350 nb_ev = list_events(app, &jul_events);
351 if (nb_ev < 0) {
352 ret = nb_ev;
353 rcu_read_unlock();
354 goto error;
355 }
356
357 if (count >= nbmem) {
358 /* In case the realloc fails, we free the memory */
359 void *ptr;
360
361 DBG2("Reallocating JUL event list from %zu to %zu entries", nbmem,
362 2 * nbmem);
363 nbmem *= 2;
364 ptr = realloc(tmp_events, nbmem * sizeof(*tmp_events));
365 if (!ptr) {
366 PERROR("realloc JUL events");
367 free(tmp_events);
368 ret = -ENOMEM;
369 rcu_read_unlock();
370 goto error;
371 }
372 tmp_events = ptr;
373 }
374 memcpy(tmp_events + (count * sizeof(*tmp_events)), jul_events,
375 nb_ev * sizeof(*tmp_events));
376 free(jul_events);
377 count += nb_ev;
378 }
379 rcu_read_unlock();
380
381 ret = count;
382 *events = tmp_events;
383
384error:
385 return ret;
386}
387
388/*
389 * Create a JUL app object using the given PID.
390 *
391 * Return newly allocated object or else NULL on error.
392 */
393struct jul_app *jul_create_app(pid_t pid, struct lttcomm_sock *sock)
394{
395 struct jul_app *app;
396
397 assert(sock);
398
399 app = zmalloc(sizeof(*app));
400 if (!app) {
401 PERROR("zmalloc JUL create");
402 goto error;
403 }
404
405 app->pid = pid;
406 app->sock = sock;
407 /* Flag it invalid until assignation. */
408 app->ust_app_sock = -1;
409 lttng_ht_node_init_ulong(&app->node, (unsigned long) app->sock->fd);
410
411error:
412 return app;
413}
414
415/*
416 * Lookup JUL app by socket in the global hash table.
417 *
418 * RCU read side lock MUST be acquired.
419 *
420 * Return object if found else NULL.
421 */
422struct jul_app *jul_find_app_by_sock(int sock)
423{
424 struct lttng_ht_node_ulong *node;
425 struct lttng_ht_iter iter;
426 struct jul_app *app;
427
428 assert(sock >= 0);
429
430 lttng_ht_lookup(jul_apps_ht_by_sock, (void *)((unsigned long) sock), &iter);
431 node = lttng_ht_iter_get_node_ulong(&iter);
432 if (node == NULL) {
433 goto error;
434 }
435 app = caa_container_of(node, struct jul_app, node);
436
437 DBG3("JUL app pid %d found by sock %d.", app->pid, sock);
438 return app;
439
440error:
441 DBG3("JUL app NOT found by sock %d.", sock);
442 return NULL;
443}
444
445/*
446 * Add JUL application object to a given hash table.
447 */
448void jul_add_app(struct jul_app *app)
449{
450 assert(app);
451
452 DBG3("JUL adding app sock: %d and pid: %d to ht", app->sock->fd, app->pid);
453
454 rcu_read_lock();
455 lttng_ht_add_unique_ulong(jul_apps_ht_by_sock, &app->node);
456 rcu_read_unlock();
457}
458
459/*
460 * Attach a given JUL application to an UST app object. This is done by copying
461 * the socket fd value into the ust app obj. atomically.
462 */
463void jul_attach_app(struct jul_app *japp)
464{
465 struct ust_app *uapp;
466
467 assert(japp);
468
469 rcu_read_lock();
470 uapp = ust_app_find_by_pid(japp->pid);
471 if (!uapp) {
472 goto end;
473 }
474
475 uatomic_set(&uapp->jul_app_sock, japp->sock->fd);
476
477 DBG3("JUL app pid: %d, sock: %d attached to UST app.", japp->pid,
478 japp->sock->fd);
479
480end:
481 rcu_read_unlock();
482 return;
483}
484
485/*
486 * Remove JUL app. reference from an UST app object and set it to NULL.
487 */
488void jul_detach_app(struct jul_app *japp)
489{
490 struct ust_app *uapp;
491
492 assert(japp);
493
494 rcu_read_lock();
495
496 if (japp->ust_app_sock < 0) {
497 goto end;
498 }
499
500 uapp = ust_app_find_by_sock(japp->ust_app_sock);
501 if (!uapp) {
502 goto end;
503 }
504
505 uapp->jul_app_sock = -1;
506
507end:
508 rcu_read_unlock();
509 return;
510}
511
512/*
513 * Delete JUL application from the global hash table.
514 */
515void jul_delete_app(struct jul_app *app)
516{
517 int ret;
518 struct lttng_ht_iter iter;
519
520 assert(app);
521
522 DBG3("JUL deleting app pid: %d and sock: %d", app->pid, app->sock->fd);
523
524 iter.iter.node = &app->node.node;
525 rcu_read_lock();
526 ret = lttng_ht_del(jul_apps_ht_by_sock, &iter);
527 rcu_read_unlock();
528 assert(!ret);
529}
530
531/*
532 * Destroy a JUL application object by detaching it from its corresponding UST
533 * app if one, closing the socket and freeing the memory.
534 */
535void jul_destroy_app(struct jul_app *app)
536{
537 assert(app);
538
539 if (app->sock) {
540 app->sock->ops->close(app->sock);
541 lttcomm_destroy_sock(app->sock);
542 }
543
544 call_rcu(&app->node.head, destroy_app_jul_rcu);
545}
546
547/*
548 * Initialize an already allocated JUL domain object.
549 *
550 * Return 0 on success or else a negative errno value.
551 */
552int jul_init_domain(struct jul_domain *dom)
553{
554 int ret;
555
556 assert(dom);
557
558 dom->events = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
559 if (!dom->events) {
560 ret = -ENOMEM;
561 goto error;
562 }
563
564 return 0;
565
566error:
567 return ret;
568}
569
570/*
571 * Create a newly allocated JUL event data structure. If name is valid, it's
572 * copied into the created event.
573 *
574 * Return a new object else NULL on error.
575 */
576struct jul_event *jul_create_event(const char *name)
577{
578 struct jul_event *event;
579
580 DBG3("JUL create new event with name %s", name);
581
582 event = zmalloc(sizeof(*event));
583 if (!event) {
584 goto error;
585 }
586
587 if (name) {
588 strncpy(event->name, name, sizeof(event->name));
589 event->name[sizeof(event->name) - 1] = '\0';
590 lttng_ht_node_init_str(&event->node, event->name);
591 }
592
593error:
594 return event;
595}
596
597/*
598 * Unique add of a JUL event to a given domain.
599 */
600void jul_add_event(struct jul_event *event, struct jul_domain *dom)
601{
602 assert(event);
603 assert(dom);
604 assert(dom->events);
605
606 DBG3("JUL adding event %s to domain", event->name);
607
608 rcu_read_lock();
609 lttng_ht_add_unique_str(dom->events, &event->node);
610 rcu_read_unlock();
611}
612
613/*
614 * Find a JUL event in the given domain using name.
615 *
616 * RCU read side lock MUST be acquired.
617 *
618 * Return object if found else NULL.
619 */
620struct jul_event *jul_find_by_name(const char *name, struct jul_domain *dom)
621{
622 struct lttng_ht_node_str *node;
623 struct lttng_ht_iter iter;
624
625 assert(name);
626 assert(dom);
627 assert(dom->events);
628
629 lttng_ht_lookup(dom->events, (void *)name, &iter);
630 node = lttng_ht_iter_get_node_str(&iter);
631 if (node == NULL) {
632 goto error;
633 }
634
635 DBG3("JUL found by name %s in domain.", name);
636 return caa_container_of(node, struct jul_event, node);
637
638error:
639 DBG3("JUL NOT found by name %s in domain.", name);
640 return NULL;
641}
642
643/*
644 * Delete JUL event from given domain. Events hash table MUST be initialized.
645 */
646void jul_delete_event(struct jul_event *event, struct jul_domain *dom)
647{
648 int ret;
649 struct lttng_ht_iter iter;
650
651 assert(event);
652 assert(dom);
653 assert(dom->events);
654
655 DBG3("JUL deleting event %s from domain", event->name);
656
657 iter.iter.node = &event->node.node;
658 rcu_read_lock();
659 ret = lttng_ht_del(dom->events, &iter);
660 rcu_read_unlock();
661 assert(!ret);
662}
663
664/*
665 * Free given JUl event. After this call, the pointer is not usable anymore.
666 */
667void jul_destroy_event(struct jul_event *event)
668{
669 assert(event);
670
671 free(event);
672}
673
674/*
675 * Destroy a JUL domain completely. Note that the given pointer is NOT freed
676 * thus a reference can be passed to this function.
677 */
678void jul_destroy_domain(struct jul_domain *dom)
679{
680 struct lttng_ht_node_str *node;
681 struct lttng_ht_iter iter;
682
683 assert(dom);
684
685 DBG3("JUL destroy domain");
686
687 /*
688 * Just ignore if no events hash table exists. This is possible if for
689 * instance a JUL domain object was allocated but not initialized.
690 */
691 if (!dom->events) {
692 return;
693 }
694
695 rcu_read_lock();
696 cds_lfht_for_each_entry(dom->events->ht, &iter.iter, node, node) {
697 int ret;
698
699 ret = lttng_ht_del(dom->events, &iter);
700 assert(!ret);
701 call_rcu(&node->head, destroy_event_jul_rcu);
702 }
703 rcu_read_unlock();
704
705 lttng_ht_destroy(dom->events);
706}
707
708/*
709 * Initialize JUL subsystem.
710 */
711int jul_init(void)
712{
713 jul_apps_ht_by_sock = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
714 if (!jul_apps_ht_by_sock) {
715 return -1;
716 }
717
718 return 0;
719}
720
721/*
722 * Update a JUL application (given socket) using the given domain.
723 *
724 * Note that this function is most likely to be used with a tracing session
725 * thus the caller should make sure to hold the appropriate lock(s).
726 */
727void jul_update(struct jul_domain *domain, int sock)
728{
729 int ret;
730 struct jul_app *app;
731 struct jul_event *event;
732 struct lttng_ht_iter iter;
733
734 assert(domain);
735 assert(sock >= 0);
736
737 DBG("JUL updating app socket %d", sock);
738
739 rcu_read_lock();
740 cds_lfht_for_each_entry(domain->events->ht, &iter.iter, event, node.node) {
741 /* Skip event if disabled. */
742 if (!event->enabled) {
743 continue;
744 }
745
746 app = jul_find_app_by_sock(sock);
747 /*
748 * We are in the registration path thus if the application is gone,
749 * there is a serious code flow error.
750 */
751 assert(app);
752
753 ret = enable_event(app, event);
754 if (ret != LTTNG_OK) {
755 DBG2("JUL update unable to enable event %s on app pid: %d sock %d",
756 event->name, app->pid, app->sock->fd);
757 /* Let's try the others here and don't assume the app is dead. */
758 continue;
759 }
760 }
761 rcu_read_unlock();
762}
This page took 0.026181 seconds and 5 git commands to generate.