From b178f53e90c376dd44b020535c32649edef8f80e Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Galarneau?= Date: Mon, 8 Apr 2019 15:44:01 -0400 Subject: [PATCH] Generate session name and default output on sessiond's end MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The lttng client currently generates the default session name and output parameters. This has, over time, resulted in a number of problems. Notably, it is possible for scripts to create session too quickly using automatically-generated session names that would clash since the session's creation timestamp is the only variable part of a session "automatic" name. Hence, sessions created in the same second would clash and result in spurious session creation failures. More importantly, generating session names and outputs on the client end makes it impossible to reliably differentiate output locations that were automatically generated vs. those that were explicitly provided. This causes destinations to be "opaque" to the LTTng daemons as the subdir, session name, and session's creation timestamp are all "cooked" as part of the output destination path/subdir. Keeping these path components separate will make it easier to implement output path configurations that allow the grouping of session outputs by name, by host, etc. Since a session's creation time is used as part of its shm-path, an accessor to the session's creation time is added to the public API: lttng_session_get_creation_time(). This creation time attribute can be accessed when an lttng_session structure is created using the session listing API. Note that existing session creation functions are preserved to maintain the binary compatibility with existing liblttng-ctl users. The session creation functions are reimplemented on top of this newly-introduced API. The only function for which compatibility is dropped is the hidden _lttng_create_session_ext(). Overhaul of path separation --- Not generating paths on the client-end has uncovered a number of problems in the path handling of the session daemon, especially when a network output was used. A lot of code presumed that a network session would be created with a URL containing a sub-directory of the form "session_name-timestamp". While this is true for remote sessions created by the lttng client, a sub-directory is not required when liblttng-ctl is used directly. Hence, this commit ensures that session directories are split as base path, chunk directory, domain directory, application directory. A number of changes in this fix ensure that a session's base path contains everything up to the "session" path element _or_ up to the user-specified output directory. For example, creating a local session using default output settings, the session base output is: /home/user/lttng-traces/session-timestamp Creating a remote session using default output settings, the session base output path is: /hostname/session-timestamp/ Using custom output directories, whether locally or remotely, causes the session base path to be set to that custom output directory. For example, using a local output path of /tmp/my_path will result in a session base path of the form: /tmp/my_path Whereas creating a session with a network output of net://localhost/my_path will result in a session base path of the form: /hostname/my_path Another problematic element is the subdir of the kernel_session and ust_session consumer output which in different scenarios contained chunk names and arbitrary parts of the path hierarchy. The consumer output subdir has been renamed to 'domain_subdir' and now only ever contains: "kernel/", "ust/", or "". Finally, the chunk_path session attribute only contains the name of the current chunk directory being produced. Signed-off-by: Jérémie Galarneau --- extras/bindings/swig/python/lttng.i.in | 9 +- include/Makefile.am | 5 +- include/lttng/lttng-error.h | 1 + include/lttng/lttng.h | 1 + include/lttng/session-descriptor-internal.h | 112 ++ include/lttng/session-descriptor.h | 257 ++++ include/lttng/session-internal.h | 31 + include/lttng/session.h | 45 +- src/bin/lttng-sessiond/client.c | 163 +-- src/bin/lttng-sessiond/cmd.c | 533 +++++--- src/bin/lttng-sessiond/cmd.h | 10 +- src/bin/lttng-sessiond/consumer.c | 215 ++- src/bin/lttng-sessiond/consumer.h | 18 +- src/bin/lttng-sessiond/kernel-consumer.c | 9 +- src/bin/lttng-sessiond/kernel.c | 4 +- src/bin/lttng-sessiond/rotate.c | 141 +- src/bin/lttng-sessiond/session.c | 170 ++- src/bin/lttng-sessiond/session.h | 12 +- src/bin/lttng-sessiond/snapshot.c | 20 +- src/bin/lttng-sessiond/snapshot.h | 7 +- src/bin/lttng-sessiond/ust-app.c | 25 +- src/bin/lttng-sessiond/ust-consumer.c | 8 +- src/bin/lttng-sessiond/utils.c | 18 +- src/bin/lttng/commands/create.c | 515 +++---- src/common/Makefile.am | 3 +- src/common/error.c | 1 + src/common/session-descriptor.c | 1180 +++++++++++++++++ src/common/sessiond-comm/sessiond-comm.h | 19 +- src/lib/lttng-ctl/lttng-ctl.c | 465 ++++--- .../tools/snapshots/test_kernel_streaming | 12 +- .../tools/snapshots/test_ust_streaming | 15 +- tests/unit/test_session.c | 53 +- 32 files changed, 2956 insertions(+), 1121 deletions(-) create mode 100644 include/lttng/session-descriptor-internal.h create mode 100644 include/lttng/session-descriptor.h create mode 100644 include/lttng/session-internal.h create mode 100644 src/common/session-descriptor.c diff --git a/extras/bindings/swig/python/lttng.i.in b/extras/bindings/swig/python/lttng.i.in index d74e08478..7418d01d6 100644 --- a/extras/bindings/swig/python/lttng.i.in +++ b/extras/bindings/swig/python/lttng.i.in @@ -156,12 +156,10 @@ enum lttng_event_context_type { PyObject *name = PyString_FromString((*$1)[i].name); PyObject *path = PyString_FromString((*$1)[i].path); PyObject *enabled = PyInt_FromSize_t((*$1)[i].enabled); - PyObject *padding = PyString_FromString((*$1)[i].padding); PyTuple_SetItem(tmp, 0, name); PyTuple_SetItem(tmp, 1, path); PyTuple_SetItem(tmp, 2, enabled); - PyTuple_SetItem(tmp, 3, padding); PyList_Append(sessions, tmp); } $result = sessions; @@ -990,7 +988,12 @@ struct lttng_session { char name[NAME_MAX]; char path[PATH_MAX]; uint32_t enabled; - char padding[LTTNG_SESSION_PADDING1]; + uint32_t snapshot_mode; + unsigned int live_timer_interval; + union { + char padding[LTTNG_SESSION_PADDING1]; + void *ptr; + } extended; %extend { char *__repr__() { diff --git a/include/Makefile.am b/include/Makefile.am index a4fa6fb3a..7f27cc82f 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -83,7 +83,8 @@ lttnginclude_HEADERS = \ lttng/endpoint.h \ lttng/rotation.h \ lttng/location.h \ - lttng/userspace-probe.h + lttng/userspace-probe.h \ + lttng/session-descriptor.h lttngactioninclude_HEADERS= \ lttng/action/action.h \ @@ -125,5 +126,7 @@ noinst_HEADERS = \ lttng/ref-internal.h \ lttng/location-internal.h \ lttng/userspace-probe-internal.h \ + lttng/session-internal.h \ + lttng/session-descriptor-internal.h \ version.h \ version.i diff --git a/include/lttng/lttng-error.h b/include/lttng/lttng-error.h index 3f645fe1b..edd9fc39e 100644 --- a/include/lttng/lttng-error.h +++ b/include/lttng/lttng-error.h @@ -168,6 +168,7 @@ enum lttng_error_code { LTTNG_ERR_MKDIR_FAIL_CONSUMER = 145, /* mkdir failure on consumer */ LTTNG_ERR_CHAN_NOT_FOUND = 146, /* Channel not found */ LTTNG_ERR_SNAPSHOT_UNSUPPORTED = 147, /* Session configuration does not allow the use of snapshots */ + LTTNG_ERR_SESSION_NOT_EXIST = 148, /* The session does not exist on the session daemon */ /* MUST be last element */ LTTNG_ERR_NR, /* Last element */ diff --git a/include/lttng/lttng.h b/include/lttng/lttng.h index 5c17fe37e..bde1547a4 100644 --- a/include/lttng/lttng.h +++ b/include/lttng/lttng.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include diff --git a/include/lttng/session-descriptor-internal.h b/include/lttng/session-descriptor-internal.h new file mode 100644 index 000000000..09d493393 --- /dev/null +++ b/include/lttng/session-descriptor-internal.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2019 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_SESSION_DESCRIPTOR_INTERNAL_H +#define LTTNG_SESSION_DESCRIPTOR_INTERNAL_H + +#include +#include +#include +#include +#include +#include + +/* Note that these enums are used as part of the lttnctl protocol. */ +enum lttng_session_descriptor_type { + LTTNG_SESSION_DESCRIPTOR_TYPE_UNKNOWN = -1, + /* + * The output type determines whether this is a no-output, local, + * or networked tracing session. + */ + LTTNG_SESSION_DESCRIPTOR_TYPE_REGULAR = 1, + LTTNG_SESSION_DESCRIPTOR_TYPE_SNAPSHOT = 2, + LTTNG_SESSION_DESCRIPTOR_TYPE_LIVE = 3, +}; + +enum lttng_session_descriptor_output_type { + LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE = 0, + LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL = 1, + LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK = 2, +}; + +LTTNG_HIDDEN +ssize_t lttng_session_descriptor_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_session_descriptor **descriptor); + +LTTNG_HIDDEN +int lttng_session_descriptor_serialize( + const struct lttng_session_descriptor *descriptor, + struct lttng_dynamic_buffer *buffer); + +LTTNG_HIDDEN +enum lttng_session_descriptor_type +lttng_session_descriptor_get_type( + const struct lttng_session_descriptor *descriptor); + +LTTNG_HIDDEN +enum lttng_session_descriptor_output_type +lttng_session_descriptor_get_output_type( + const struct lttng_session_descriptor *descriptor); + +LTTNG_HIDDEN +void lttng_session_descriptor_get_local_output_uri( + const struct lttng_session_descriptor *descriptor, + struct lttng_uri *local_uri); + +LTTNG_HIDDEN +void lttng_session_descriptor_get_network_output_uris( + const struct lttng_session_descriptor *descriptor, + struct lttng_uri *control, + struct lttng_uri *data); + +LTTNG_HIDDEN +unsigned long long +lttng_session_descriptor_live_get_timer_interval( + const struct lttng_session_descriptor *descriptor); + +LTTNG_HIDDEN +enum lttng_session_descriptor_status +lttng_session_descriptor_get_session_name( + const struct lttng_session_descriptor *descriptor, + const char **name); + +LTTNG_HIDDEN +int lttng_session_descriptor_set_session_name( + struct lttng_session_descriptor *descriptor, + const char *name); + +LTTNG_HIDDEN +bool lttng_session_descriptor_is_output_destination_initialized( + const struct lttng_session_descriptor *descriptor); + +LTTNG_HIDDEN +bool lttng_session_descriptor_has_output_directory( + const struct lttng_session_descriptor *descriptor); + +LTTNG_HIDDEN +enum lttng_error_code lttng_session_descriptor_set_default_output( + struct lttng_session_descriptor *descriptor, + time_t *session_creation_time, + const char *absolute_home_path); + +LTTNG_HIDDEN +int lttng_session_descriptor_assign( + struct lttng_session_descriptor *dst_descriptor, + const struct lttng_session_descriptor *src_descriptor); + +#endif /* LTTNG_SESSION_DESCRIPTOR_INTERNAL_H */ diff --git a/include/lttng/session-descriptor.h b/include/lttng/session-descriptor.h new file mode 100644 index 000000000..32799684d --- /dev/null +++ b/include/lttng/session-descriptor.h @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2019 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_SESSION_DESCRIPTOR_H +#define LTTNG_SESSION_DESCRIPTOR_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct lttng_session_descriptor; + +/* + * Session descriptor API. + * + * A session descriptor is an object describing the immutable configuration + * options of an LTTng tracing session. + * + * When used with the lttng_create_session_ext() function, a session descriptor + * allows the creation of a tracing session of the following types: regular, + * snapshot, and live. + * + * Certain parameters can be omitted at the time of creation of a session + * descriptor to use default or auto-generated values selected by the + * session daemon. For instance, a session's name can be left unspecified, + * in which case one that is guaranteed not to clash with pre-existing + * sessions will be automatically be generated by the session daemon. + * + * Most session descriptors can be created in either "no output", local, or + * network output modes. The various output modes supported vary by session + * type. + * + * Regular session creation functions and output modes: + * * "no output": lttng_session_descriptor_create() + * * local: lttng_session_descriptor_local_create() + * * network: lttng_session_descriptor_network_create() + * + * Snapshot session creation functions and output modes: + * * "no output": lttng_session_descriptor_snapshot_create() + * * local: lttng_session_descriptor_snapshot_local_create() + * * network: lttng_session_descriptor_snapshot_network_create() + * + * Live session creation functions and output modes: + * * "no output": lttng_session_descriptor_live_create() + * * network: lttng_session_descriptor_live_network_create() + * + * Local output functions accept a 'path' parameter that must be an absolute + * path to which the user has write access. When a local output is automatically + * generated, it adopts the form: + * $LTTNG_HOME/DEFAULT_TRACE_DIR_NAME/SESSION_NAME-CREATION_TIME + * + * Where CREATION_TIME is time of the creation of the session on the session + * daemon in the form "yyyymmdd-hhmmss". + * + * Network output locations can also be auto-genated by leaving the + * 'control_url' and 'data_url' output parameters unspecified. In such cases, + * the session daemon will create a default output targeting a relay daemon + * at net://127.0.0.1, using the default 'control' and 'data' ports. + * + * The format of the 'control_url' and 'data_url' paramaters is: + * NETPROTO://(HOST | IPADDR)[:CTRLPORT[:DATAPORT]][/TRACEPATH] + * + * NETPROTO: Network protocol, amongst: + * * net: TCP over IPv4; the default values of 'CTRLPORT' and 'DATAPORT' + * are defined at build time of the lttng toolchain. + * * net6: TCP over IPv6: same default ports as the 'net' protocol. + * * tcp: Same as the 'net' protocol. + * * tcp6: Same as the 'net6' protocol. + * + * HOST | IPADDR: Hostname or IP address (IPv6 address *must* be enclosed + * in brackets; see RFC 2732). + * + * CTRLPORT: Control port. + * + * DATAPORT: Data port. + * + * TRACEPATH: Path of trace files on the remote file system. This path is + * relative to the base output directory set on the relay daemon + * end. + * + * The 'data_url' parameter is optional: + * * This parameter is meaningless for local tracing. + * * If 'control_url' is specified and a network protocol is used, the + * default data port, and the 'control_url' host will be used. + */ + +enum lttng_session_descriptor_status { + /* Invalid session descriptor parameter. */ + LTTNG_SESSION_DESCRIPTOR_STATUS_INVALID = -1, + LTTNG_SESSION_DESCRIPTOR_STATUS_OK = 0, + /* Session descriptor parameter is unset. */ + LTTNG_SESSION_DESCRIPTOR_STATUS_UNSET = 1, +}; + +/* + * Create a session descriptor in no-output mode. + * + * The 'name' parameter can be left NULL to auto-generate a session name. + * + * Returns an lttng_session_descriptor instance on success, NULL on error. + */ +extern struct lttng_session_descriptor * +lttng_session_descriptor_create(const char *name); + +/* + * Create a session descriptor with a local output destination. + * + * The 'name' parameter can be left NULL to auto-generate a session name. + * + * The 'path' must either be an absolute path or it can be left NULL to + * use the default local outout destination. + * + * Returns an lttng_session_descriptor instance on success, NULL on error. + */ +extern struct lttng_session_descriptor * +lttng_session_descriptor_local_create(const char *name, const char *path); + +/* + * Create a session descriptor with a remote output destination. + * + * The 'name' parameter can be left NULL to auto-generate a session name. + * + * The 'control_url' and 'data_url' must conform to the URL format + * described above or can be left NULL to use the default network output. + * + * Returns an lttng_session_descriptor instance on success, NULL on error. + */ +extern struct lttng_session_descriptor * +lttng_session_descriptor_network_create(const char *name, + const char *control_url, const char *data_url); + +/* + * Create a snapshot session descriptor without a default output. + * + * The 'name' parameter can be left NULL to auto-generate a session name. + * + * Returns an lttng_session_descriptor instance on success, NULL on error. + */ +extern struct lttng_session_descriptor * +lttng_session_descriptor_snapshot_create(const char *name); + +/* + * Create a snapshot session descriptor with a local output destination. + * + * The 'name' parameter can be left NULL to auto-generate a session name. + * + * The 'path' must either be an absolute path or it can be left NULL to + * use the default local output destination as the default snapshot output. + * + * Returns an lttng_session_descriptor instance on success, NULL on error. + */ +extern struct lttng_session_descriptor * +lttng_session_descriptor_snapshot_local_create(const char *name, + const char *path); + +/* + * Create a snapshot session descriptor with a remote output destination. + * + * The 'name' parameter can be left NULL to auto-generate a session name. + * + * The 'control_url' and 'data_url' must conform to the URL format + * described above or can be left NULL to use the default network output as + * the default snapshot output. + * + * Returns an lttng_session_descriptor instance on success, NULL on error. + */ +extern struct lttng_session_descriptor * +lttng_session_descriptor_snapshot_network_create(const char *name, + const char *control_url, const char *data_url); + +/* + * Create a live session descriptor without an output. + * + * The 'name' parameter can be left NULL to auto-generate a session name. + * + * The 'live_timer_interval_us' parameter is the live timer's period, specified + * in microseconds. + * + * This parameter can't be 0. There is no default value defined for a live + * timer's period. + * + * Returns an lttng_session_descriptor instance on success, NULL on error. + */ +extern struct lttng_session_descriptor * +lttng_session_descriptor_live_create( + const char *name, unsigned long long live_timer_interval_us); + +/* + * Create a live session descriptor with a remote output destination. + * + * The 'name' parameter can be left NULL to auto-generate a session name. + * + * The 'control_url' and 'data_url' must conform to the URL format + * described above or can be left NULL to use the default network output. + * + * The 'live_timer_interval_us' parameter is the live timer's period, specified + * in microseconds. + * + * This parameter can't be 0. There is no default value defined for a live + * timer's period. + * + * Returns an lttng_session_descriptor instance on success, NULL on error. + */ +extern struct lttng_session_descriptor * +lttng_session_descriptor_live_network_create( + const char *name, + const char *control_url, const char *data_url, + unsigned long long live_timer_interval_us); + +/* + * Get a session descriptor's session name. + * + * The 'name' parameter is used as an output parameter and will point to + * the session descriptor's session name on success + * (LTTNG_SESSION_DESCRIPTOR_STATUS_OK). Its content of is left unspecified + * for other return codes. The pointer returned through 'name' is only + * guaranteed to remain valid until the next method call on the session + * descriptor. + * + * Returns LTTNG_SESSION_DESCRIPTOR_STATUS_OK on success, + * LTTNG_SESSION_DESCRIPTOR_STATUS_INVALID if 'descriptor' or 'name' are + * NULL, and LTTNG_SESSION_DESCRIPTOR_STATUS_UNSET if the descriptor's + * name parameter is unset. + */ +extern enum lttng_session_descriptor_status +lttng_session_descriptor_get_session_name( + const struct lttng_session_descriptor *descriptor, + const char **name); + +/* + * Destroy a local lttng_session object. + * + * This does not destroy the session on the session daemon; it releases + * the resources allocated by the descriptor object. + */ +extern void lttng_session_descriptor_destroy( + struct lttng_session_descriptor *descriptor); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_SESSION_DESCRIPTOR_H */ diff --git a/include/lttng/session-internal.h b/include/lttng/session-internal.h new file mode 100644 index 000000000..765f3ce5c --- /dev/null +++ b/include/lttng/session-internal.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2019 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_SESSION_INTERNAL_H +#define LTTNG_SESSION_INTERNAL_H + +#include +#include + +struct lttng_session_extended { + struct { + uint64_t value; + uint8_t is_set; + } creation_time LTTNG_PACKED; +} LTTNG_PACKED; + +#endif /* LTTNG_SESSION_INTERNAL_H */ diff --git a/include/lttng/session.h b/include/lttng/session.h index e626b0081..6c54d7e4f 100644 --- a/include/lttng/session.h +++ b/include/lttng/session.h @@ -22,6 +22,8 @@ extern "C" { #endif +struct lttng_session_descriptor; + /* * Basic session information. * @@ -47,9 +49,29 @@ struct lttng_session { uint32_t snapshot_mode; unsigned int live_timer_interval; /* usec */ - char padding[LTTNG_SESSION_PADDING1]; + union { + char padding[LTTNG_SESSION_PADDING1]; + void *ptr; + } extended; }; +/* + * Create a session on the session daemon from a session descriptor. + * + * See the session descriptor API description in session-descriptor.h + * + * Note that unspecified session descriptor parameters, such as a session's + * name, are updated in the session descriptor if the creation of the session + * succeeds. This allows users to query the session's auto-generated name + * after its creation. Note that other attributes can be queried using the + * session listing API. + * + * Returns LTTNG_OK on success. See lttng-error.h for the meaning of the other + * return codes. + */ +extern enum lttng_error_code lttng_create_session_ext( + struct lttng_session_descriptor *session_descriptor); + /* * Create a tracing session using a name and an optional URL. * @@ -119,11 +141,28 @@ extern int lttng_destroy_session_no_wait(const char *name); /* * List all the tracing sessions. * - * Return the size (number of entries) of the "lttng_session" array. Caller - * must free sessions. On error, a negative LTTng error code is returned. + * Return the number of entries of the "lttng_session" array. The caller + * must free the returned sessions array directly using free(). + * + * On error, a negative LTTng error code is returned. */ extern int lttng_list_sessions(struct lttng_session **sessions); +/* + * Get the creation time of an lttng_session object on the session daemon. + * + * This function must only be used with lttng_session objects returned + * by lttng_list_sessions() or lttng_session_create(). + * + * The creation time returned is a UNIX timestamp; the number of seconds since + * Epoch (1970-01-01 00:00:00 +0000 (UTC)). + * + * Returns LTTNG_OK on success. See lttng-error.h for the meaning of the other + * return codes. + */ +extern enum lttng_error_code lttng_session_get_creation_time( + const struct lttng_session *session, uint64_t *creation_time); + /* * Set the shared memory path for a session. * diff --git a/src/bin/lttng-sessiond/client.c b/src/bin/lttng-sessiond/client.c index fb50b3cc6..f3002f910 100644 --- a/src/bin/lttng-sessiond/client.c +++ b/src/bin/lttng-sessiond/client.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include "client.h" #include "lttng-sessiond.h" @@ -389,10 +391,13 @@ static int copy_session_consumer(int domain, struct ltt_session *session) } /* Append correct directory to subdir */ - strncat(consumer->subdir, dir_name, - sizeof(consumer->subdir) - strlen(consumer->subdir) - 1); - DBG3("Copy session consumer subdir %s", consumer->subdir); - + ret = lttng_strncpy(consumer->domain_subdir, dir_name, + sizeof(consumer->domain_subdir)); + if (ret) { + ret = LTTNG_ERR_UNK; + goto error; + } + DBG3("Copy session consumer subdir %s", consumer->domain_subdir); ret = LTTNG_OK; error: @@ -718,9 +723,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, *sock_error = 0; switch (cmd_ctx->lsm->cmd_type) { - case LTTNG_CREATE_SESSION: - case LTTNG_CREATE_SESSION_SNAPSHOT: - case LTTNG_CREATE_SESSION_LIVE: + case LTTNG_CREATE_SESSION_EXT: case LTTNG_DESTROY_SESSION: case LTTNG_LIST_SESSIONS: case LTTNG_LIST_DOMAINS: @@ -798,9 +801,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, /* Commands that DO NOT need a session. */ switch (cmd_ctx->lsm->cmd_type) { - case LTTNG_CREATE_SESSION: - case LTTNG_CREATE_SESSION_SNAPSHOT: - case LTTNG_CREATE_SESSION_LIVE: + case LTTNG_CREATE_SESSION_EXT: case LTTNG_LIST_SESSIONS: case LTTNG_LIST_TRACEPOINTS: case LTTNG_LIST_SYSCALLS: @@ -1517,47 +1518,6 @@ error_add_context: ret = cmd_stop_trace(cmd_ctx->session); break; } - case LTTNG_CREATE_SESSION: - { - size_t nb_uri, len; - struct lttng_uri *uris = NULL; - - nb_uri = cmd_ctx->lsm->u.uri.size; - len = nb_uri * sizeof(struct lttng_uri); - - if (nb_uri > 0) { - uris = zmalloc(len); - if (uris == NULL) { - ret = LTTNG_ERR_FATAL; - goto error; - } - - /* Receive variable len data */ - DBG("Waiting for %zu URIs from client ...", nb_uri); - ret = lttcomm_recv_unix_sock(sock, uris, len); - if (ret <= 0) { - DBG("No URIs received from client... continuing"); - *sock_error = 1; - ret = LTTNG_ERR_SESSION_FAIL; - free(uris); - goto error; - } - - if (nb_uri == 1 && uris[0].dtype != LTTNG_DST_PATH) { - DBG("Creating session with ONE network URI is a bad call"); - ret = LTTNG_ERR_SESSION_FAIL; - free(uris); - goto error; - } - } - - ret = cmd_create_session_uri(cmd_ctx->lsm->session.name, uris, nb_uri, - &cmd_ctx->creds, 0); - - free(uris); - - break; - } case LTTNG_DESTROY_SESSION: { ret = cmd_destroy_session(cmd_ctx->session, @@ -1652,7 +1612,9 @@ error_add_context: nr_sessions = lttng_sessions_count( LTTNG_SOCK_GET_UID_CRED(&cmd_ctx->creds), LTTNG_SOCK_GET_GID_CRED(&cmd_ctx->creds)); - payload_len = sizeof(struct lttng_session) * nr_sessions; + + payload_len = (sizeof(struct lttng_session) * nr_sessions) + + (sizeof(struct lttng_session_extended) * nr_sessions); sessions_payload = zmalloc(payload_len); if (!sessions_payload) { @@ -1661,7 +1623,7 @@ error_add_context: goto setup_error; } - cmd_list_lttng_sessions(sessions_payload, + cmd_list_lttng_sessions(sessions_payload, nr_sessions, LTTNG_SOCK_GET_UID_CRED(&cmd_ctx->creds), LTTNG_SOCK_GET_GID_CRED(&cmd_ctx->creds)); session_unlock_list(); @@ -1792,82 +1754,35 @@ error_add_context: cmd_ctx->lsm->u.snapshot_record.wait); break; } - case LTTNG_CREATE_SESSION_SNAPSHOT: + case LTTNG_CREATE_SESSION_EXT: { - size_t nb_uri, len; - struct lttng_uri *uris = NULL; + struct lttng_dynamic_buffer payload; + struct lttng_session_descriptor *return_descriptor = NULL; - nb_uri = cmd_ctx->lsm->u.uri.size; - len = nb_uri * sizeof(struct lttng_uri); - - if (nb_uri > 0) { - uris = zmalloc(len); - if (uris == NULL) { - ret = LTTNG_ERR_FATAL; - goto error; - } - - /* Receive variable len data */ - DBG("Waiting for %zu URIs from client ...", nb_uri); - ret = lttcomm_recv_unix_sock(sock, uris, len); - if (ret <= 0) { - DBG("No URIs received from client... continuing"); - *sock_error = 1; - ret = LTTNG_ERR_SESSION_FAIL; - free(uris); - goto error; - } - - if (nb_uri == 1 && uris[0].dtype != LTTNG_DST_PATH) { - DBG("Creating session with ONE network URI is a bad call"); - ret = LTTNG_ERR_SESSION_FAIL; - free(uris); - goto error; - } + lttng_dynamic_buffer_init(&payload); + ret = cmd_create_session(cmd_ctx, sock, &return_descriptor); + if (ret != LTTNG_OK) { + goto error; } - ret = cmd_create_session_snapshot(cmd_ctx->lsm->session.name, uris, - nb_uri, &cmd_ctx->creds); - free(uris); - break; - } - case LTTNG_CREATE_SESSION_LIVE: - { - size_t nb_uri, len; - struct lttng_uri *uris = NULL; - - nb_uri = cmd_ctx->lsm->u.uri.size; - len = nb_uri * sizeof(struct lttng_uri); - - if (nb_uri > 0) { - uris = zmalloc(len); - if (uris == NULL) { - ret = LTTNG_ERR_FATAL; - goto error; - } - - /* Receive variable len data */ - DBG("Waiting for %zu URIs from client ...", nb_uri); - ret = lttcomm_recv_unix_sock(sock, uris, len); - if (ret <= 0) { - DBG("No URIs received from client... continuing"); - *sock_error = 1; - ret = LTTNG_ERR_SESSION_FAIL; - free(uris); - goto error; - } - - if (nb_uri == 1 && uris[0].dtype != LTTNG_DST_PATH) { - DBG("Creating session with ONE network URI is a bad call"); - ret = LTTNG_ERR_SESSION_FAIL; - free(uris); - goto error; - } + ret = lttng_session_descriptor_serialize(return_descriptor, + &payload); + if (ret) { + ERR("Failed to serialize session descriptor in reply to \"create session\" command"); + lttng_session_descriptor_destroy(return_descriptor); + ret = LTTNG_ERR_NOMEM; + goto error; } - - ret = cmd_create_session_uri(cmd_ctx->lsm->session.name, uris, - nb_uri, &cmd_ctx->creds, cmd_ctx->lsm->u.session_live.timer_interval); - free(uris); + ret = setup_lttng_msg_no_cmd_header(cmd_ctx, payload.data, + payload.size); + if (ret) { + lttng_session_descriptor_destroy(return_descriptor); + ret = LTTNG_ERR_NOMEM; + goto error; + } + lttng_dynamic_buffer_reset(&payload); + lttng_session_descriptor_destroy(return_descriptor); + ret = LTTNG_OK; break; } case LTTNG_SAVE_SESSION: diff --git a/src/bin/lttng-sessiond/cmd.c b/src/bin/lttng-sessiond/cmd.c index 9cbadc6dd..01efcaf05 100644 --- a/src/bin/lttng-sessiond/cmd.c +++ b/src/bin/lttng-sessiond/cmd.c @@ -39,7 +39,9 @@ #include #include #include +#include #include +#include #include #include "channel.h" @@ -816,34 +818,47 @@ error: * Add URI so the consumer output object. Set the correct path depending on the * domain adding the default trace directory. */ -static int add_uri_to_consumer(struct consumer_output *consumer, - struct lttng_uri *uri, enum lttng_domain_type domain, - const char *session_name) +static enum lttng_error_code add_uri_to_consumer( + const struct ltt_session *session, + struct consumer_output *consumer, + struct lttng_uri *uri, enum lttng_domain_type domain) { - int ret = LTTNG_OK; - const char *default_trace_dir; + int ret; + enum lttng_error_code ret_code = LTTNG_OK; assert(uri); if (consumer == NULL) { DBG("No consumer detected. Don't add URI. Stopping."); - ret = LTTNG_ERR_NO_CONSUMER; + ret_code = LTTNG_ERR_NO_CONSUMER; goto error; } switch (domain) { case LTTNG_DOMAIN_KERNEL: - default_trace_dir = DEFAULT_KERNEL_TRACE_DIR; + ret = lttng_strncpy(consumer->domain_subdir, + DEFAULT_KERNEL_TRACE_DIR, + sizeof(consumer->domain_subdir)); break; case LTTNG_DOMAIN_UST: - default_trace_dir = DEFAULT_UST_TRACE_DIR; + ret = lttng_strncpy(consumer->domain_subdir, + DEFAULT_UST_TRACE_DIR, + sizeof(consumer->domain_subdir)); break; default: /* - * This case is possible is we try to add the URI to the global tracing - * session consumer object which in this case there is no subdir. + * This case is possible is we try to add the URI to the global + * tracing session consumer object which in this case there is + * no subdir. */ - default_trace_dir = ""; + memset(consumer->domain_subdir, 0, + sizeof(consumer->domain_subdir)); + ret = 0; + } + if (ret) { + ERR("Failed to initialize consumer output domain subdirectory"); + ret_code = LTTNG_ERR_FATAL; + goto error; } switch (uri->dtype) { @@ -856,67 +871,46 @@ static int add_uri_to_consumer(struct consumer_output *consumer, consumer->dst.net.control_isset) || (uri->stype == LTTNG_STREAM_DATA && consumer->dst.net.data_isset)) { - ret = LTTNG_ERR_URL_EXIST; + ret_code = LTTNG_ERR_URL_EXIST; goto error; } } else { - memset(&consumer->dst.net, 0, sizeof(consumer->dst.net)); + memset(&consumer->dst, 0, sizeof(consumer->dst)); } - consumer->type = CONSUMER_DST_NET; - /* Set URI into consumer output object */ - ret = consumer_set_network_uri(consumer, uri); + ret = consumer_set_network_uri(session, consumer, uri); if (ret < 0) { - ret = -ret; + ret_code = -ret; goto error; } else if (ret == 1) { /* * URI was the same in the consumer so we do not append the subdir * again so to not duplicate output dir. */ - ret = LTTNG_OK; + ret_code = LTTNG_OK; goto error; } - - if (uri->stype == LTTNG_STREAM_CONTROL && strlen(uri->subdir) == 0) { - ret = consumer_set_subdir(consumer, session_name); - if (ret < 0) { - ret = LTTNG_ERR_FATAL; - goto error; - } - } - - if (uri->stype == LTTNG_STREAM_CONTROL) { - /* On a new subdir, reappend the default trace dir. */ - strncat(consumer->subdir, default_trace_dir, - sizeof(consumer->subdir) - strlen(consumer->subdir) - 1); - DBG3("Append domain trace name to subdir %s", consumer->subdir); - } - break; case LTTNG_DST_PATH: - DBG2("Setting trace directory path from URI to %s", uri->dst.path); - memset(consumer->dst.session_root_path, 0, - sizeof(consumer->dst.session_root_path)); - /* Explicit length checks for strcpy and strcat. */ - if (strlen(uri->dst.path) + strlen(default_trace_dir) - >= sizeof(consumer->dst.session_root_path)) { - ret = LTTNG_ERR_FATAL; + if (*uri->dst.path != '/' || strstr(uri->dst.path, "../")) { + ret_code = LTTNG_ERR_INVALID; goto error; } - strcpy(consumer->dst.session_root_path, uri->dst.path); - /* Append default trace dir */ - strcat(consumer->dst.session_root_path, default_trace_dir); - /* Flag consumer as local. */ + DBG2("Setting trace directory path from URI to %s", + uri->dst.path); + memset(&consumer->dst, 0, sizeof(consumer->dst)); + + ret = lttng_strncpy(consumer->dst.session_root_path, + uri->dst.path, + sizeof(consumer->dst.session_root_path)); consumer->type = CONSUMER_DST_LOCAL; break; } - ret = LTTNG_OK; - + ret_code = LTTNG_OK; error: - return ret; + return ret_code; } /* @@ -2521,7 +2515,7 @@ int domain_mkdir(const struct consumer_output *output, struct consumer_socket *socket; struct lttng_ht_iter iter; int ret; - char *path = NULL; + char path[LTTNG_PATH_MAX]; if (!output || !output->socks) { ERR("No consumer output found"); @@ -2529,18 +2523,12 @@ int domain_mkdir(const struct consumer_output *output, goto end; } - path = zmalloc(LTTNG_PATH_MAX * sizeof(char)); - if (!path) { - ERR("Cannot allocate mkdir path"); - ret = -1; - goto end; - } - - ret = snprintf(path, LTTNG_PATH_MAX, "%s%s%s", + ret = snprintf(path, sizeof(path), "%s/%s%s", session_get_base_path(session), - output->chunk_path, output->subdir); + output->chunk_path, + output->domain_subdir); if (ret < 0 || ret >= LTTNG_PATH_MAX) { - ERR("Format path"); + ERR("Failed to format path new chunk domain path"); ret = -1; goto end; } @@ -2556,7 +2544,7 @@ int domain_mkdir(const struct consumer_output *output, ret = consumer_mkdir(socket, session->id, output, path, uid, gid); pthread_mutex_unlock(socket->lock); if (ret) { - ERR("Consumer mkdir"); + ERR("Failed to create directory at \"%s\"", path); ret = -1; goto end_unlock; } @@ -2568,7 +2556,6 @@ int domain_mkdir(const struct consumer_output *output, end_unlock: rcu_read_unlock(); end: - free(path); return ret; } @@ -2820,8 +2807,9 @@ int cmd_set_consumer_uri(struct ltt_session *session, size_t nb_uri, /* Set the "global" consumer URIs */ for (i = 0; i < nb_uri; i++) { - ret = add_uri_to_consumer(session->consumer, - &uris[i], 0, session->name); + ret = add_uri_to_consumer(session, + session->consumer, + &uris[i], LTTNG_DOMAIN_NONE); if (ret != LTTNG_OK) { goto error; } @@ -2830,10 +2818,9 @@ int cmd_set_consumer_uri(struct ltt_session *session, size_t nb_uri, /* Set UST session URIs */ if (session->ust_session) { for (i = 0; i < nb_uri; i++) { - ret = add_uri_to_consumer( + ret = add_uri_to_consumer(session, session->ust_session->consumer, - &uris[i], LTTNG_DOMAIN_UST, - session->name); + &uris[i], LTTNG_DOMAIN_UST); if (ret != LTTNG_OK) { goto error; } @@ -2843,10 +2830,9 @@ int cmd_set_consumer_uri(struct ltt_session *session, size_t nb_uri, /* Set kernel session URIs */ if (session->kernel_session) { for (i = 0; i < nb_uri; i++) { - ret = add_uri_to_consumer( + ret = add_uri_to_consumer(session, session->kernel_session->consumer, - &uris[i], LTTNG_DOMAIN_KERNEL, - session->name); + &uris[i], LTTNG_DOMAIN_KERNEL); if (ret != LTTNG_OK) { goto error; } @@ -2873,142 +2859,247 @@ error: return ret; } -/* - * Command LTTNG_CREATE_SESSION processed by the client thread. - */ -int cmd_create_session_uri(char *name, struct lttng_uri *uris, - size_t nb_uri, lttng_sock_cred *creds, unsigned int live_timer) +static +enum lttng_error_code set_session_output_from_descriptor( + struct ltt_session *session, + const struct lttng_session_descriptor *descriptor) { int ret; - struct ltt_session *session = NULL; - - assert(name); - assert(creds); - - /* Check if the session already exists. */ - session_lock_list(); - session = session_find_by_name(name); - session_unlock_list(); - if (session != NULL) { - ret = LTTNG_ERR_EXIST_SESS; + enum lttng_error_code ret_code = LTTNG_OK; + enum lttng_session_descriptor_type session_type = + lttng_session_descriptor_get_type(descriptor); + enum lttng_session_descriptor_output_type output_type = + lttng_session_descriptor_get_output_type(descriptor); + struct lttng_uri uris[2] = {}; + size_t uri_count = 0; + + switch (output_type) { + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE: + goto end; + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL: + lttng_session_descriptor_get_local_output_uri(descriptor, + &uris[0]); + uri_count = 1; + break; + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK: + lttng_session_descriptor_get_network_output_uris(descriptor, + &uris[0], &uris[1]); + uri_count = 2; + break; + default: + ret_code = LTTNG_ERR_INVALID; goto end; } - /* Create tracing session in the registry */ - ret = session_create(name, LTTNG_SOCK_GET_UID_CRED(creds), - LTTNG_SOCK_GET_GID_CRED(creds)); - if (ret != LTTNG_OK) { + switch (session_type) { + case LTTNG_SESSION_DESCRIPTOR_TYPE_SNAPSHOT: + { + struct snapshot_output *new_output = NULL; + + new_output = snapshot_output_alloc(); + if (!new_output) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + ret = snapshot_output_init_with_uri(session, + DEFAULT_SNAPSHOT_MAX_SIZE, + NULL, uris, uri_count, session->consumer, + new_output, &session->snapshot); + if (ret < 0) { + ret_code = (ret == -ENOMEM) ? + LTTNG_ERR_NOMEM : LTTNG_ERR_INVALID; + snapshot_output_destroy(new_output); + goto end; + } + snapshot_add_output(&session->snapshot, new_output); + break; + } + case LTTNG_SESSION_DESCRIPTOR_TYPE_REGULAR: + case LTTNG_SESSION_DESCRIPTOR_TYPE_LIVE: + { + ret_code = cmd_set_consumer_uri(session, uri_count, uris); + break; + } + default: + ret_code = LTTNG_ERR_INVALID; goto end; } +end: + return ret_code; +} + +static +enum lttng_error_code cmd_create_session_from_descriptor( + struct lttng_session_descriptor *descriptor, + const lttng_sock_cred *creds, + const char *home_path) +{ + int ret; + enum lttng_error_code ret_code; + const char *session_name; + struct ltt_session *new_session = NULL; + enum lttng_session_descriptor_status descriptor_status; - /* Get the newly created session pointer back. */ session_lock_list(); - session = session_find_by_name(name); - session_unlock_list(); - assert(session); + if (home_path) { + if (*home_path != '/') { + ERR("Home path provided by client is not absolute"); + ret_code = LTTNG_ERR_INVALID; + goto end; + } + } - session->live_timer = live_timer; - /* Create default consumer output for the session not yet created. */ - session->consumer = consumer_create_output(CONSUMER_DST_LOCAL); - if (session->consumer == NULL) { - ret = LTTNG_ERR_FATAL; + descriptor_status = lttng_session_descriptor_get_session_name( + descriptor, &session_name); + switch (descriptor_status) { + case LTTNG_SESSION_DESCRIPTOR_STATUS_OK: + break; + case LTTNG_SESSION_DESCRIPTOR_STATUS_UNSET: + session_name = NULL; + break; + default: + ret_code = LTTNG_ERR_INVALID; + goto end; + } + ret_code = session_create(session_name, creds->uid, creds->gid, + &new_session); + if (ret_code != LTTNG_OK) { goto end; } - if (uris) { - ret = cmd_set_consumer_uri(session, nb_uri, uris); - if (ret != LTTNG_OK) { + if (!session_name) { + ret = lttng_session_descriptor_set_session_name(descriptor, + new_session->name); + if (ret) { + ret_code = LTTNG_ERR_SESSION_FAIL; + goto end; + } + } + + if (!lttng_session_descriptor_is_output_destination_initialized( + descriptor)) { + /* + * Only include the session's creation time in the output + * destination if the name of the session itself was + * not auto-generated. + */ + ret_code = lttng_session_descriptor_set_default_output( + descriptor, + session_name ? &new_session->creation_time : NULL, + home_path); + if (ret_code != LTTNG_OK) { goto end; } - session->output_traces = 1; } else { - session->output_traces = 0; - DBG2("Session %s created with no output", session->name); + new_session->has_user_specified_directory = + lttng_session_descriptor_has_output_directory( + descriptor); } - session->consumer->enabled = 1; + switch (lttng_session_descriptor_get_type(descriptor)) { + case LTTNG_SESSION_DESCRIPTOR_TYPE_SNAPSHOT: + new_session->snapshot_mode = 1; + break; + case LTTNG_SESSION_DESCRIPTOR_TYPE_LIVE: + new_session->live_timer = + lttng_session_descriptor_live_get_timer_interval( + descriptor); + break; + default: + break; + } - ret = LTTNG_OK; + ret_code = set_session_output_from_descriptor(new_session, descriptor); + if (ret_code != LTTNG_OK) { + goto end; + } + new_session->consumer->enabled = 1; + ret_code = LTTNG_OK; end: - if (session) { - session_lock_list(); - session_put(session); - session_unlock_list(); + /* Release reference provided by the session_create function. */ + session_put(new_session); + if (ret_code != LTTNG_OK && new_session) { + /* Release the global reference on error. */ + session_destroy(new_session); } - return ret; + session_unlock_list(); + return ret_code; } -/* - * Command LTTNG_CREATE_SESSION_SNAPSHOT processed by the client thread. - */ -int cmd_create_session_snapshot(char *name, struct lttng_uri *uris, - size_t nb_uri, lttng_sock_cred *creds) +enum lttng_error_code cmd_create_session(struct command_ctx *cmd_ctx, int sock, + struct lttng_session_descriptor **return_descriptor) { int ret; - struct ltt_session *session = NULL; - struct snapshot_output *new_output = NULL; - - assert(name); - assert(creds); - - /* - * Create session in no output mode with URIs set to NULL. The uris we've - * received are for a default snapshot output if one. - */ - ret = cmd_create_session_uri(name, NULL, 0, creds, 0); - if (ret != LTTNG_OK) { - goto end; + size_t payload_size; + struct lttng_dynamic_buffer payload; + struct lttng_buffer_view home_dir_view; + struct lttng_buffer_view session_descriptor_view; + struct lttng_session_descriptor *session_descriptor = NULL; + enum lttng_error_code ret_code; + + lttng_dynamic_buffer_init(&payload); + if (cmd_ctx->lsm->u.create_session.home_dir_size >= + LTTNG_PATH_MAX) { + ret_code = LTTNG_ERR_INVALID; + goto error; } - - /* Get the newly created session pointer back. This should NEVER fail. */ - session_lock_list(); - session = session_find_by_name(name); - session_unlock_list(); - assert(session); - - /* Flag session for snapshot mode. */ - session->snapshot_mode = 1; - - /* Skip snapshot output creation if no URI is given. */ - if (nb_uri == 0) { - /* Not an error. */ - goto end; + if (cmd_ctx->lsm->u.create_session.session_descriptor_size > + LTTNG_SESSION_DESCRIPTOR_MAX_LEN) { + ret_code = LTTNG_ERR_INVALID; + goto error; } - new_output = snapshot_output_alloc(); - if (!new_output) { - ret = LTTNG_ERR_NOMEM; - goto error_snapshot_alloc; + payload_size = cmd_ctx->lsm->u.create_session.home_dir_size + + cmd_ctx->lsm->u.create_session.session_descriptor_size; + ret = lttng_dynamic_buffer_set_size(&payload, payload_size); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto error; } - ret = snapshot_output_init_with_uri(DEFAULT_SNAPSHOT_MAX_SIZE, NULL, - uris, nb_uri, session->consumer, new_output, &session->snapshot); - if (ret < 0) { - if (ret == -ENOMEM) { - ret = LTTNG_ERR_NOMEM; - } else { - ret = LTTNG_ERR_INVALID; - } - goto error_snapshot; + ret = lttcomm_recv_unix_sock(sock, payload.data, payload.size); + if (ret <= 0) { + ERR("Reception of session descriptor failed, aborting."); + ret_code = LTTNG_ERR_SESSION_FAIL; + goto error; } - rcu_read_lock(); - snapshot_add_output(&session->snapshot, new_output); - rcu_read_unlock(); + home_dir_view = lttng_buffer_view_from_dynamic_buffer( + &payload, + 0, + cmd_ctx->lsm->u.create_session.home_dir_size); + session_descriptor_view = lttng_buffer_view_from_dynamic_buffer( + &payload, + cmd_ctx->lsm->u.create_session.home_dir_size, + cmd_ctx->lsm->u.create_session.session_descriptor_size); - ret = LTTNG_OK; - goto end; + ret = lttng_session_descriptor_create_from_buffer( + &session_descriptor_view, &session_descriptor); + if (ret < 0) { + ERR("Failed to create session descriptor from payload of \"create session\" command"); + ret_code = LTTNG_ERR_INVALID; + goto error; + } -error_snapshot: - snapshot_output_destroy(new_output); -error_snapshot_alloc: -end: - if (session) { - session_lock_list(); - session_put(session); - session_unlock_list(); + /* + * Sets the descriptor's auto-generated properties (name, output) if + * needed. + */ + ret_code = cmd_create_session_from_descriptor(session_descriptor, + &cmd_ctx->creds, + home_dir_view.size ? home_dir_view.data : NULL); + if (ret_code != LTTNG_OK) { + goto error; } - return ret; + + ret_code = LTTNG_OK; + *return_descriptor = session_descriptor; + session_descriptor = NULL; +error: + lttng_dynamic_buffer_reset(&payload); + lttng_session_descriptor_destroy(session_descriptor); + return ret_code; } /* @@ -3412,13 +3503,15 @@ error: * The session list lock MUST be acquired before calling this function. Use * session_lock_list() and session_unlock_list(). */ -void cmd_list_lttng_sessions(struct lttng_session *sessions, uid_t uid, - gid_t gid) +void cmd_list_lttng_sessions(struct lttng_session *sessions, + size_t session_count, uid_t uid, gid_t gid) { int ret; unsigned int i = 0; struct ltt_session *session; struct ltt_session_list *list = session_get_list(); + struct lttng_session_extended *extended = + (typeof(extended)) (&sessions[session_count]); DBG("Getting all available session for UID %d GID %d", uid, gid); @@ -3462,6 +3555,8 @@ void cmd_list_lttng_sessions(struct lttng_session *sessions, uid_t uid, sessions[i].enabled = session->active; sessions[i].snapshot_mode = session->snapshot_mode; sessions[i].live_timer_interval = session->live_timer; + extended[i].creation_time.value = (uint64_t) session->creation_time; + extended[i].creation_time.is_set = 1; i++; session_put(session); } @@ -3573,7 +3668,7 @@ int cmd_snapshot_add_output(struct ltt_session *session, goto error; } - ret = snapshot_output_init(output->max_size, output->name, + ret = snapshot_output_init(session, output->max_size, output->name, output->ctrl_url, output->data_url, session->consumer, new_output, &session->snapshot); if (ret < 0) { @@ -4336,8 +4431,10 @@ int cmd_snapshot_record(struct ltt_session *session, /* Use temporary output for the session. */ if (*output->ctrl_url != '\0') { - ret = snapshot_output_init(output->max_size, output->name, - output->ctrl_url, output->data_url, session->consumer, + ret = snapshot_output_init(session, output->max_size, + output->name, + output->ctrl_url, output->data_url, + session->consumer, &tmp_output, NULL); if (ret < 0) { if (ret == -ENOMEM) { @@ -4611,6 +4708,32 @@ int cmd_rotate_session(struct ltt_session *session, goto end; } + /* Current chunk directory, ex: 20170922-111754-42 */ + ret = snprintf(session->consumer->chunk_path, + sizeof(session->consumer->chunk_path), + "%s-%" PRIu64, datetime, + session->current_archive_id + 1); + if (ret < 0 || ret >= sizeof(session->consumer->chunk_path)) { + ERR("Failed to format the new chunk's directory in rotate session command"); + cmd_ret = LTTNG_ERR_UNK; + goto error; + } + + /* + * The active path for the next rotation/destroy. + * Ex: ~/lttng-traces/auto-20170922-111748/20170922-111754-42 + */ + ret = snprintf(session->rotation_chunk.active_tracing_path, + sizeof(session->rotation_chunk.active_tracing_path), + "%s/%s", + session_get_base_path(session), + session->consumer->chunk_path); + if (ret < 0 || ret >= sizeof(session->rotation_chunk.active_tracing_path)) { + ERR("Failed to format active tracing path in rotate session command"); + cmd_ret = LTTNG_ERR_UNK; + goto error; + } + /* * A rotation has a local step even if the destination is a relay * daemon; the buffers must be consumed by the consumer daemon. @@ -4621,43 +4744,23 @@ int cmd_rotate_session(struct ltt_session *session, session->rotation_state = LTTNG_ROTATION_STATE_ONGOING; if (session->kernel_session) { - /* - * The active path for the next rotation/destroy. - * Ex: ~/lttng-traces/auto-20170922-111748/20170922-111754-42 - */ - ret = snprintf(session->rotation_chunk.active_tracing_path, - sizeof(session->rotation_chunk.active_tracing_path), - "%s/%s-%" PRIu64, - session_get_base_path(session), - datetime, session->current_archive_id + 1); - if (ret < 0 || ret == sizeof(session->rotation_chunk.active_tracing_path)) { - ERR("Failed to format active kernel tracing path in rotate session command"); - cmd_ret = LTTNG_ERR_UNK; - goto error; - } - /* - * The sub-directory for the consumer - * Ex: /20170922-111754-42/kernel - */ - ret = snprintf(session->kernel_session->consumer->chunk_path, - sizeof(session->kernel_session->consumer->chunk_path), - "/%s-%" PRIu64, datetime, - session->current_archive_id + 1); - if (ret < 0 || ret == sizeof(session->kernel_session->consumer->chunk_path)) { - ERR("Failed to format the kernel consumer's sub-directory in rotate session command"); + ret = lttng_strncpy( + session->kernel_session->consumer->chunk_path, + session->consumer->chunk_path, + sizeof(session->kernel_session->consumer->chunk_path)); + if (ret) { + ERR("Failed to copy current chunk directory to kernel session"); cmd_ret = LTTNG_ERR_UNK; goto error; } /* - * Create the new chunk folder, before the rotation begins so we don't - * race with the consumer/tracer activity. + * Create the new chunk folder, before the rotation begins so we + * don't race with the consumer/tracer activity. */ ret = domain_mkdir(session->kernel_session->consumer, session, session->kernel_session->uid, session->kernel_session->gid); if (ret) { - ERR("Failed to create kernel session tracing path at %s", - session->kernel_session->consumer->chunk_path); cmd_ret = LTTNG_ERR_CREATE_DIR_FAIL; goto error; } @@ -4667,27 +4770,15 @@ int cmd_rotate_session(struct ltt_session *session, } } if (session->ust_session) { - ret = snprintf(session->rotation_chunk.active_tracing_path, - PATH_MAX, "%s/%s-%" PRIu64, - session_get_base_path(session), - datetime, session->current_archive_id + 1); - if (ret < 0) { - ERR("Failed to format active UST tracing path in rotate session command"); - cmd_ret = LTTNG_ERR_UNK; - goto error; - } - ret = snprintf(session->ust_session->consumer->chunk_path, - PATH_MAX, "/%s-%" PRIu64, datetime, - session->current_archive_id + 1); - if (ret < 0) { - ERR("Failed to format the UST consumer's sub-directory in rotate session command"); + ret = lttng_strncpy( + session->ust_session->consumer->chunk_path, + session->consumer->chunk_path, + sizeof(session->ust_session->consumer->chunk_path)); + if (ret) { + ERR("Failed to copy current chunk directory to userspace session"); cmd_ret = LTTNG_ERR_UNK; goto error; } - /* - * Create the new chunk folder, before the rotation begins so we don't - * race with the consumer/tracer activity. - */ ret = domain_mkdir(session->ust_session->consumer, session, session->ust_session->uid, session->ust_session->gid); diff --git a/src/bin/lttng-sessiond/cmd.h b/src/bin/lttng-sessiond/cmd.h index a207e52b3..0821fb33d 100644 --- a/src/bin/lttng-sessiond/cmd.h +++ b/src/bin/lttng-sessiond/cmd.h @@ -44,10 +44,8 @@ struct cmd_completion_handler { void cmd_init(void); /* Session commands */ -int cmd_create_session_uri(char *name, struct lttng_uri *uris, - size_t nb_uri, lttng_sock_cred *creds, unsigned int live_timer); -int cmd_create_session_snapshot(char *name, struct lttng_uri *uris, - size_t nb_uri, lttng_sock_cred *creds); +enum lttng_error_code cmd_create_session(struct command_ctx *cmd_ctx, int sock, + struct lttng_session_descriptor **return_descriptor); int cmd_destroy_session(struct ltt_session *session, struct notification_thread_handle *notification_thread_handle); @@ -101,8 +99,8 @@ ssize_t cmd_list_channels(enum lttng_domain_type domain, struct ltt_session *session, struct lttng_channel **channels); ssize_t cmd_list_domains(struct ltt_session *session, struct lttng_domain **domains); -void cmd_list_lttng_sessions(struct lttng_session *sessions, uid_t uid, - gid_t gid); +void cmd_list_lttng_sessions(struct lttng_session *sessions, + size_t session_count, uid_t uid, gid_t gid); ssize_t cmd_list_tracepoint_fields(enum lttng_domain_type domain, struct lttng_event_field **fields); ssize_t cmd_list_tracepoints(enum lttng_domain_type domain, diff --git a/src/bin/lttng-sessiond/consumer.c b/src/bin/lttng-sessiond/consumer.c index 80e313778..12db236d0 100644 --- a/src/bin/lttng-sessiond/consumer.c +++ b/src/bin/lttng-sessiond/consumer.c @@ -549,25 +549,26 @@ void consumer_output_put(struct consumer_output *obj) * * Should *NOT* be called with RCU read-side lock held. */ -struct consumer_output *consumer_copy_output(struct consumer_output *obj) +struct consumer_output *consumer_copy_output(struct consumer_output *src) { int ret; struct consumer_output *output; - assert(obj); + assert(src); - output = consumer_create_output(obj->type); + output = consumer_create_output(src->type); if (output == NULL) { goto end; } - output->enabled = obj->enabled; - output->net_seq_index = obj->net_seq_index; - memcpy(output->subdir, obj->subdir, sizeof(output->subdir)); - output->snapshot = obj->snapshot; - output->relay_major_version = obj->relay_major_version; - output->relay_minor_version = obj->relay_minor_version; - memcpy(&output->dst, &obj->dst, sizeof(output->dst)); - ret = consumer_copy_sockets(output, obj); + output->enabled = src->enabled; + output->net_seq_index = src->net_seq_index; + memcpy(output->domain_subdir, src->domain_subdir, + sizeof(output->domain_subdir)); + output->snapshot = src->snapshot; + output->relay_major_version = src->relay_major_version; + output->relay_minor_version = src->relay_minor_version; + memcpy(&output->dst, &src->dst, sizeof(output->dst)); + ret = consumer_copy_sockets(output, src); if (ret < 0) { goto error_put; } @@ -626,33 +627,32 @@ error: } /* - * Set network URI to the consumer output object. + * Set network URI to the consumer output. * * Return 0 on success. Return 1 if the URI were equal. Else, negative value on * error. */ -int consumer_set_network_uri(struct consumer_output *obj, +int consumer_set_network_uri(const struct ltt_session *session, + struct consumer_output *output, struct lttng_uri *uri) { int ret; - char tmp_path[PATH_MAX]; - char hostname[HOST_NAME_MAX]; struct lttng_uri *dst_uri = NULL; /* Code flow error safety net. */ - assert(obj); + assert(output); assert(uri); switch (uri->stype) { case LTTNG_STREAM_CONTROL: - dst_uri = &obj->dst.net.control; - obj->dst.net.control_isset = 1; + dst_uri = &output->dst.net.control; + output->dst.net.control_isset = 1; if (uri->port == 0) { /* Assign default port. */ uri->port = DEFAULT_NETWORK_CONTROL_PORT; } else { - if (obj->dst.net.data_isset && uri->port == - obj->dst.net.data.port) { + if (output->dst.net.data_isset && uri->port == + output->dst.net.data.port) { ret = -LTTNG_ERR_INVALID; goto error; } @@ -660,14 +660,14 @@ int consumer_set_network_uri(struct consumer_output *obj, DBG3("Consumer control URI set with port %d", uri->port); break; case LTTNG_STREAM_DATA: - dst_uri = &obj->dst.net.data; - obj->dst.net.data_isset = 1; + dst_uri = &output->dst.net.data; + output->dst.net.data_isset = 1; if (uri->port == 0) { /* Assign default port. */ uri->port = DEFAULT_NETWORK_DATA_PORT; } else { - if (obj->dst.net.control_isset && uri->port == - obj->dst.net.control.port) { + if (output->dst.net.control_isset && uri->port == + output->dst.net.control.port) { ret = -LTTNG_ERR_INVALID; goto error; } @@ -688,42 +688,89 @@ int consumer_set_network_uri(struct consumer_output *obj, } /* URIs were not equal, replacing it. */ - memset(dst_uri, 0, sizeof(struct lttng_uri)); memcpy(dst_uri, uri, sizeof(struct lttng_uri)); - obj->type = CONSUMER_DST_NET; - - /* Handle subdir and add hostname in front. */ - if (dst_uri->stype == LTTNG_STREAM_CONTROL) { - /* Get hostname to append it in the pathname */ - ret = gethostname(hostname, sizeof(hostname)); - if (ret < 0) { - PERROR("gethostname. Fallback on default localhost"); - strncpy(hostname, "localhost", sizeof(hostname)); - } - hostname[sizeof(hostname) - 1] = '\0'; + output->type = CONSUMER_DST_NET; + if (dst_uri->stype != LTTNG_STREAM_CONTROL) { + /* Only the control uri needs to contain the path. */ + goto end; + } - /* Setup consumer subdir if none present in the control URI */ - if (strlen(dst_uri->subdir) == 0) { - ret = snprintf(tmp_path, sizeof(tmp_path), "%s/%s", - hostname, obj->subdir); - } else { - ret = snprintf(tmp_path, sizeof(tmp_path), "%s/%s", - hostname, dst_uri->subdir); - } - if (ret < 0) { - PERROR("snprintf set consumer uri subdir"); - ret = -LTTNG_ERR_NOMEM; + /* + * If the user has specified a subdir as part of the control + * URL, the session's base output directory is: + * /RELAYD_OUTPUT_PATH/HOSTNAME/USER_SPECIFIED_DIR + * + * Hence, the "base_dir" from which all stream files and + * session rotation chunks are created takes the form + * /HOSTNAME/USER_SPECIFIED_DIR + * + * If the user has not specified an output directory as part of + * the control URL, the base output directory has the form: + * /RELAYD_OUTPUT_PATH/HOSTNAME/SESSION_NAME-CREATION_TIME + * + * Hence, the "base_dir" from which all stream files and + * session rotation chunks are created takes the form + * /HOSTNAME/SESSION_NAME-CREATION_TIME + * + * Note that automatically generated session names already + * contain the session's creation time. In that case, the + * creation time is omitted to prevent it from being duplicated + * in the final directory hierarchy. + */ + if (*uri->subdir) { + if (strstr(uri->subdir, "../")) { + ERR("Network URI subdirs are not allowed to walk up the path hierarchy"); + ret = -LTTNG_ERR_INVALID; goto error; } + ret = snprintf(output->dst.net.base_dir, + sizeof(output->dst.net.base_dir), + "/%s/%s/", session->hostname, uri->subdir); + } else { + if (session->has_auto_generated_name) { + ret = snprintf(output->dst.net.base_dir, + sizeof(output->dst.net.base_dir), + "/%s/%s/", session->hostname, + session->name); + } else { + char session_creation_datetime[16]; + size_t strftime_ret; + struct tm *timeinfo; - if (lttng_strncpy(obj->dst.net.base_dir, tmp_path, - sizeof(obj->dst.net.base_dir))) { - ret = -LTTNG_ERR_INVALID; - goto error; + timeinfo = localtime(&session->creation_time); + if (!timeinfo) { + ret = -LTTNG_ERR_FATAL; + goto error; + } + strftime_ret = strftime(session_creation_datetime, + sizeof(session_creation_datetime), + "%Y%m%d-%H%M%S", timeinfo); + if (strftime_ret == 0) { + ERR("Failed to format session creation timestamp while setting network URI"); + ret = -LTTNG_ERR_FATAL; + goto error; + } + ret = snprintf(output->dst.net.base_dir, + sizeof(output->dst.net.base_dir), + "/%s/%s-%s/", session->hostname, + session->name, + session_creation_datetime); } - DBG3("Consumer set network uri base_dir path %s", tmp_path); } + if (ret >= sizeof(output->dst.net.base_dir)) { + ret = -LTTNG_ERR_INVALID; + ERR("Truncation occurred while setting network output base directory"); + goto error; + } else if (ret == -1) { + ret = -LTTNG_ERR_INVALID; + PERROR("Error occurred while setting network output base directory"); + goto error; + } + + DBG3("Consumer set network uri base_dir path %s", + output->dst.net.base_dir); +end: return 0; equal: return 1; @@ -1121,58 +1168,6 @@ int consumer_send_channel_monitor_pipe(struct consumer_socket *consumer_sock, LTTNG_CONSUMER_SET_CHANNEL_MONITOR_PIPE, pipe); } -/* - * Set consumer subdirectory using the session name and a generated datetime if - * needed. This is appended to the current subdirectory. - */ -int consumer_set_subdir(struct consumer_output *consumer, - const char *session_name) -{ - int ret = 0; - unsigned int have_default_name = 0; - char datetime[16], tmp_path[PATH_MAX]; - time_t rawtime; - struct tm *timeinfo; - - assert(consumer); - assert(session_name); - - memset(tmp_path, 0, sizeof(tmp_path)); - - /* Flag if we have a default session. */ - if (strncmp(session_name, DEFAULT_SESSION_NAME "-", - strlen(DEFAULT_SESSION_NAME) + 1) == 0) { - have_default_name = 1; - } else { - /* Get date and time for session path */ - time(&rawtime); - timeinfo = localtime(&rawtime); - strftime(datetime, sizeof(datetime), "%Y%m%d-%H%M%S", timeinfo); - } - - if (have_default_name) { - ret = snprintf(tmp_path, sizeof(tmp_path), - "%s/%s", consumer->subdir, session_name); - } else { - ret = snprintf(tmp_path, sizeof(tmp_path), - "%s/%s-%s/", consumer->subdir, session_name, datetime); - } - if (ret < 0) { - PERROR("snprintf session name date"); - goto error; - } - - if (lttng_strncpy(consumer->subdir, tmp_path, - sizeof(consumer->subdir))) { - ret = -EINVAL; - goto error; - } - DBG2("Consumer subdir set to %s", consumer->subdir); - -error: - return ret; -} - /* * Ask the consumer if the data is pending for the specific session id. * Returns 1 if data is pending, 0 otherwise, or < 0 on error. @@ -1449,7 +1444,7 @@ enum lttng_error_code consumer_snapshot_channel(struct consumer_socket *socket, sizeof(msg.u.snapshot_channel.pathname), "%s/%s/%s-%s-%" PRIu64 "%s", output->consumer->dst.net.base_dir, - output->consumer->subdir, + output->consumer->domain_subdir, output->name, output->datetime, output->nb_snapshot, session_path); @@ -1460,7 +1455,7 @@ enum lttng_error_code consumer_snapshot_channel(struct consumer_socket *socket, ERR("Snapshot path exceeds the maximal allowed length of %zu bytes (%i bytes required) with path \"%s/%s/%s-%s-%" PRIu64 "%s\"", sizeof(msg.u.snapshot_channel.pathname), ret, output->consumer->dst.net.base_dir, - output->consumer->subdir, + output->consumer->domain_subdir, output->name, output->datetime, output->nb_snapshot, session_path); @@ -1648,7 +1643,7 @@ end: */ int consumer_rotate_channel(struct consumer_socket *socket, uint64_t key, uid_t uid, gid_t gid, struct consumer_output *output, - char *domain_path, bool is_metadata_channel, + const char *domain_path, bool is_metadata_channel, uint64_t new_chunk_id) { int ret; @@ -1671,7 +1666,7 @@ int consumer_rotate_channel(struct consumer_socket *socket, uint64_t key, sizeof(msg.u.rotate_channel.pathname), "%s%s%s", output->dst.net.base_dir, output->chunk_path, domain_path); - if (ret < 0 || ret == sizeof(msg.u.rotate_channel.pathname)) { + if (ret < 0 || ret >= sizeof(msg.u.rotate_channel.pathname)) { ERR("Failed to format channel path name when asking consumer to rotate channel"); ret = -LTTNG_ERR_INVALID; goto error; @@ -1679,10 +1674,10 @@ int consumer_rotate_channel(struct consumer_socket *socket, uint64_t key, } else { msg.u.rotate_channel.relayd_id = (uint64_t) -1ULL; ret = snprintf(msg.u.rotate_channel.pathname, - sizeof(msg.u.rotate_channel.pathname), "%s%s%s", + sizeof(msg.u.rotate_channel.pathname), "%s/%s%s", output->dst.session_root_path, output->chunk_path, domain_path); - if (ret < 0 || ret == sizeof(msg.u.rotate_channel.pathname)) { + if (ret < 0 || ret >= sizeof(msg.u.rotate_channel.pathname)) { ERR("Failed to format channel path name when asking consumer to rotate channel"); ret = -LTTNG_ERR_INVALID; goto error; diff --git a/src/bin/lttng-sessiond/consumer.h b/src/bin/lttng-sessiond/consumer.h index 0e7ed0017..7277c405e 100644 --- a/src/bin/lttng-sessiond/consumer.h +++ b/src/bin/lttng-sessiond/consumer.h @@ -27,6 +27,7 @@ struct snapshot; struct snapshot_output; +struct ltt_session; enum consumer_dst_type { CONSUMER_DST_LOCAL, @@ -147,14 +148,15 @@ struct consumer_output { /* * Subdirectory path name used for both local and network - * consumer (/kernel or /ust). + * consumer ("/kernel", "/ust", or empty). */ - char subdir[LTTNG_PATH_MAX]; + char domain_subdir[max(sizeof(DEFAULT_KERNEL_TRACE_DIR), + sizeof(DEFAULT_UST_TRACE_DIR))]; /* * Hashtable of consumer_socket index by the file descriptor value. For - * multiarch consumer support, we can have more than one consumer (ex: 32 - * and 64 bit). + * multiarch consumer support, we can have more than one consumer (ex: + * 32 and 64 bit). */ struct lttng_ht *socks; @@ -195,7 +197,8 @@ struct consumer_output *consumer_create_output(enum consumer_dst_type type); struct consumer_output *consumer_copy_output(struct consumer_output *obj); void consumer_output_get(struct consumer_output *obj); void consumer_output_put(struct consumer_output *obj); -int consumer_set_network_uri(struct consumer_output *obj, +int consumer_set_network_uri(const struct ltt_session *session, + struct consumer_output *obj, struct lttng_uri *uri); int consumer_send_fds(struct consumer_socket *sock, const int *fds, size_t nb_fd); @@ -220,8 +223,6 @@ int consumer_recv_status_channel(struct consumer_socket *sock, void consumer_output_send_destroy_relayd(struct consumer_output *consumer); int consumer_create_socket(struct consumer_data *data, struct consumer_output *output); -int consumer_set_subdir(struct consumer_output *consumer, - const char *session_name); void consumer_init_ask_channel_comm_msg(struct lttcomm_consumer_msg *msg, uint64_t subbuf_size, @@ -300,7 +301,8 @@ enum lttng_error_code consumer_snapshot_channel(struct consumer_socket *socket, /* Rotation commands. */ int consumer_rotate_channel(struct consumer_socket *socket, uint64_t key, uid_t uid, gid_t gid, struct consumer_output *output, - char *domain_path, bool is_metadata_channel, uint64_t new_chunk_id); + const char *domain_path, bool is_metadata_channel, + uint64_t new_chunk_id); int consumer_rotate_rename(struct consumer_socket *socket, uint64_t session_id, const struct consumer_output *output, const char *old_path, const char *new_path, uid_t uid, gid_t gid); diff --git a/src/bin/lttng-sessiond/kernel-consumer.c b/src/bin/lttng-sessiond/kernel-consumer.c index 1368dc163..993e7b95c 100644 --- a/src/bin/lttng-sessiond/kernel-consumer.c +++ b/src/bin/lttng-sessiond/kernel-consumer.c @@ -48,7 +48,7 @@ static char *create_channel_path(struct consumer_output *consumer, ret = snprintf(tmp_path, sizeof(tmp_path), "%s%s%s", consumer->dst.session_root_path, consumer->chunk_path, - consumer->subdir); + consumer->domain_subdir); if (ret < 0) { PERROR("snprintf kernel channel path"); goto error; @@ -57,7 +57,7 @@ static char *create_channel_path(struct consumer_output *consumer, sizeof(tmp_path), ret, consumer->dst.session_root_path, consumer->chunk_path, - consumer->subdir); + consumer->domain_subdir); goto error; } pathname = lttng_strndup(tmp_path, sizeof(tmp_path)); @@ -76,9 +76,10 @@ static char *create_channel_path(struct consumer_output *consumer, } DBG3("Kernel local consumer tracefile path: %s", pathname); } else { + /* Network output. */ ret = snprintf(tmp_path, sizeof(tmp_path), "%s%s", consumer->dst.net.base_dir, - consumer->subdir); + consumer->domain_subdir); if (ret < 0) { PERROR("snprintf kernel metadata path"); goto error; @@ -86,7 +87,7 @@ static char *create_channel_path(struct consumer_output *consumer, ERR("Kernel channel path exceeds the maximal allowed length of of %zu bytes (%i bytes required) with path \"%s%s\"", sizeof(tmp_path), ret, consumer->dst.net.base_dir, - consumer->subdir); + consumer->domain_subdir); goto error; } pathname = lttng_strndup(tmp_path, sizeof(tmp_path)); diff --git a/src/bin/lttng-sessiond/kernel.c b/src/bin/lttng-sessiond/kernel.c index e3118f191..b4fc995ce 100644 --- a/src/bin/lttng-sessiond/kernel.c +++ b/src/bin/lttng-sessiond/kernel.c @@ -1432,7 +1432,7 @@ enum lttng_error_code kernel_rotate_session(struct ltt_session *session) chan->key, session->name); ret = consumer_rotate_channel(socket, chan->key, ksess->uid, ksess->gid, ksess->consumer, - ksess->consumer->subdir, + ksess->consumer->domain_subdir, /* is_metadata_channel */ false, session->current_archive_id); if (ret < 0) { @@ -1446,7 +1446,7 @@ enum lttng_error_code kernel_rotate_session(struct ltt_session *session) */ ret = consumer_rotate_channel(socket, ksess->metadata->key, ksess->uid, ksess->gid, ksess->consumer, - ksess->consumer->subdir, + ksess->consumer->domain_subdir, /* is_metadata_channel */ true, session->current_archive_id); if (ret < 0) { diff --git a/src/bin/lttng-sessiond/rotate.c b/src/bin/lttng-sessiond/rotate.c index c3413a2ca..f4127c968 100644 --- a/src/bin/lttng-sessiond/rotate.c +++ b/src/bin/lttng-sessiond/rotate.c @@ -120,39 +120,50 @@ int rename_first_chunk(struct ltt_session *session, int ret; char current_full_path[LTTNG_PATH_MAX], new_full_path[LTTNG_PATH_MAX]; - /* Current domain path: /kernel */ if (session->net_handle > 0) { - ret = snprintf(current_full_path, sizeof(current_full_path), "%s/%s", - consumer->dst.net.base_dir, consumer->subdir); + /* + * Current domain path: + * HOSTNAME/{SESSION-[TIMESTAMP], USER_DIRECTORY}/DOMAIN + */ + ret = snprintf(current_full_path, sizeof(current_full_path), + "%s%s", + consumer->dst.net.base_dir, + consumer->domain_subdir); if (ret < 0 || ret >= sizeof(current_full_path)) { ERR("Failed to initialize current full path while renaming first rotation chunk of session \"%s\"", session->name); - ret = -1; + ret = -LTTNG_ERR_UNK; goto error; } } else { - ret = snprintf(current_full_path, sizeof(current_full_path), "%s/%s", - consumer->dst.session_root_path, consumer->subdir); + /* + * Current domain path: + * SESSION_OUTPUT_PATH/DOMAIN + */ + ret = snprintf(current_full_path, sizeof(current_full_path), + "%s/%s", + consumer->dst.session_root_path, + consumer->domain_subdir); if (ret < 0 || ret >= sizeof(current_full_path)) { ERR("Failed to initialize current full path while renaming first rotation chunk of session \"%s\"", session->name); - ret = -1; + ret = -LTTNG_ERR_UNK; goto error; } } - /* New domain path: /--/kernel */ + /* + * New domain path: + * SESSION_BASE_PATH/_-INDEX/DOMAIN + */ ret = snprintf(new_full_path, sizeof(new_full_path), "%s/%s", - new_path, consumer->subdir); + new_path, consumer->domain_subdir); if (ret < 0 || ret >= sizeof(new_full_path)) { ERR("Failed to initialize new full path while renaming first rotation chunk of session \"%s\"", session->name); - ret = -1; + ret = -LTTNG_ERR_UNK; goto error; } - /* - * Move the per-domain fcurrenter inside the first rotation - * fcurrenter. - */ + /* Move the per-domain inside the first rotation chunk path. */ ret = session_rename_chunk(session, current_full_path, new_full_path); if (ret < 0) { ret = -LTTNG_ERR_UNK; @@ -171,70 +182,70 @@ error: * * Returns 0 on success, a negative value on error. */ -int rename_completed_chunk(struct ltt_session *session, time_t ts) +int rename_completed_chunk(struct ltt_session *session, time_t end_ts) { - struct tm *timeinfo; - char new_path[LTTNG_PATH_MAX]; - char datetime[21], start_datetime[21]; int ret; size_t strf_ret; + struct tm *timeinfo; + char new_path[LTTNG_PATH_MAX]; + char start_datetime[21], end_datetime[21]; DBG("Renaming completed chunk for session %s", session->name); - timeinfo = localtime(&ts); + + /* Format chunk start time. */ + timeinfo = localtime(&session->last_chunk_start_ts); if (!timeinfo) { - ERR("Failed to retrieve local time while renaming completed chunk"); + ERR("Failed to separate local time while renaming completed chunk"); ret = -1; goto end; } - - strf_ret = strftime(datetime, sizeof(datetime), "%Y%m%dT%H%M%S%z", - timeinfo); + strf_ret = strftime(start_datetime, sizeof(start_datetime), + "%Y%m%dT%H%M%S%z", timeinfo); if (strf_ret == 0) { ERR("Failed to format timestamp while renaming completed session chunk"); ret = -1; goto end; } - if (session->current_archive_id == 1) { - char start_time[21]; - - timeinfo = localtime(&session->last_chunk_start_ts); - if (!timeinfo) { - ERR("Failed to retrieve local time while renaming completed chunk"); - ret = -1; - goto end; - } + /* Format chunk end time. */ + timeinfo = localtime(&end_ts); + if (!timeinfo) { + ERR("Failed to parse time while renaming completed chunk"); + ret = -1; + goto end; + } + strf_ret = strftime(end_datetime, sizeof(end_datetime), + "%Y%m%dT%H%M%S%z", timeinfo); + if (strf_ret == 0) { + ERR("Failed to format timestamp while renaming completed session chunk"); + ret = -1; + goto end; + } - strf_ret = strftime(start_time, sizeof(start_time), - "%Y%m%dT%H%M%S%z", timeinfo); - if (strf_ret == 0) { - ERR("Failed to format timestamp while renaming completed session chunk"); - ret = -1; - goto end; - } + /* Format completed chunk's path. */ + ret = snprintf(new_path, sizeof(new_path), "%s/archives/%s-%s-%" PRIu64, + session_get_base_path(session), + start_datetime, end_datetime, + session->current_archive_id); + if (ret < 0 || ret >= sizeof(new_path)) { + ERR("Failed to format new chunk path while renaming chunk of session \"%s\"", + session->name); + ret = -1; + goto error; + } + if (session->current_archive_id == 1) { /* * On the first rotation, the current_rotate_path is the * session_root_path, so we need to create the chunk folder * and move the domain-specific folders inside it. */ - ret = snprintf(new_path, sizeof(new_path), "%s/archives/%s-%s-%" PRIu64, - session->rotation_chunk.current_rotate_path, - start_time, - datetime, session->current_archive_id); - if (ret < 0 || ret >= sizeof(new_path)) { - ERR("Failed to format new chunk path while renaming session \"%s\"'s first chunk", - session->name); - ret = -1; - goto end; - } - if (session->kernel_session) { ret = rename_first_chunk(session, session->kernel_session->consumer, new_path); if (ret) { - ERR("Failed to rename kernel session trace folder to %s", new_path); + ERR("Failed to rename kernel session trace folder to \"%s\"", new_path); /* * This is not a fatal error for the rotation * thread, we just need to inform the client @@ -251,7 +262,7 @@ int rename_completed_chunk(struct ltt_session *session, time_t ts) session->ust_session->consumer, new_path); if (ret) { - ERR("Failed to rename userspace session trace folder to %s", new_path); + ERR("Failed to rename userspace session trace folder to \"%s\"", new_path); ret = 0; goto error; } @@ -261,35 +272,11 @@ int rename_completed_chunk(struct ltt_session *session, time_t ts) * After the first rotation, all the trace data is already in * its own chunk folder, we just need to append the suffix. */ - /* Recreate the session->rotation_chunk.current_rotate_path */ - timeinfo = localtime(&session->last_chunk_start_ts); - if (!timeinfo) { - ERR("Failed to retrieve local time while renaming completed chunk"); - ret = -1; - goto end; - } - strf_ret = strftime(start_datetime, sizeof(start_datetime), - "%Y%m%dT%H%M%S%z", timeinfo); - if (!strf_ret) { - ERR("Failed to format timestamp while renaming completed session chunk"); - ret = -1; - goto end; - } - ret = snprintf(new_path, sizeof(new_path), "%s/archives/%s-%s-%" PRIu64, - session_get_base_path(session), - start_datetime, - datetime, session->current_archive_id); - if (ret < 0 || ret >= sizeof(new_path)) { - ERR("Failed to format new chunk path while renaming chunk of session \"%s\"", - session->name); - ret = -1; - goto error; - } ret = session_rename_chunk(session, session->rotation_chunk.current_rotate_path, new_path); if (ret) { - ERR("Failed to rename session trace folder from %s to %s", + ERR("Failed to rename session trace folder from \"%s\" to \"%s\"", session->rotation_chunk.current_rotate_path, new_path); ret = 0; diff --git a/src/bin/lttng-sessiond/session.c b/src/bin/lttng-sessiond/session.c index 5e6488e4f..491de6175 100644 --- a/src/bin/lttng-sessiond/session.c +++ b/src/bin/lttng-sessiond/session.c @@ -467,6 +467,9 @@ bool session_get(struct ltt_session *session) */ void session_put(struct ltt_session *session) { + if (!session) { + return; + } /* * The session list lock must be held as any session_put() * may cause the removal of the session from the session_list. @@ -554,77 +557,144 @@ end: } /* - * Create a brand new session and add it to the session list. + * Create a new session and add it to the session list. + * Session list lock must be held by the caller. */ -int session_create(char *name, uid_t uid, gid_t gid) +enum lttng_error_code session_create(const char *name, uid_t uid, gid_t gid, + struct ltt_session **out_session) { int ret; - struct ltt_session *new_session; + enum lttng_error_code ret_code; + struct ltt_session *new_session = NULL; - /* Allocate session data structure */ + ASSERT_LOCKED(ltt_session_list.lock); + if (name) { + struct ltt_session *clashing_session; + + clashing_session = session_find_by_name(name); + if (clashing_session) { + session_put(clashing_session); + ret_code = LTTNG_ERR_EXIST_SESS; + goto error; + } + } new_session = zmalloc(sizeof(struct ltt_session)); - if (new_session == NULL) { - PERROR("zmalloc"); - ret = LTTNG_ERR_FATAL; - goto error_malloc; + if (!new_session) { + PERROR("Failed to allocate an ltt_session structure"); + ret_code = LTTNG_ERR_NOMEM; + goto error; } urcu_ref_init(&new_session->ref); + pthread_mutex_init(&new_session->lock, NULL); - /* Define session name */ - if (name != NULL) { - if (snprintf(new_session->name, NAME_MAX, "%s", name) < 0) { - ret = LTTNG_ERR_FATAL; - goto error_asprintf; - } - } else { - ERR("No session name given"); - ret = LTTNG_ERR_FATAL; + new_session->creation_time = time(NULL); + if (new_session->creation_time == (time_t) -1) { + PERROR("Failed to sample session creation time"); + ret_code = LTTNG_ERR_SESSION_FAIL; goto error; } - ret = validate_name(name); - if (ret < 0) { - ret = LTTNG_ERR_SESSION_INVALID_CHAR; + /* Create default consumer output. */ + new_session->consumer = consumer_create_output(CONSUMER_DST_LOCAL); + if (new_session->consumer == NULL) { + ret_code = LTTNG_ERR_NOMEM; goto error; } + if (name) { + ret = lttng_strncpy(new_session->name, name, sizeof(new_session->name)); + if (ret) { + ret_code = LTTNG_ERR_SESSION_INVALID_CHAR; + goto error; + } + ret = validate_name(name); + if (ret < 0) { + ret_code = LTTNG_ERR_SESSION_INVALID_CHAR; + goto error; + } + } else { + int i = 0; + bool found_name = false; + char datetime[16]; + struct tm *timeinfo; + + timeinfo = localtime(&new_session->creation_time); + if (!timeinfo) { + ret_code = LTTNG_ERR_SESSION_FAIL; + goto error; + } + strftime(datetime, sizeof(datetime), "%Y%m%d-%H%M%S", timeinfo); + for (i = 0; i < INT_MAX; i++) { + struct ltt_session *clashing_session; + + if (i == 0) { + ret = snprintf(new_session->name, + sizeof(new_session->name), + "%s-%s", + DEFAULT_SESSION_NAME, + datetime); + } else { + ret = snprintf(new_session->name, + sizeof(new_session->name), + "%s%d-%s", + DEFAULT_SESSION_NAME, i, + datetime); + } + if (ret == -1 || ret >= sizeof(new_session->name)) { + /* + * Null-terminate in case the name is used + * in logging statements. + */ + new_session->name[sizeof(new_session->name) - 1] = '\0'; + ret_code = LTTNG_ERR_SESSION_FAIL; + goto error; + } + + clashing_session = + session_find_by_name(new_session->name); + session_put(clashing_session); + if (!clashing_session) { + found_name = true; + break; + } + } + if (found_name) { + DBG("Generated session name \"%s\"", new_session->name); + new_session->has_auto_generated_name = true; + } else { + ERR("Failed to auto-generate a session name"); + ret_code = LTTNG_ERR_SESSION_FAIL; + goto error; + } + } + ret = gethostname(new_session->hostname, sizeof(new_session->hostname)); if (ret < 0) { if (errno == ENAMETOOLONG) { new_session->hostname[sizeof(new_session->hostname) - 1] = '\0'; + ERR("Hostname exceeds the maximal permitted length and has been truncated to %s", + new_session->hostname); } else { - ret = LTTNG_ERR_FATAL; + ret_code = LTTNG_ERR_SESSION_FAIL; goto error; } } - /* Init kernel session */ - new_session->kernel_session = NULL; - new_session->ust_session = NULL; - - /* Init lock */ - pthread_mutex_init(&new_session->lock, NULL); - new_session->uid = uid; new_session->gid = gid; ret = snapshot_init(&new_session->snapshot); if (ret < 0) { - ret = LTTNG_ERR_NOMEM; + ret_code = LTTNG_ERR_NOMEM; goto error; } - new_session->rotation_pending_local = false; - new_session->rotation_pending_relay = false; new_session->rotation_state = LTTNG_ROTATION_STATE_NO_ROTATION; - new_session->rotation_pending_check_timer_enabled = false; - new_session->rotation_schedule_timer_enabled = false; - - /* Add new session to the session list */ - session_lock_list(); + /* Add new session to the session list. */ new_session->id = add_session_list(new_session); + /* * Add the new session to the ltt_sessions_ht_by_id. * No ownership is taken by the hash table; it is merely @@ -633,25 +703,25 @@ int session_create(char *name, uid_t uid, gid_t gid) */ add_session_ht(new_session); new_session->published = true; - session_unlock_list(); /* - * Consumer is let to NULL since the create_session_uri command will set it - * up and, if valid, assign it to the session. + * Consumer is left to NULL since the create_session_uri command will + * set it up and, if valid, assign it to the session. */ - DBG("Tracing session %s created with ID %" PRIu64 " by UID %d GID %d", - name, new_session->id, new_session->uid, new_session->gid); - - return LTTNG_OK; - + DBG("Tracing session %s created with ID %" PRIu64 " by uid = %d, gid = %d", + new_session->name, new_session->id, new_session->uid, + new_session->gid); + ret_code = LTTNG_OK; +end: + if (new_session) { + (void) session_get(new_session); + *out_session = new_session; + } + return ret_code; error: -error_asprintf: - session_lock_list(); session_put(new_session); - session_unlock_list(); - -error_malloc: - return ret; + new_session = NULL; + goto end; } /* diff --git a/src/bin/lttng-sessiond/session.h b/src/bin/lttng-sessiond/session.h index 8d41c38aa..b008c967e 100644 --- a/src/bin/lttng-sessiond/session.h +++ b/src/bin/lttng-sessiond/session.h @@ -69,7 +69,9 @@ struct ltt_session_list { */ struct ltt_session { char name[NAME_MAX]; + bool has_auto_generated_name; char hostname[HOST_NAME_MAX]; /* Local hostname. */ + time_t creation_time; struct ltt_kernel_session *kernel_session; struct ltt_ust_session *ust_session; struct urcu_ref ref; @@ -100,7 +102,11 @@ struct ltt_session { * copied into those sessions. */ struct consumer_output *consumer; - + /* + * Indicates whether or not the user has specified an output directory + * or if it was configured using the default configuration. + */ + bool has_user_specified_directory; /* Did at least ONE start command has been triggered?. */ unsigned int has_been_started:1; /* @@ -222,8 +228,8 @@ struct ltt_session { }; /* Prototypes */ -int session_create(char *name, uid_t uid, gid_t gid); - +enum lttng_error_code session_create(const char *name, uid_t uid, gid_t gid, + struct ltt_session **out_session); void session_lock(struct ltt_session *session); void session_lock_list(void); int session_trylock_list(void); diff --git a/src/bin/lttng-sessiond/snapshot.c b/src/bin/lttng-sessiond/snapshot.c index c4c3bf1f8..03b830de7 100644 --- a/src/bin/lttng-sessiond/snapshot.c +++ b/src/bin/lttng-sessiond/snapshot.c @@ -39,7 +39,8 @@ static inline unsigned long get_next_output_id(struct snapshot *snapshot) * * Return 0 on success or else a negative value. */ -static int output_init(uint64_t max_size, const char *name, +static int output_init(const struct ltt_session *session, + uint64_t max_size, const char *name, struct lttng_uri *uris, size_t nb_uri, struct consumer_output *consumer, struct snapshot_output *output, struct snapshot *snapshot) @@ -115,7 +116,8 @@ static int output_init(uint64_t max_size, const char *name, for (i = 0; i < nb_uri; i ++) { /* Network URIs */ - ret = consumer_set_network_uri(output->consumer, &uris[i]); + ret = consumer_set_network_uri(session, output->consumer, + &uris[i]); if (ret < 0) { goto error; } @@ -132,13 +134,14 @@ end: * * Return 0 on success or else a negative value. */ -int snapshot_output_init_with_uri(uint64_t max_size, const char *name, +int snapshot_output_init_with_uri(const struct ltt_session *session, + uint64_t max_size, const char *name, struct lttng_uri *uris, size_t nb_uri, struct consumer_output *consumer, struct snapshot_output *output, struct snapshot *snapshot) { - return output_init(max_size, name, uris, nb_uri, consumer, output, - snapshot); + return output_init(session, max_size, name, uris, nb_uri, consumer, + output, snapshot); } /* @@ -147,7 +150,8 @@ int snapshot_output_init_with_uri(uint64_t max_size, const char *name, * * Return 0 on success or else a negative value. */ -int snapshot_output_init(uint64_t max_size, const char *name, +int snapshot_output_init(const struct ltt_session *session, + uint64_t max_size, const char *name, const char *ctrl_url, const char *data_url, struct consumer_output *consumer, struct snapshot_output *output, struct snapshot *snapshot) @@ -162,8 +166,8 @@ int snapshot_output_init(uint64_t max_size, const char *name, goto error; } - ret = output_init(max_size, name, uris, nb_uri, consumer, output, - snapshot); + ret = output_init(session, max_size, name, uris, nb_uri, consumer, + output, snapshot); error: free(uris); diff --git a/src/bin/lttng-sessiond/snapshot.h b/src/bin/lttng-sessiond/snapshot.h index bdf057036..12641860a 100644 --- a/src/bin/lttng-sessiond/snapshot.h +++ b/src/bin/lttng-sessiond/snapshot.h @@ -28,6 +28,7 @@ #include "consumer.h" struct consumer_output; +struct ltt_session; struct snapshot_output { uint32_t id; @@ -72,11 +73,13 @@ void snapshot_add_output(struct snapshot *snapshot, /* Snapshot output object. */ struct snapshot_output *snapshot_output_alloc(void); void snapshot_output_destroy(struct snapshot_output *obj); -int snapshot_output_init(uint64_t max_size, const char *name, +int snapshot_output_init(const struct ltt_session *session, + uint64_t max_size, const char *name, const char *ctrl_url, const char *data_url, struct consumer_output *consumer, struct snapshot_output *output, struct snapshot *snapshot); -int snapshot_output_init_with_uri(uint64_t max_size, const char *name, +int snapshot_output_init_with_uri(const struct ltt_session *session, + uint64_t max_size, const char *name, struct lttng_uri *uris, size_t nb_uri, struct consumer_output *consumer, struct snapshot_output *output, struct snapshot *snapshot); diff --git a/src/bin/lttng-sessiond/ust-app.c b/src/bin/lttng-sessiond/ust-app.c index 59d8a8fc4..4bee50496 100644 --- a/src/bin/lttng-sessiond/ust-app.c +++ b/src/bin/lttng-sessiond/ust-app.c @@ -4402,24 +4402,18 @@ int ust_app_start_trace(struct ltt_ust_session *usess, struct ust_app *app) /* Create directories if consumer is LOCAL and has a path defined. */ if (usess->consumer->type == CONSUMER_DST_LOCAL && usess->consumer->dst.session_root_path[0] != '\0') { - char *tmp_path; + char tmp_path[LTTNG_PATH_MAX]; - tmp_path = zmalloc(LTTNG_PATH_MAX); - if (!tmp_path) { - ERR("Alloc tmp_path"); - goto error_unlock; - } - ret = snprintf(tmp_path, LTTNG_PATH_MAX, "%s%s%s", + ret = snprintf(tmp_path, sizeof(tmp_path), "%s/%s%s", usess->consumer->dst.session_root_path, usess->consumer->chunk_path, - usess->consumer->subdir); - if (ret >= LTTNG_PATH_MAX) { - ERR("Local destination path exceeds the maximal allowed length of %i bytes (needs %i bytes) with path = \"%s%s%s\"", - LTTNG_PATH_MAX, ret, + usess->consumer->domain_subdir); + if (ret >= sizeof(tmp_path)) { + ERR("Local destination path exceeds the maximal allowed length of %zu bytes (needs %i bytes) with path = \"%s%s%s\"", + sizeof(tmp_path), ret, usess->consumer->dst.session_root_path, usess->consumer->chunk_path, - usess->consumer->subdir); - free(tmp_path); + usess->consumer->domain_subdir); goto error_unlock; } @@ -4427,7 +4421,6 @@ int ust_app_start_trace(struct ltt_ust_session *usess, struct ust_app *app) tmp_path); ret = run_as_mkdir_recursive(tmp_path, S_IRWXU | S_IRWXG, ua_sess->euid, ua_sess->egid); - free(tmp_path); if (ret < 0) { if (errno != EEXIST) { ERR("Trace directory creation error"); @@ -6343,7 +6336,7 @@ enum lttng_error_code ust_app_rotate_session(struct ltt_session *session) ret = snprintf(pathname, sizeof(pathname), DEFAULT_UST_TRACE_DIR "/" DEFAULT_UST_TRACE_UID_PATH, reg->uid, reg->bits_per_long); - if (ret < 0 || ret == sizeof(pathname)) { + if (ret < 0 || ret >= sizeof(pathname)) { PERROR("Failed to format rotation path"); cmd_ret = LTTNG_ERR_INVALID; goto error; @@ -6396,7 +6389,7 @@ enum lttng_error_code ust_app_rotate_session(struct ltt_session *session) ret = snprintf(pathname, sizeof(pathname), DEFAULT_UST_TRACE_DIR "/%s", ua_sess->path); - if (ret < 0 || ret == sizeof(pathname)) { + if (ret < 0 || ret >= sizeof(pathname)) { PERROR("Failed to format rotation path"); cmd_ret = LTTNG_ERR_INVALID; goto error; diff --git a/src/bin/lttng-sessiond/ust-consumer.c b/src/bin/lttng-sessiond/ust-consumer.c index 575593f03..6be57e447 100644 --- a/src/bin/lttng-sessiond/ust-consumer.c +++ b/src/bin/lttng-sessiond/ust-consumer.c @@ -65,10 +65,10 @@ static char *setup_trace_path(struct consumer_output *consumer, /* Get correct path name destination */ if (consumer->type == CONSUMER_DST_LOCAL) { /* Set application path to the destination path */ - ret = snprintf(pathname, LTTNG_PATH_MAX, "%s%s%s%s", + ret = snprintf(pathname, LTTNG_PATH_MAX, "%s/%s%s/%s", consumer->dst.session_root_path, consumer->chunk_path, - consumer->subdir, ua_sess->path); + consumer->domain_subdir, ua_sess->path); if (ret < 0) { PERROR("snprintf channel path"); goto error; @@ -84,10 +84,10 @@ static char *setup_trace_path(struct consumer_output *consumer, } } } else { - ret = snprintf(pathname, LTTNG_PATH_MAX, "%s%s%s%s", + ret = snprintf(pathname, LTTNG_PATH_MAX, "%s%s/%s%s", consumer->dst.net.base_dir, consumer->chunk_path, - consumer->subdir, + consumer->domain_subdir, ua_sess->path); if (ret < 0) { PERROR("snprintf channel path"); diff --git a/src/bin/lttng-sessiond/utils.c b/src/bin/lttng-sessiond/utils.c index 99ec7772b..38082f639 100644 --- a/src/bin/lttng-sessiond/utils.c +++ b/src/bin/lttng-sessiond/utils.c @@ -98,19 +98,7 @@ int loglevels_match(int a_loglevel_type, int a_loglevel_value, const char *session_get_base_path(const struct ltt_session *session) { - struct consumer_output *consumer; - - if (session->kernel_session) { - consumer = session->kernel_session->consumer; - } else if (session->ust_session) { - consumer = session->ust_session->consumer; - } else { - abort(); - } - - if (session->net_handle > 0) { - return consumer->dst.net.base_dir; - } else { - return consumer->dst.session_root_path; - } + return session->net_handle > 0 ? + session->consumer->dst.net.base_dir : + session->consumer->dst.session_root_path; } diff --git a/src/bin/lttng/commands/create.c b/src/bin/lttng/commands/create.c index d3e5b9a84..a7d2d89be 100644 --- a/src/bin/lttng/commands/create.c +++ b/src/bin/lttng/commands/create.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 - David Goulet + * Copyright (C) 2019 - Jérémie Galarneau * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2 only, @@ -39,6 +40,7 @@ #include #include #include +#include static char *opt_output_path; static char *opt_session_name; @@ -63,8 +65,14 @@ enum { OPT_LIVE_TIMER, }; -static struct mi_writer *writer; +enum output_type { + OUTPUT_NONE, + OUTPUT_LOCAL, + OUTPUT_NETWORK, + OUTPUT_UNSPECIFIED, +}; +static struct mi_writer *writer; static struct poptOption long_options[] = { /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ {"help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL}, @@ -81,13 +89,6 @@ static struct poptOption long_options[] = { {0, 0, 0, 0, 0, 0, 0} }; -/* - * Please have a look at src/lib/lttng-ctl/lttng-ctl.c for more information on - * why this declaration exists and used ONLY in for this command. - */ -extern int _lttng_create_session_ext(const char *name, const char *url, - const char *datetime); - /* * Retrieve the created session and mi output it based on provided argument * This is currently a summary of what was pretty printed and is subject to @@ -139,84 +140,131 @@ end: return ret; } -/* - * For a session name, set the consumer URLs. - */ -static int set_consumer_url(const char *session_name, const char *ctrl_url, - const char *data_url) +static +struct lttng_session_descriptor *create_session_descriptor(void) { int ret; - struct lttng_handle *handle; - - assert(session_name); - - /* - * Set handle with the session_name, but no domain. This implies that - * the actions taken with this handle apply on the tracing session - * rather then the domain-specific session. - */ - handle = lttng_create_handle(session_name, NULL); - if (handle == NULL) { - ret = CMD_FATAL; - goto error; - } - - ret = lttng_set_consumer_url(handle, ctrl_url, data_url); - if (ret < 0) { - goto error; - } - - if (ctrl_url) { - MSG("Control URL %s set for session %s", ctrl_url, session_name); - } - - if (data_url) { - MSG("Data URL %s set for session %s", data_url, session_name); - } - -error: - lttng_destroy_handle(handle); - return ret; -} - -static int add_snapshot_output(const char *session_name, const char *ctrl_url, - const char *data_url) -{ - int ret; - struct lttng_snapshot_output *output = NULL; + ssize_t uri_count; + enum output_type output_type; + struct lttng_uri *uris = NULL; + struct lttng_session_descriptor *descriptor = NULL; + const char *uri_str1 = NULL, *uri_str2 = NULL; + char local_output_path[LTTNG_PATH_MAX] = {}; + + if (opt_no_output) { + output_type = OUTPUT_NONE; + } else if (opt_output_path) { + char *expanded_output_path; + + output_type = OUTPUT_LOCAL; + expanded_output_path = utils_expand_path(opt_output_path); + if (!expanded_output_path) { + ERR("Failed to expand output path."); + goto end; + } + ret = lttng_strncpy(local_output_path, expanded_output_path, + sizeof(local_output_path)); + free(expanded_output_path); + if (ret) { + ERR("Output path exceeds the maximal supported length (%zu bytes)", + sizeof(local_output_path)); + goto end; + } + } else if (opt_url || opt_ctrl_url) { + uri_str1 = opt_ctrl_url ? opt_ctrl_url : opt_url; + uri_str2 = opt_data_url; - assert(session_name); + uri_count = uri_parse_str_urls(uri_str1, uri_str2, &uris); + if (uri_count != 1 && uri_count != 2) { + ERR("Unrecognized URL format."); + goto end; + } - output = lttng_snapshot_output_create(); - if (!output) { - ret = CMD_FATAL; - goto error_create; + switch (uri_count) { + case 1: + output_type = OUTPUT_LOCAL; + if (uris[0].dtype != LTTNG_DST_PATH) { + ERR("Unrecognized URL format."); + goto end; + } + ret = lttng_strncpy(local_output_path, uris[0].dst.path, + sizeof(local_output_path)); + if (ret) { + ERR("Output path exceeds the maximal supported length (%zu bytes)", + sizeof(local_output_path)); + } + break; + case 2: + output_type = OUTPUT_NETWORK; + break; + default: + /* Already checked. */ + abort(); + } + } else { + output_type = OUTPUT_UNSPECIFIED; } - if (ctrl_url) { - ret = lttng_snapshot_output_set_ctrl_url(ctrl_url, output); - if (ret < 0) { - goto error; + if (opt_snapshot) { + /* Snapshot session. */ + switch (output_type) { + case OUTPUT_UNSPECIFIED: + case OUTPUT_LOCAL: + descriptor = lttng_session_descriptor_snapshot_local_create( + opt_session_name, + output_type == OUTPUT_LOCAL ? + local_output_path : NULL); + break; + case OUTPUT_NONE: + descriptor = lttng_session_descriptor_snapshot_create( + opt_session_name); + break; + case OUTPUT_NETWORK: + descriptor = lttng_session_descriptor_snapshot_network_create( + opt_session_name, uri_str1, uri_str2); + break; + default: + abort(); } - } + } else if (opt_live_timer) { + /* Live session. */ + if (output_type != OUTPUT_UNSPECIFIED && + output_type != OUTPUT_NETWORK) { + ERR("Unsupported output type specified for live session."); + goto end; + } + descriptor = lttng_session_descriptor_live_network_create( + opt_session_name, uri_str1, uri_str2, + opt_live_timer); - if (data_url) { - ret = lttng_snapshot_output_set_data_url(data_url, output); - if (ret < 0) { - goto error; + } else { + /* Regular session. */ + switch (output_type) { + case OUTPUT_UNSPECIFIED: + case OUTPUT_LOCAL: + descriptor = lttng_session_descriptor_local_create( + opt_session_name, + output_type == OUTPUT_LOCAL ? + local_output_path : NULL); + break; + case OUTPUT_NONE: + descriptor = lttng_session_descriptor_create( + opt_session_name); + break; + case OUTPUT_NETWORK: + descriptor = lttng_session_descriptor_network_create( + opt_session_name, uri_str1, uri_str2); + break; + default: + abort(); } } - - /* This call, if successful, populates the id of the output object. */ - ret = lttng_snapshot_add_output(session_name, output); - if (ret < 0) { - goto error; + if (!descriptor) { + ERR("Failed to initialize session creation command."); } - -error: - lttng_snapshot_output_destroy(output); -error_create: - return ret; +end: + free(uris); + return descriptor; } /* @@ -227,34 +275,21 @@ error_create: */ static int create_session(void) { - int ret; - char *session_name = NULL, *traces_path = NULL, *alloc_path = NULL; - char *alloc_url = NULL, *url = NULL, datetime[16]; - char session_name_date[NAME_MAX + 17], *print_str_url = NULL; - time_t rawtime; - struct tm *timeinfo; - char shm_path[PATH_MAX] = ""; - - /* Get date and time for automatic session name/path */ - time(&rawtime); - timeinfo = localtime(&rawtime); - strftime(datetime, sizeof(datetime), "%Y%m%d-%H%M%S", timeinfo); - - /* Auto session name creation */ - if (opt_session_name == NULL) { - ret = snprintf(session_name_date, sizeof(session_name_date), - DEFAULT_SESSION_NAME "-%s", datetime); - if (ret < 0) { - PERROR("snprintf session name"); - goto error; - } - session_name = session_name_date; - DBG("Auto session name set to %s", session_name_date); - } else { + int ret, i; + char shm_path[LTTNG_PATH_MAX] = {}; + struct lttng_session_descriptor *session_descriptor = NULL; + enum lttng_session_descriptor_status descriptor_status; + enum lttng_error_code ret_code; + struct lttng_session *sessions = NULL; + const struct lttng_session *created_session = NULL; + const char *created_session_name; + + /* Validate options. */ + if (opt_session_name) { if (strlen(opt_session_name) > NAME_MAX) { ERR("Session name too long. Length must be lower or equal to %d", NAME_MAX); - ret = LTTNG_ERR_SESSION_FAIL; + ret = CMD_ERROR; goto error; } /* @@ -272,193 +307,169 @@ static int create_session(void) ret = CMD_ERROR; goto error; } - session_name = opt_session_name; - ret = snprintf(session_name_date, sizeof(session_name_date), - "%s-%s", session_name, datetime); - if (ret < 0) { - PERROR("snprintf session name"); - goto error; - } } - if ((!opt_ctrl_url && opt_data_url) || (opt_ctrl_url && !opt_data_url)) { - ERR("You need both control and data URL."); + if (opt_snapshot && opt_live_timer) { + ERR("Snapshot and live modes are mutually exclusive."); ret = CMD_ERROR; goto error; } - if (opt_output_path != NULL) { - traces_path = utils_expand_path(opt_output_path); - if (traces_path == NULL) { - ret = CMD_ERROR; - goto error; - } - - /* Create URL string from the local file system path */ - ret = asprintf(&alloc_url, "file://%s", traces_path); - if (ret < 0) { - PERROR("asprintf url path"); - ret = CMD_FATAL; - goto error; - } - /* URL to use in the lttng_create_session() call */ - url = alloc_url; - print_str_url = traces_path; - } else if (opt_url) { /* Handling URL (-U opt) */ - url = opt_url; - print_str_url = url; - } else if (opt_data_url && opt_ctrl_url) { - /* - * With both control and data, we'll be setting the consumer URL after - * session creation thus use no URL. - */ - url = NULL; - } else if (!opt_no_output) { - char *tmp_path; - - /* Auto output path */ - tmp_path = utils_get_home_dir(); - if (tmp_path == NULL) { - ERR("HOME path not found.\n \ - Please specify an output path using -o, --output PATH"); - ret = CMD_FATAL; - goto error; - } - alloc_path = strdup(tmp_path); - if (!alloc_path) { - PERROR("allocating alloc_path"); - ret = CMD_FATAL; - goto error; - } - ret = asprintf(&alloc_url, - "file://%s/" DEFAULT_TRACE_DIR_NAME "/%s", - alloc_path, session_name_date); - if (ret < 0) { - PERROR("asprintf trace dir name"); - ret = CMD_FATAL; - goto error; - } - - url = alloc_url; - print_str_url = alloc_url + strlen("file://"); - } else { - /* No output means --no-output or --snapshot mode. */ - url = NULL; + if ((!opt_ctrl_url && opt_data_url) || (opt_ctrl_url && !opt_data_url)) { + ERR("Both control and data URLs must be specified."); + ret = CMD_ERROR; + goto error; } - /* Use default live URL if NO url is/are found. */ - if ((opt_live_timer && !opt_url) && (opt_live_timer && !opt_data_url)) { - /* Override the url */ - free(url); - url = NULL; - - ret = asprintf(&alloc_url, "net://127.0.0.1"); - if (ret < 0) { - PERROR("asprintf default live URL"); - ret = CMD_FATAL; - goto error; - } - url = alloc_url; - print_str_url = url; + session_descriptor = create_session_descriptor(); + if (!session_descriptor) { + ret = CMD_ERROR; + goto error; } - - if (opt_snapshot && opt_live_timer) { - ERR("Snapshot and live modes are mutually exclusive."); + ret_code = lttng_create_session_ext(session_descriptor); + if (ret_code != LTTNG_OK) { + ERR("%s", lttng_strerror(-ret_code)); ret = CMD_ERROR; goto error; } - if (opt_snapshot) { - /* No output by default. */ - const char *snapshot_url = NULL; - - if (opt_url) { - snapshot_url = url; - } else if (!opt_data_url && !opt_ctrl_url) { - /* This is the session path that we need to use as output. */ - snapshot_url = url; - } - ret = lttng_create_session_snapshot(session_name, snapshot_url); - } else if (opt_live_timer) { - const char *pathname; - - if (opt_relayd_path) { - pathname = opt_relayd_path; - } else { - pathname = INSTALL_BIN_PATH "/lttng-relayd"; - } - if (!opt_url && !opt_data_url && !check_relayd() && - spawn_relayd(pathname, 0) < 0) { - goto error; - } - ret = lttng_create_session_live(session_name, url, opt_live_timer); - } else { - ret = _lttng_create_session_ext(session_name, url, datetime); + descriptor_status = lttng_session_descriptor_get_session_name( + session_descriptor, &created_session_name); + if (descriptor_status != LTTNG_SESSION_DESCRIPTOR_STATUS_OK) { + ERR("Failed to obtain created session name"); + ret = CMD_ERROR; + goto error; } + + ret = lttng_list_sessions(&sessions); if (ret < 0) { - /* Don't set ret so lttng can interpret the sessiond error. */ - switch (-ret) { - case LTTNG_ERR_EXIST_SESS: - WARN("Session %s already exists", session_name); - break; - default: + ERR("Failed to fetch properties of created session: %s", + lttng_strerror(ret)); + ret = CMD_ERROR; + goto error; + } + for (i = 0; i < ret; i++) { + if (!strcmp(created_session_name, sessions[i].name)) { + created_session = &sessions[i]; break; } + } + if (!created_session) { + ERR("Failed to fetch properties of created session"); + ret = CMD_ERROR; goto error; } - if (opt_ctrl_url && opt_data_url) { - if (opt_snapshot) { - ret = add_snapshot_output(session_name, opt_ctrl_url, - opt_data_url); - } else { - /* Setting up control URI (-C or/and -D opt) */ - ret = set_consumer_url(session_name, opt_ctrl_url, opt_data_url); - } - if (ret < 0) { - /* Destroy created session because the URL are not valid. */ - lttng_destroy_session(session_name); - goto error; + if (opt_shm_path) { + char datetime_suffix[17] = {}; + + /* + * An auto-generated session name already includes the creation + * timestamp. + */ + if (opt_session_name) { + uint64_t creation_time; + struct tm *timeinfo; + time_t creation_time_t; + size_t strftime_ret; + + ret_code = lttng_session_get_creation_time( + created_session, + &creation_time); + if (ret_code != LTTNG_OK) { + ERR("%s", lttng_strerror(-ret_code)); + ret = CMD_ERROR; + goto error; + } + creation_time_t = (time_t) creation_time; + timeinfo = localtime(&creation_time_t); + if (!timeinfo) { + PERROR("Failed to interpret session creation time"); + ret = CMD_ERROR; + goto error; + } + strftime_ret = strftime(datetime_suffix, + sizeof(datetime_suffix), + "-%Y%m%d-%H%M%S", timeinfo); + if (strftime_ret == 0) { + ERR("Failed to format session creation time."); + ret = CMD_ERROR; + goto error; + } } - } - if (opt_shm_path) { ret = snprintf(shm_path, sizeof(shm_path), - "%s/%s", opt_shm_path, session_name_date); - if (ret < 0) { - PERROR("snprintf shm_path"); + "%s/%s%s", opt_shm_path, created_session_name, + datetime_suffix); + if (ret < 0 || ret >= sizeof(shm_path)) { + ERR("Failed to format the shared memory path."); + ret = CMD_ERROR; goto error; } - - ret = lttng_set_session_shm_path(session_name, shm_path); + ret = lttng_set_session_shm_path(created_session_name, + shm_path); if (ret < 0) { - lttng_destroy_session(session_name); + lttng_destroy_session(created_session_name); + ret = CMD_ERROR; goto error; } } - MSG("Session %s created.", session_name); - if (print_str_url && !opt_snapshot) { - MSG("Traces will be written in %s", print_str_url); + if (opt_snapshot) { + MSG("Snapshot session %s created.", created_session_name); + } else if (opt_live_timer) { + MSG("Live session %s created.", created_session_name); + } else { + MSG("Session %s created.", created_session_name); + } + + if (*created_session->path && !opt_snapshot) { + MSG("Traces will be output to %s", created_session->path); if (opt_live_timer) { - MSG("Live timer set to %u %s", opt_live_timer, USEC_UNIT); + MSG("Live timer interval set to %u %s", opt_live_timer, + USEC_UNIT); } } else if (opt_snapshot) { - if (print_str_url) { - MSG("Default snapshot output set to: %s", print_str_url); + struct lttng_snapshot_output_list *list; + struct lttng_snapshot_output *iter; + char snapshot_url[LTTNG_PATH_MAX] = {}; + + ret = lttng_snapshot_list_output(created_session_name, &list); + if (ret < 0) { + ERR("Failed to list snapshot outputs."); + ret = CMD_ERROR; + goto error; + } + + while ((iter = lttng_snapshot_output_list_get_next(list))) { + const char *url = NULL; + + url = lttng_snapshot_output_get_ctrl_url( + iter); + ret = lttng_strncpy(snapshot_url, url, + sizeof(snapshot_url)); + if (ret) { + snapshot_url[0] = '\0'; + ERR("Failed to retrieve snapshot output destination"); + } + break; } - MSG("Snapshot mode set. Every channel enabled for that session will " - "be set to mmap output, and default to overwrite mode."); + lttng_snapshot_output_list_destroy(list); + + if (*snapshot_url) { + MSG("Default snapshot output set to %s", + snapshot_url); + } + MSG("Every channel enabled for this session will be set to mmap output and default to overwrite mode."); } if (opt_shm_path) { - MSG("Session %s set to shm_path: %s.", session_name, - shm_path); + MSG("Shared memory path set to %s", shm_path); } /* Mi output */ if (lttng_opt_mi) { - ret = mi_created_session(session_name); + ret = mi_created_session(created_session_name); if (ret) { ret = CMD_ERROR; goto error; @@ -466,22 +477,16 @@ static int create_session(void) } /* Init lttng session config */ - ret = config_init(session_name); + ret = config_init(created_session_name); if (ret < 0) { ret = CMD_ERROR; goto error; } ret = CMD_SUCCESS; - error: - free(alloc_url); - free(traces_path); - free(alloc_path); - - if (ret < 0) { - ERR("%s", lttng_strerror(ret)); - } + lttng_session_descriptor_destroy(session_descriptor); + free(sessions); return ret; } diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 16e9e548b..dae067df6 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -27,7 +27,8 @@ libcommon_la_SOURCES = error.h error.c utils.c utils.h runas.h runas.c \ buffer-view.h buffer-view.c \ location.c \ waiter.h waiter.c \ - userspace-probe.c event.c time.c + userspace-probe.c event.c time.c \ + session-descriptor.c if HAVE_ELF_H libcommon_la_SOURCES += lttng-elf.h lttng-elf.c diff --git a/src/common/error.c b/src/common/error.c index 11cdb0ed8..236f21bae 100644 --- a/src/common/error.c +++ b/src/common/error.c @@ -209,6 +209,7 @@ static const char *error_string_array[] = { [ ERROR_INDEX(LTTNG_ERR_MKDIR_FAIL_CONSUMER) ] = "mkdir failure on consumer", [ ERROR_INDEX(LTTNG_ERR_CHAN_NOT_FOUND) ] = "Channel not found", [ ERROR_INDEX(LTTNG_ERR_SNAPSHOT_UNSUPPORTED) ] = "Session configuration does not allow the use of snapshots", + [ ERROR_INDEX(LTTNG_ERR_SESSION_NOT_EXIST) ] = "Tracing session does not exist", /* Last element */ [ ERROR_INDEX(LTTNG_ERR_NR) ] = "Unknown error code" diff --git a/src/common/session-descriptor.c b/src/common/session-descriptor.c new file mode 100644 index 000000000..6f0a357e3 --- /dev/null +++ b/src/common/session-descriptor.c @@ -0,0 +1,1180 @@ +/* + * Copyright (C) 2019 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct lttng_session_descriptor_network_location { + struct lttng_uri *control; + struct lttng_uri *data; +}; + +struct lttng_session_descriptor { + enum lttng_session_descriptor_type type; + /* + * If an output type that is not OUTPUT_TYPE_NONE is specified, + * it means that an output of that type must be generated at + * session-creation time. + */ + enum lttng_session_descriptor_output_type output_type; + char *name; + union { + struct lttng_session_descriptor_network_location network; + struct lttng_uri *local; + } output; +}; + +struct lttng_session_descriptor_snapshot { + struct lttng_session_descriptor base; + /* + * Assumes at-most one snapshot output is supported. Uses + * the output field of the base class. + */ +}; + +struct lttng_session_descriptor_live { + struct lttng_session_descriptor base; + unsigned long long live_timer_us; +}; + +struct lttng_session_descriptor_comm { + /* enum lttng_session_descriptor_type */ + uint8_t type; + /* enum lttng_session_descriptor_output_type */ + uint8_t output_type; + /* Includes trailing null. */ + uint32_t name_len; + /* Name follows, followed by URIs */ + uint8_t uri_count; +} LTTNG_PACKED; + +struct lttng_session_descriptor_live_comm { + struct lttng_session_descriptor_comm base; + /* Live-specific parameters. */ + uint64_t live_timer_us; +} LTTNG_PACKED; + +static +struct lttng_uri *uri_copy(const struct lttng_uri *uri) +{ + struct lttng_uri *new_uri = NULL; + + if (!uri) { + goto end; + } + + new_uri = zmalloc(sizeof(*new_uri)); + if (!new_uri) { + goto end; + } + memcpy(new_uri, uri, sizeof(*new_uri)); +end: + return new_uri; +} + +static +struct lttng_uri *uri_from_path(const char *path) +{ + struct lttng_uri *uris = NULL; + ssize_t uri_count; + char local_protocol_string[LTTNG_PATH_MAX + sizeof("file://")] = + "file://"; + + if (strlen(path) >= LTTNG_PATH_MAX) { + goto end; + } + + if (path[0] != '/') { + /* Not an absolute path. */ + goto end; + } + + strncat(local_protocol_string, path, LTTNG_PATH_MAX); + uri_count = uri_parse(local_protocol_string, &uris); + if (uri_count != 1) { + goto error; + } + if (uris[0].dtype != LTTNG_DST_PATH) { + goto error; + } + +end: + return uris; +error: + free(uris); + return NULL; +} + +static +void network_location_fini( + struct lttng_session_descriptor_network_location *location) +{ + free(location->control); + free(location->data); +} + +/* Assumes ownership of control and data. */ +static +int network_location_set_from_lttng_uris( + struct lttng_session_descriptor_network_location *location, + struct lttng_uri *control, struct lttng_uri *data) +{ + int ret = 0; + + if (!control && !data) { + goto end; + } + + if (!(control && data)) { + /* None or both must be set. */ + ret = -1; + goto end; + } + + if (control->stype != LTTNG_STREAM_CONTROL || + data->stype != LTTNG_STREAM_DATA) { + ret = -1; + goto end; + } + + free(location->control); + free(location->data); + location->control = control; + location->data = data; + control = NULL; + data = NULL; +end: + free(control); + free(data); + return ret; +} + +static +int network_location_set_from_uri_strings( + struct lttng_session_descriptor_network_location *location, + const char *control, const char *data) +{ + int ret = 0; + ssize_t uri_count; + struct lttng_uri *parsed_uris = NULL; + struct lttng_uri *control_uri = NULL; + struct lttng_uri *data_uri = NULL; + + uri_count = uri_parse_str_urls(control, data, &parsed_uris); + if (uri_count != 2 && uri_count != 0) { + ret = -1; + goto end; + } + + /* + * uri_parse_str_urls returns a contiguous array of lttng_uris whereas + * session descriptors expect individually allocated lttng_uris. + */ + if (uri_count == 2) { + control_uri = zmalloc(sizeof(*control_uri)); + data_uri = zmalloc(sizeof(*data_uri)); + if (!control_uri || !data_uri) { + ret = -1; + goto end; + } + memcpy(control_uri, &parsed_uris[0], sizeof(*control_uri)); + memcpy(data_uri, &parsed_uris[1], sizeof(*data_uri)); + } + + /* Ownership of control and data uris is transferred. */ + ret = network_location_set_from_lttng_uris( + location, + control_uri, + data_uri); + control_uri = NULL; + data_uri = NULL; +end: + free(parsed_uris); + free(control_uri); + free(data_uri); + return ret; +} + +struct lttng_session_descriptor * +lttng_session_descriptor_create(const char *name) +{ + struct lttng_session_descriptor *descriptor; + + descriptor = zmalloc(sizeof(*descriptor)); + if (!descriptor) { + goto error; + } + + descriptor->type = LTTNG_SESSION_DESCRIPTOR_TYPE_REGULAR; + descriptor->output_type = + LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE; + if (lttng_session_descriptor_set_session_name(descriptor, name)) { + goto error; + } + return descriptor; +error: + lttng_session_descriptor_destroy(descriptor); + return NULL; +} + +/* Ownership of uri is transferred. */ +static +struct lttng_session_descriptor * +_lttng_session_descriptor_local_create(const char *name, + struct lttng_uri *uri) +{ + struct lttng_session_descriptor *descriptor; + + descriptor = lttng_session_descriptor_create(name); + if (!descriptor) { + goto error; + } + descriptor->type = LTTNG_SESSION_DESCRIPTOR_TYPE_REGULAR; + descriptor->output_type = + LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL; + if (uri) { + if (uri->dtype != LTTNG_DST_PATH) { + goto error; + } + descriptor->output.local = uri; + uri = NULL; + } + return descriptor; +error: + free(uri); + lttng_session_descriptor_destroy(descriptor); + return NULL; +} + +struct lttng_session_descriptor * +lttng_session_descriptor_local_create(const char *name, const char *path) +{ + struct lttng_uri *uri = NULL; + struct lttng_session_descriptor *descriptor; + + if (path) { + uri = uri_from_path(path); + if (!uri) { + goto error; + } + } + descriptor = _lttng_session_descriptor_local_create(name, uri); + return descriptor; +error: + return NULL; +} + +/* Assumes the ownership of both uris. */ +static +struct lttng_session_descriptor * +_lttng_session_descriptor_network_create(const char *name, + struct lttng_uri *control, struct lttng_uri *data) +{ + int ret; + struct lttng_session_descriptor *descriptor; + + descriptor = lttng_session_descriptor_create(name); + if (!descriptor) { + goto error; + } + + descriptor->type = LTTNG_SESSION_DESCRIPTOR_TYPE_REGULAR; + descriptor->output_type = LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK; + /* Assumes the ownership of both uris. */ + ret = network_location_set_from_lttng_uris(&descriptor->output.network, + control, data); + control = NULL; + data = NULL; + if (ret) { + goto error; + } + return descriptor; +error: + lttng_session_descriptor_destroy(descriptor); + free(control); + free(data); + return NULL; +} + +struct lttng_session_descriptor * +lttng_session_descriptor_network_create(const char *name, + const char *control_url, const char *data_url) +{ + int ret; + struct lttng_session_descriptor *descriptor; + + descriptor = _lttng_session_descriptor_network_create(name, + NULL, NULL); + if (!descriptor) { + goto error; + } + + ret = network_location_set_from_uri_strings(&descriptor->output.network, + control_url, data_url); + if (ret) { + goto error; + } + return descriptor; +error: + lttng_session_descriptor_destroy(descriptor); + return NULL; +} + +static +struct lttng_session_descriptor_snapshot * +_lttng_session_descriptor_snapshot_create(const char *name) +{ + struct lttng_session_descriptor_snapshot *descriptor; + + descriptor = zmalloc(sizeof(*descriptor)); + if (!descriptor) { + goto error; + } + + descriptor->base.type = LTTNG_SESSION_DESCRIPTOR_TYPE_SNAPSHOT; + descriptor->base.output_type = + LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE; + if (lttng_session_descriptor_set_session_name(&descriptor->base, + name)) { + goto error; + } + return descriptor; +error: + lttng_session_descriptor_destroy(descriptor ? &descriptor->base : NULL); + return NULL; +} + +/* Ownership of control and data is transferred. */ +static +struct lttng_session_descriptor_snapshot * +_lttng_session_descriptor_snapshot_network_create(const char *name, + struct lttng_uri *control, struct lttng_uri *data) +{ + int ret; + struct lttng_session_descriptor_snapshot *descriptor; + + descriptor = _lttng_session_descriptor_snapshot_create(name); + if (!descriptor) { + goto error; + } + + descriptor->base.output_type = + LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK; + /* Ownership of control and data is transferred. */ + ret = network_location_set_from_lttng_uris( + &descriptor->base.output.network, + control, data); + control = NULL; + data = NULL; + if (ret) { + goto error; + } + return descriptor; +error: + free(control); + free(data); + lttng_session_descriptor_destroy(descriptor ? &descriptor->base : NULL); + return NULL; +} + +struct lttng_session_descriptor * +lttng_session_descriptor_snapshot_create(const char *name) +{ + struct lttng_session_descriptor_snapshot *descriptor; + + descriptor = _lttng_session_descriptor_snapshot_create(name); + return descriptor ? &descriptor->base : NULL; +} + +struct lttng_session_descriptor * +lttng_session_descriptor_snapshot_network_create(const char *name, + const char *control_url, const char *data_url) +{ + int ret; + struct lttng_session_descriptor_snapshot *descriptor; + + descriptor = _lttng_session_descriptor_snapshot_network_create(name, + NULL, NULL); + if (!descriptor) { + goto error; + } + + ret = network_location_set_from_uri_strings( + &descriptor->base.output.network, + control_url, data_url); + if (ret) { + goto error; + } + return &descriptor->base; +error: + lttng_session_descriptor_destroy(descriptor ? &descriptor->base : NULL); + return NULL; +} + +/* Ownership of uri is transferred. */ +static +struct lttng_session_descriptor_snapshot * +_lttng_session_descriptor_snapshot_local_create(const char *name, + struct lttng_uri *uri) +{ + struct lttng_session_descriptor_snapshot *descriptor; + + descriptor = _lttng_session_descriptor_snapshot_create(name); + if (!descriptor) { + goto error; + } + descriptor->base.output_type = + LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL; + if (uri) { + if (uri->dtype != LTTNG_DST_PATH) { + goto error; + } + descriptor->base.output.local = uri; + uri = NULL; + } + return descriptor; +error: + free(uri); + lttng_session_descriptor_destroy(descriptor ? &descriptor->base : NULL); + return NULL; +} + +struct lttng_session_descriptor * +lttng_session_descriptor_snapshot_local_create(const char *name, + const char *path) +{ + struct lttng_uri *path_uri = NULL; + struct lttng_session_descriptor_snapshot *descriptor; + + if (path) { + path_uri = uri_from_path(path); + if (!path_uri) { + goto error; + } + } + descriptor = _lttng_session_descriptor_snapshot_local_create(name, + path_uri); + return descriptor ? &descriptor->base : NULL; +error: + return NULL; +} + +static +struct lttng_session_descriptor_live * +_lttng_session_descriptor_live_create(const char *name, + unsigned long long live_timer_interval_us) +{ + struct lttng_session_descriptor_live *descriptor = NULL; + + if (live_timer_interval_us == 0) { + goto error; + } + descriptor = zmalloc(sizeof(*descriptor)); + if (!descriptor) { + goto error; + } + + descriptor->base.type = LTTNG_SESSION_DESCRIPTOR_TYPE_LIVE; + descriptor->base.output_type = + LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE; + descriptor->live_timer_us = live_timer_interval_us; + if (lttng_session_descriptor_set_session_name(&descriptor->base, + name)) { + goto error; + } + + return descriptor; +error: + lttng_session_descriptor_destroy(descriptor ? &descriptor->base : NULL); + return NULL; +} + +/* Ownership of control and data is transferred. */ +static +struct lttng_session_descriptor_live * +_lttng_session_descriptor_live_network_create( + const char *name, + struct lttng_uri *control, struct lttng_uri *data, + unsigned long long live_timer_interval_us) +{ + int ret; + struct lttng_session_descriptor_live *descriptor; + + descriptor = _lttng_session_descriptor_live_create(name, + live_timer_interval_us); + descriptor->base.output_type = + LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK; + + /* Ownerwhip of control and data is transferred. */ + ret = network_location_set_from_lttng_uris( + &descriptor->base.output.network, + control, data); + control = NULL; + data = NULL; + if (ret) { + goto error; + } + return descriptor; +error: + free(control); + free(data); + lttng_session_descriptor_destroy(descriptor ? &descriptor->base : NULL); + return NULL; +} + +struct lttng_session_descriptor * +lttng_session_descriptor_live_create( + const char *name, + unsigned long long live_timer_us) +{ + struct lttng_session_descriptor_live *descriptor; + + descriptor = _lttng_session_descriptor_live_create(name, live_timer_us); + if (!descriptor) { + goto error; + } + + return descriptor ? &descriptor->base : NULL; +error: + lttng_session_descriptor_destroy(descriptor ? &descriptor->base : NULL); + return NULL; +} + +struct lttng_session_descriptor * +lttng_session_descriptor_live_network_create( + const char *name, + const char *control_url, const char *data_url, + unsigned long long live_timer_us) +{ + int ret; + struct lttng_session_descriptor_live *descriptor; + + descriptor = _lttng_session_descriptor_live_network_create(name, + NULL, NULL, live_timer_us); + if (!descriptor) { + goto error; + } + + ret = network_location_set_from_uri_strings( + &descriptor->base.output.network, + control_url, data_url); + if (ret) { + goto error; + } + return &descriptor->base; +error: + lttng_session_descriptor_destroy(descriptor ? &descriptor->base : NULL); + return NULL; +} + +void lttng_session_descriptor_destroy( + struct lttng_session_descriptor *descriptor) +{ + if (!descriptor) { + return; + } + + switch (descriptor->output_type) { + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE: + break; + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL: + free(descriptor->output.local); + break; + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK: + network_location_fini(&descriptor->output.network); + break; + default: + abort(); + } + + free(descriptor->name); + free(descriptor); +} + +LTTNG_HIDDEN +ssize_t lttng_session_descriptor_create_from_buffer( + const struct lttng_buffer_view *payload, + struct lttng_session_descriptor **descriptor) +{ + int i; + ssize_t offset = 0, ret; + struct lttng_buffer_view current_view; + const char *name = NULL; + const struct lttng_session_descriptor_comm *base_header; + size_t max_expected_uri_count; + uint64_t live_timer_us = 0; + struct lttng_uri *uris[2] = {}; + enum lttng_session_descriptor_type type; + enum lttng_session_descriptor_output_type output_type; + + current_view = lttng_buffer_view_from_view(payload, offset, + sizeof(*base_header)); + base_header = (typeof(base_header)) current_view.data; + if (!base_header) { + ret = -1; + goto end; + } + + switch (base_header->type) { + case LTTNG_SESSION_DESCRIPTOR_TYPE_REGULAR: + case LTTNG_SESSION_DESCRIPTOR_TYPE_SNAPSHOT: + break; + case LTTNG_SESSION_DESCRIPTOR_TYPE_LIVE: + { + const struct lttng_session_descriptor_live_comm *live_header; + + current_view = lttng_buffer_view_from_view(payload, offset, + sizeof(*live_header)); + live_header = (typeof(live_header)) current_view.data; + if (!live_header) { + ret = -1; + goto end; + } + + live_timer_us = live_header->live_timer_us; + break; + } + default: + ret = -1; + goto end; + } + /* type has been validated. */ + type = base_header->type; + + switch (base_header->output_type) { + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE: + max_expected_uri_count = 0; + break; + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL: + max_expected_uri_count = 1; + break; + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK: + max_expected_uri_count = 2; + break; + default: + ret = -1; + goto end; + } + /* output_type has been validated. */ + output_type = base_header->output_type; + + /* Skip after header. */ + offset += current_view.size; + if (!base_header->name_len) { + goto skip_name; + } + + /* Map the name. */ + current_view = lttng_buffer_view_from_view(payload, offset, + base_header->name_len); + name = current_view.data; + if (!name) { + ret = -1; + goto end; + } + + if (base_header->name_len == 1 || + name[base_header->name_len - 1] || + strlen(name) != base_header->name_len - 1) { + /* + * Check that the name is not NULL, is NULL-terminated, and + * does not contain a NULL before the last byte. + */ + ret = -1; + goto end; + } + + /* Skip after the name. */ + offset += base_header->name_len; +skip_name: + if (base_header->uri_count > max_expected_uri_count) { + ret = -1; + goto end; + } + + for (i = 0; i < base_header->uri_count; i++) { + struct lttng_uri *uri; + + /* Map a URI. */ + current_view = lttng_buffer_view_from_view(payload, + offset, sizeof(*uri)); + uri = (typeof(uri)) current_view.data; + if (!uri) { + ret = -1; + goto end; + } + uris[i] = zmalloc(sizeof(*uri)); + if (!uris[i]) { + ret = -1; + goto end; + } + memcpy(uris[i], uri, sizeof(*uri)); + offset += sizeof(*uri); + } + + switch (type) { + case LTTNG_SESSION_DESCRIPTOR_TYPE_REGULAR: + switch (output_type) { + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE: + *descriptor = lttng_session_descriptor_create(name); + break; + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL: + *descriptor = _lttng_session_descriptor_local_create( + name, uris[0]); + break; + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK: + *descriptor = _lttng_session_descriptor_network_create( + name, uris[0], uris[1]); + break; + default: + /* Already checked. */ + abort(); + } + break; + case LTTNG_SESSION_DESCRIPTOR_TYPE_SNAPSHOT: + { + struct lttng_session_descriptor_snapshot *snapshot; + switch (output_type) { + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE: + snapshot = _lttng_session_descriptor_snapshot_create( + name); + break; + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL: + snapshot = _lttng_session_descriptor_snapshot_local_create( + name, uris[0]); + break; + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK: + snapshot = _lttng_session_descriptor_snapshot_network_create( + name, uris[0], uris[1]); + break; + default: + /* Already checked. */ + abort(); + } + *descriptor = snapshot ? &snapshot->base : NULL; + break; + } + case LTTNG_SESSION_DESCRIPTOR_TYPE_LIVE: + { + struct lttng_session_descriptor_live *live; + + switch (output_type) { + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE: + live = _lttng_session_descriptor_live_create( + name, live_timer_us); + break; + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK: + live = _lttng_session_descriptor_live_network_create( + name, uris[0], uris[1], + live_timer_us); + break; + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL: + ret = -1; + goto end; + default: + /* Already checked. */ + abort(); + } + *descriptor = live ? &live->base : NULL; + break; + } + default: + /* Already checked. */ + abort(); + } + memset(uris, 0, sizeof(uris)); + if (!*descriptor) { + ret = -1; + goto end; + } + + ret = offset; +end: + free(uris[0]); + free(uris[1]); + return ret; +} + +LTTNG_HIDDEN +int lttng_session_descriptor_serialize( + const struct lttng_session_descriptor *descriptor, + struct lttng_dynamic_buffer *buffer) +{ + int ret, i; + /* There are, at most, two URIs to serialize. */ + struct lttng_uri *uris[2] = {}; + size_t uri_count = 0; + /* The live header is a superset of all headers. */ + struct lttng_session_descriptor_live_comm header = { + .base.type = (uint8_t) descriptor->type, + .base.output_type = (uint8_t) descriptor->output_type, + .base.name_len = descriptor->name ? + strlen(descriptor->name) + 1 : 0, + }; + const void *header_ptr = NULL; + size_t header_size; + + switch (descriptor->output_type) { + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE: + break; + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL: + uris[0] = descriptor->output.local; + break; + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK: + uris[0] = descriptor->output.network.control; + uris[1] = descriptor->output.network.data; + break; + default: + ret = -1; + goto end; + } + uri_count += !!uris[0]; + uri_count += !!uris[1]; + + header.base.uri_count = uri_count; + if (descriptor->type == LTTNG_SESSION_DESCRIPTOR_TYPE_LIVE) { + const struct lttng_session_descriptor_live *live = + container_of(descriptor, typeof(*live), + base); + + header.live_timer_us = live->live_timer_us; + header_ptr = &header; + header_size = sizeof(header); + } else { + header_ptr = &header.base; + header_size = sizeof(header.base); + } + + ret = lttng_dynamic_buffer_append(buffer, header_ptr, header_size); + if (ret) { + goto end; + } + if (header.base.name_len) { + ret = lttng_dynamic_buffer_append(buffer, descriptor->name, + header.base.name_len); + if (ret) { + goto end; + } + } + + for (i = 0; i < uri_count; i++) { + ret = lttng_dynamic_buffer_append(buffer, uris[i], + sizeof(struct lttng_uri)); + if (ret) { + goto end; + } + } +end: + return ret; +} + +LTTNG_HIDDEN +enum lttng_session_descriptor_type +lttng_session_descriptor_get_type( + const struct lttng_session_descriptor *descriptor) +{ + return descriptor->type; +} + +LTTNG_HIDDEN +enum lttng_session_descriptor_output_type +lttng_session_descriptor_get_output_type( + const struct lttng_session_descriptor *descriptor) +{ + return descriptor->output_type; +} + +LTTNG_HIDDEN +void lttng_session_descriptor_get_local_output_uri( + const struct lttng_session_descriptor *descriptor, + struct lttng_uri *local_uri) +{ + memcpy(local_uri, descriptor->output.local, sizeof(*local_uri)); +} + +LTTNG_HIDDEN +void lttng_session_descriptor_get_network_output_uris( + const struct lttng_session_descriptor *descriptor, + struct lttng_uri *control, + struct lttng_uri *data) +{ + memcpy(control, descriptor->output.network.control, sizeof(*control)); + memcpy(data, descriptor->output.network.data, sizeof(*data)); +} + +LTTNG_HIDDEN +unsigned long long +lttng_session_descriptor_live_get_timer_interval( + const struct lttng_session_descriptor *descriptor) +{ + struct lttng_session_descriptor_live *live; + + live = container_of(descriptor, typeof(*live), base); + return live->live_timer_us; +} + +enum lttng_session_descriptor_status +lttng_session_descriptor_get_session_name( + const struct lttng_session_descriptor *descriptor, + const char **session_name) +{ + enum lttng_session_descriptor_status status; + + if (!descriptor || !session_name) { + status = LTTNG_SESSION_DESCRIPTOR_STATUS_INVALID; + goto end; + } + + *session_name = descriptor->name; + status = descriptor->name ? + LTTNG_SESSION_DESCRIPTOR_STATUS_OK : + LTTNG_SESSION_DESCRIPTOR_STATUS_UNSET; +end: + return status; +} + +LTTNG_HIDDEN +int lttng_session_descriptor_set_session_name( + struct lttng_session_descriptor *descriptor, + const char *name) +{ + int ret = 0; + char *new_name; + + if (!name) { + goto end; + } + if (strlen(name) >= LTTNG_NAME_MAX) { + ret = -1; + goto end; + } + new_name = strdup(name); + if (!new_name) { + ret = -1; + goto end; + } + free(descriptor->name); + descriptor->name = new_name; +end: + return ret; +} + +LTTNG_HIDDEN +bool lttng_session_descriptor_is_output_destination_initialized( + const struct lttng_session_descriptor *descriptor) +{ + switch (descriptor->output_type) { + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE: + return true; + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL: + return descriptor->output.local; + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK: + return descriptor->output.network.control; + default: + abort(); + } +} + +LTTNG_HIDDEN +bool lttng_session_descriptor_has_output_directory( + const struct lttng_session_descriptor *descriptor) +{ + switch (descriptor->output_type) { + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE: + break; + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL: + if (descriptor->output.local) { + return *descriptor->output.local->dst.path; + } + break; + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK: + if (descriptor->output.network.control) { + return *descriptor->output.network.control->subdir; + } + break; + default: + abort(); + } + return false; +} + +LTTNG_HIDDEN +enum lttng_error_code lttng_session_descriptor_set_default_output( + struct lttng_session_descriptor *descriptor, + time_t *session_creation_time, + const char *absolute_home_path) +{ + enum lttng_error_code ret_code = LTTNG_OK; + struct lttng_uri *uris = NULL; + + switch (descriptor->output_type) { + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE: + goto end; + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL: + { + int ret; + ssize_t uri_ret; + char local_uri[LTTNG_PATH_MAX]; + char creation_datetime_suffix[17] = {}; + + if (session_creation_time) { + size_t strftime_ret; + struct tm *timeinfo; + + timeinfo = localtime(session_creation_time); + if (!timeinfo) { + ret_code = LTTNG_ERR_FATAL; + goto end; + } + strftime_ret = strftime(creation_datetime_suffix, + sizeof(creation_datetime_suffix), + "-%Y%m%d-%H%M%S", timeinfo); + if (strftime_ret == 0) { + ERR("Failed to format session creation timestamp while setting default local output destination"); + ret_code = LTTNG_ERR_FATAL; + goto end; + } + } + assert(descriptor->name); + ret = snprintf(local_uri, sizeof(local_uri), + "file://%s/%s/%s%s", + absolute_home_path, + DEFAULT_TRACE_DIR_NAME, descriptor->name, + creation_datetime_suffix); + if (ret >= sizeof(local_uri)) { + ERR("Truncation occurred while setting default local output destination"); + ret_code = LTTNG_ERR_SET_URL; + goto end; + } else if (ret < 0) { + PERROR("Failed to format default local output URI"); + ret_code = LTTNG_ERR_SET_URL; + goto end; + } + + uri_ret = uri_parse(local_uri, &uris); + if (uri_ret != 1) { + ret_code = LTTNG_ERR_SET_URL; + goto end; + } + free(descriptor->output.local); + descriptor->output.local = &uris[0]; + uris = NULL; + break; + } + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK: + { + int ret; + ssize_t uri_ret; + struct lttng_uri *control = NULL, *data = NULL; + + uri_ret = uri_parse_str_urls("net://127.0.0.1", NULL, &uris); + if (uri_ret != 2) { + ret_code = LTTNG_ERR_SET_URL; + goto end; + } + + control = uri_copy(&uris[0]); + data = uri_copy(&uris[1]); + if (!control || !data) { + free(control); + free(data); + ret_code = LTTNG_ERR_SET_URL; + goto end; + } + + /* Ownership of uris is transferred. */ + ret = network_location_set_from_lttng_uris( + &descriptor->output.network, + control, data); + if (ret) { + abort(); + ret_code = LTTNG_ERR_SET_URL; + goto end; + } + break; + } + default: + abort(); + } +end: + free(uris); + return ret_code; +} + +/* + * Note that only properties that can be populated by the session daemon + * (output destination and name) are assigned. + */ +LTTNG_HIDDEN +int lttng_session_descriptor_assign( + struct lttng_session_descriptor *dst, + const struct lttng_session_descriptor *src) +{ + int ret = 0; + + if (dst->type != src->type) { + ret = -1; + goto end; + } + if (dst->output_type != src->output_type) { + ret = -1; + goto end; + } + ret = lttng_session_descriptor_set_session_name(dst, src->name); + if (ret) { + goto end; + } + switch (dst->output_type) { + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL: + free(dst->output.local); + dst->output.local = uri_copy(src->output.local); + if (!dst->output.local) { + ret = -1; + goto end; + } + break; + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK: + { + struct lttng_uri *control_copy = NULL, *data_copy = NULL; + + control_copy = uri_copy(dst->output.network.control); + if (!control_copy && dst->output.network.control) { + ret = -1; + goto end; + } + data_copy = uri_copy(dst->output.network.data); + if (!data_copy && dst->output.network.data) { + free(control_copy); + ret = -1; + goto end; + } + ret = network_location_set_from_lttng_uris(&dst->output.network, + control_copy, data_copy); + break; + } + case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE: + goto end; + } +end: + return ret; +} diff --git a/src/common/sessiond-comm/sessiond-comm.h b/src/common/sessiond-comm/sessiond-comm.h index c0c6a8bba..836c3af66 100644 --- a/src/common/sessiond-comm/sessiond-comm.h +++ b/src/common/sessiond-comm/sessiond-comm.h @@ -68,7 +68,7 @@ enum lttcomm_sessiond_command { LTTNG_ENABLE_EVENT = 6, /* 7 */ /* Session daemon command */ - LTTNG_CREATE_SESSION = 8, + /* 8 */ LTTNG_DESTROY_SESSION = 9, LTTNG_LIST_CHANNELS = 10, LTTNG_LIST_DOMAINS = 11, @@ -91,8 +91,8 @@ enum lttcomm_sessiond_command { LTTNG_SNAPSHOT_DEL_OUTPUT = 26, LTTNG_SNAPSHOT_LIST_OUTPUT = 27, LTTNG_SNAPSHOT_RECORD = 28, - LTTNG_CREATE_SESSION_SNAPSHOT = 29, - LTTNG_CREATE_SESSION_LIVE = 30, + /* 29 */ + /* 30 */ LTTNG_SAVE_SESSION = 31, LTTNG_TRACK_PID = 32, LTTNG_UNTRACK_PID = 33, @@ -106,6 +106,7 @@ enum lttcomm_sessiond_command { LTTNG_ROTATION_GET_INFO = 46, LTTNG_ROTATION_SET_SCHEDULE = 47, LTTNG_SESSION_LIST_ROTATION_SCHEDULES = 48, + LTTNG_CREATE_SESSION_EXT = 49 }; enum lttcomm_relayd_command { @@ -356,10 +357,22 @@ struct lttcomm_session_msg { uint8_t set; uint64_t value; } LTTNG_PACKED rotation_set_schedule; + struct { + /* + * Includes the null-terminator. + * Must be an absolute path. + * + * Size bounded by LTTNG_PATH_MAX. + */ + uint16_t home_dir_size; + uint64_t session_descriptor_size; + /* An lttng_session_descriptor follows. */ + } LTTNG_PACKED create_session; } u; } LTTNG_PACKED; #define LTTNG_FILTER_MAX_LEN 65536 +#define LTTNG_SESSION_DESCRIPTOR_MAX_LEN 65536 /* * Filter bytecode data. The reloc table is located at the end of the diff --git a/src/lib/lttng-ctl/lttng-ctl.c b/src/lib/lttng-ctl/lttng-ctl.c index 165fef4dc..bbd9e8575 100644 --- a/src/lib/lttng-ctl/lttng-ctl.c +++ b/src/lib/lttng-ctl/lttng-ctl.c @@ -43,6 +43,8 @@ #include #include #include +#include +#include #include "filter/filter-ast.h" #include "filter/filter-parser.h" @@ -1748,8 +1750,89 @@ const char *lttng_strerror(int code) return error_get_str(code); } +enum lttng_error_code lttng_create_session_ext( + struct lttng_session_descriptor *session_descriptor) +{ + enum lttng_error_code ret_code; + struct lttcomm_session_msg lsm = { + .cmd_type = LTTNG_CREATE_SESSION_EXT, + }; + void *reply = NULL; + struct lttng_buffer_view reply_view; + int reply_ret; + bool sessiond_must_generate_ouput; + struct lttng_dynamic_buffer payload; + int ret; + size_t descriptor_size; + struct lttng_session_descriptor *descriptor_reply = NULL; + + lttng_dynamic_buffer_init(&payload); + if (!session_descriptor) { + ret_code = LTTNG_ERR_INVALID; + goto end; + } + + sessiond_must_generate_ouput = + !lttng_session_descriptor_is_output_destination_initialized( + session_descriptor); + if (sessiond_must_generate_ouput) { + const char *home_dir = utils_get_home_dir(); + size_t home_dir_len = home_dir ? strlen(home_dir) + 1 : 0; + + if (!home_dir || home_dir_len > LTTNG_PATH_MAX) { + ret_code = LTTNG_ERR_FATAL; + goto end; + } + + lsm.u.create_session.home_dir_size = (uint16_t) home_dir_len; + ret = lttng_dynamic_buffer_append(&payload, home_dir, + home_dir_len); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + } + + descriptor_size = payload.size; + ret = lttng_session_descriptor_serialize(session_descriptor, + &payload); + if (ret) { + ret_code = LTTNG_ERR_INVALID; + goto end; + } + descriptor_size = payload.size - descriptor_size; + lsm.u.create_session.session_descriptor_size = descriptor_size; + + /* Command returns a session descriptor on success. */ + reply_ret = lttng_ctl_ask_sessiond_varlen_no_cmd_header(&lsm, payload.data, + payload.size, &reply); + if (reply_ret < 0) { + ret_code = -reply_ret; + goto end; + } else if (reply_ret == 0) { + /* Socket unexpectedly closed by the session daemon. */ + ret_code = LTTNG_ERR_FATAL; + goto end; + } + + reply_view = lttng_buffer_view_init(reply, 0, reply_ret); + ret = lttng_session_descriptor_create_from_buffer(&reply_view, + &descriptor_reply); + if (ret < 0) { + ret_code = LTTNG_ERR_FATAL; + goto end; + } + ret_code = LTTNG_OK; + lttng_session_descriptor_assign(session_descriptor, descriptor_reply); +end: + free(reply); + lttng_dynamic_buffer_reset(&payload); + lttng_session_descriptor_destroy(descriptor_reply); + return ret_code; +} + /* - * Create a brand new session using name and url for destination. + * Create a new session using name and url for destination. * * Returns LTTNG_OK on success or a negative error code. */ @@ -1757,33 +1840,163 @@ int lttng_create_session(const char *name, const char *url) { int ret; ssize_t size; - struct lttcomm_session_msg lsm; struct lttng_uri *uris = NULL; + struct lttng_session_descriptor *descriptor = NULL; + enum lttng_error_code ret_code; - if (name == NULL) { - return -LTTNG_ERR_INVALID; + if (!name) { + ret = -LTTNG_ERR_INVALID; + goto end; } - memset(&lsm, 0, sizeof(lsm)); - - lsm.cmd_type = LTTNG_CREATE_SESSION; - lttng_ctl_copy_string(lsm.session.name, name, sizeof(lsm.session.name)); - - /* There should never be a data URL */ size = uri_parse_str_urls(url, NULL, &uris); if (size < 0) { - return -LTTNG_ERR_INVALID; + ret = -LTTNG_ERR_INVALID; + goto end; + } + switch (size) { + case 0: + descriptor = lttng_session_descriptor_create(name); + break; + case 1: + if (uris[0].dtype != LTTNG_DST_PATH) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + descriptor = lttng_session_descriptor_local_create(name, + uris[0].dst.path); + break; + case 2: + descriptor = lttng_session_descriptor_network_create(name, url, + NULL); + break; + default: + ret = -LTTNG_ERR_INVALID; + goto end; + } + if (!descriptor) { + ret = -LTTNG_ERR_INVALID; + goto end; } + ret_code = lttng_create_session_ext(descriptor); + ret = ret_code == LTTNG_OK ? 0 : -ret_code; +end: + lttng_session_descriptor_destroy(descriptor); + free(uris); + return ret; +} - lsm.u.uri.size = size; +/* + * Create a session exclusively used for snapshot. + * + * Returns LTTNG_OK on success or a negative error code. + */ +int lttng_create_session_snapshot(const char *name, const char *snapshot_url) +{ + int ret; + enum lttng_error_code ret_code; + ssize_t size; + struct lttng_uri *uris = NULL; + struct lttng_session_descriptor *descriptor = NULL; - ret = lttng_ctl_ask_sessiond_varlen_no_cmd_header(&lsm, uris, - sizeof(struct lttng_uri) * size, NULL); + if (!name) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + size = uri_parse_str_urls(snapshot_url, NULL, &uris); + if (size < 0) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + /* + * If the user does not specify a custom subdir, use the session name. + */ + if (size > 0 && uris[0].dtype != LTTNG_DST_PATH && + strlen(uris[0].subdir) == 0) { + ret = snprintf(uris[0].subdir, sizeof(uris[0].subdir), "%s", + name); + if (ret < 0) { + PERROR("Failed to set session name as network destination sub-directory"); + ret = -LTTNG_ERR_FATAL; + goto end; + } else if (ret >= sizeof(uris[0].subdir)) { + /* Truncated output. */ + ret = -LTTNG_ERR_INVALID; + goto end; + } + } + switch (size) { + case 0: + descriptor = lttng_session_descriptor_snapshot_create(name); + break; + case 1: + if (uris[0].dtype != LTTNG_DST_PATH) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + descriptor = lttng_session_descriptor_snapshot_local_create( + name, + uris[0].dst.path); + break; + case 2: + descriptor = lttng_session_descriptor_snapshot_network_create( + name, + snapshot_url, + NULL); + break; + default: + ret = -LTTNG_ERR_INVALID; + goto end; + } + if (!descriptor) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + ret_code = lttng_create_session_ext(descriptor); + ret = ret_code == LTTNG_OK ? 0 : -ret_code; +end: + lttng_session_descriptor_destroy(descriptor); free(uris); return ret; } +/* + * Create a session exclusively used for live. + * + * Returns LTTNG_OK on success or a negative error code. + */ +int lttng_create_session_live(const char *name, const char *url, + unsigned int timer_interval) +{ + int ret; + enum lttng_error_code ret_code; + struct lttng_session_descriptor *descriptor = NULL; + + if (!name) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + if (url) { + descriptor = lttng_session_descriptor_live_network_create( + name, url, NULL, timer_interval); + } else { + descriptor = lttng_session_descriptor_live_create( + name, timer_interval); + } + if (!descriptor) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + ret_code = lttng_create_session_ext(descriptor); + ret = ret_code == LTTNG_OK ? 0 : -ret_code; +end: + lttng_session_descriptor_destroy(descriptor); + return ret; +} + /* * Destroy session using name. * Returns size of returned session payload data or a negative error code. @@ -1854,19 +2067,73 @@ end: * Returns the number of lttng_session entries in sessions; * on error, returns a negative value. */ -int lttng_list_sessions(struct lttng_session **sessions) +int lttng_list_sessions(struct lttng_session **out_sessions) { int ret; struct lttcomm_session_msg lsm; + const size_t session_size = sizeof(struct lttng_session) + + sizeof(struct lttng_session_extended); + size_t session_count, i; + struct lttng_session_extended *sessions_extended_begin; + struct lttng_session *sessions = NULL; memset(&lsm, 0, sizeof(lsm)); lsm.cmd_type = LTTNG_LIST_SESSIONS; - ret = lttng_ctl_ask_sessiond(&lsm, (void**) sessions); - if (ret < 0) { - return ret; + ret = lttng_ctl_ask_sessiond(&lsm, (void**) &sessions); + if (ret <= 0) { + ret = ret == 0 ? -LTTNG_ERR_FATAL : ret; + goto end; + } + if (!sessions) { + ret = -LTTNG_ERR_FATAL; + goto end; + } + + if (ret % session_size) { + ret = -LTTNG_ERR_UNK; + free(sessions); + *out_sessions = NULL; + goto end; } + session_count = (size_t) ret / session_size; + sessions_extended_begin = (struct lttng_session_extended *) + (&sessions[session_count]); - return ret / sizeof(struct lttng_session); + /* Set extended session info pointers. */ + for (i = 0; i < session_count; i++) { + struct lttng_session *session = &sessions[i]; + struct lttng_session_extended *extended = + &(sessions_extended_begin[i]); + + session->extended.ptr = extended; + } + + ret = (int) session_count; + *out_sessions = sessions; +end: + return ret; +} + +enum lttng_error_code lttng_session_get_creation_time( + const struct lttng_session *session, uint64_t *creation_time) +{ + enum lttng_error_code ret = LTTNG_OK; + struct lttng_session_extended *extended; + + if (!session || !creation_time || !session->extended.ptr) { + ret = LTTNG_ERR_INVALID; + goto end; + } + + extended = session->extended.ptr; + if (!extended->creation_time.is_set) { + /* Not created on the session daemon yet. */ + ret = LTTNG_ERR_SESSION_NOT_EXIST; + goto end; + } + *creation_time = extended->creation_time.value; +end: + return ret; } int lttng_set_session_shm_path(const char *session_name, @@ -2565,71 +2832,12 @@ int lttng_disable_consumer(struct lttng_handle *handle) } /* - * This is an extension of create session that is ONLY and SHOULD only be used - * by the lttng command line program. It exists to avoid using URI parsing in - * the lttng client. - * - * We need the date and time for the trace path subdirectory for the case where - * the user does NOT define one using either -o or -U. Using the normal - * lttng_create_session API call, we have no clue on the session daemon side if - * the URL was generated automatically by the client or define by the user. - * - * So this function "wrapper" is hidden from the public API, takes the datetime - * string and appends it if necessary to the URI subdirectory before sending it - * to the session daemon. - * - * With this extra function, the lttng_create_session call behavior is not - * changed and the timestamp is appended to the URI on the session daemon side - * if necessary. + * [OBSOLETE] */ int _lttng_create_session_ext(const char *name, const char *url, const char *datetime) { - int ret; - ssize_t size; - struct lttcomm_session_msg lsm; - struct lttng_uri *uris = NULL; - - if (name == NULL || datetime == NULL) { - return -LTTNG_ERR_INVALID; - } - - memset(&lsm, 0, sizeof(lsm)); - - lsm.cmd_type = LTTNG_CREATE_SESSION; - lttng_ctl_copy_string(lsm.session.name, name, sizeof(lsm.session.name)); - - /* There should never be a data URL. */ - size = uri_parse_str_urls(url, NULL, &uris); - if (size < 0) { - ret = -LTTNG_ERR_INVALID; - goto error; - } - - lsm.u.uri.size = size; - - if (size > 0 && uris[0].dtype != LTTNG_DST_PATH && strlen(uris[0].subdir) == 0) { - /* Don't append datetime if the name was automatically created. */ - if (strncmp(name, DEFAULT_SESSION_NAME "-", - strlen(DEFAULT_SESSION_NAME) + 1)) { - ret = snprintf(uris[0].subdir, sizeof(uris[0].subdir), "%s-%s", - name, datetime); - } else { - ret = snprintf(uris[0].subdir, sizeof(uris[0].subdir), "%s", name); - } - if (ret < 0) { - PERROR("snprintf uri subdir"); - ret = -LTTNG_ERR_FATAL; - goto error; - } - } - - ret = lttng_ctl_ask_sessiond_varlen_no_cmd_header(&lsm, uris, - sizeof(struct lttng_uri) * size, NULL); - -error: - free(uris); - return ret; + return -ENOSYS; } /* @@ -2668,103 +2876,6 @@ end: return ret; } -/* - * Create a session exclusively used for snapshot. - * - * Returns LTTNG_OK on success or a negative error code. - */ -int lttng_create_session_snapshot(const char *name, const char *snapshot_url) -{ - int ret; - ssize_t size; - struct lttcomm_session_msg lsm; - struct lttng_uri *uris = NULL; - - if (name == NULL) { - return -LTTNG_ERR_INVALID; - } - - memset(&lsm, 0, sizeof(lsm)); - - lsm.cmd_type = LTTNG_CREATE_SESSION_SNAPSHOT; - lttng_ctl_copy_string(lsm.session.name, name, sizeof(lsm.session.name)); - - size = uri_parse_str_urls(snapshot_url, NULL, &uris); - if (size < 0) { - return -LTTNG_ERR_INVALID; - } - - lsm.u.uri.size = size; - - /* - * If the user does not specify a custom subdir, use the session name. - */ - if (size > 0 && uris[0].dtype != LTTNG_DST_PATH && strlen(uris[0].subdir) == 0) { - ret = snprintf(uris[0].subdir, sizeof(uris[0].subdir), "%s", name); - if (ret < 0) { - PERROR("snprintf uri subdir"); - ret = -LTTNG_ERR_FATAL; - goto error; - } - } - - ret = lttng_ctl_ask_sessiond_varlen_no_cmd_header(&lsm, uris, - sizeof(struct lttng_uri) * size, NULL); - -error: - free(uris); - return ret; -} - -/* - * Create a session exclusively used for live. - * - * Returns LTTNG_OK on success or a negative error code. - */ -int lttng_create_session_live(const char *name, const char *url, - unsigned int timer_interval) -{ - int ret; - ssize_t size; - struct lttcomm_session_msg lsm; - struct lttng_uri *uris = NULL; - - if (name == NULL || timer_interval == 0) { - return -LTTNG_ERR_INVALID; - } - - memset(&lsm, 0, sizeof(lsm)); - - lsm.cmd_type = LTTNG_CREATE_SESSION_LIVE; - lttng_ctl_copy_string(lsm.session.name, name, sizeof(lsm.session.name)); - - if (url) { - size = uri_parse_str_urls(url, NULL, &uris); - if (size <= 0) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - /* file:// is not accepted for live session. */ - if (uris[0].dtype == LTTNG_DST_PATH) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - } else { - size = 0; - } - - lsm.u.session_live.nb_uri = size; - lsm.u.session_live.timer_interval = timer_interval; - - ret = lttng_ctl_ask_sessiond_varlen_no_cmd_header(&lsm, uris, - sizeof(struct lttng_uri) * size, NULL); - -end: - free(uris); - return ret; -} - /* * List PIDs in the tracker. * diff --git a/tests/regression/tools/snapshots/test_kernel_streaming b/tests/regression/tools/snapshots/test_kernel_streaming index 0b535c612..3fa37eb1a 100755 --- a/tests/regression/tools/snapshots/test_kernel_streaming +++ b/tests/regression/tools/snapshots/test_kernel_streaming @@ -55,7 +55,7 @@ function test_kernel_default_name_with_del() lttng_snapshot_record $SESSION_NAME # Validate test - validate_trace $EVENT_NAME $TRACE_PATH/$HOSTNAME/snapshot-1* + validate_trace $EVENT_NAME $TRACE_PATH/$HOSTNAME/$SESSION_NAME*/snapshot-1* if [ $? -ne 0 ]; then return $? fi @@ -65,7 +65,7 @@ function test_kernel_default_name_with_del() lttng_snapshot_record $SESSION_NAME # Validate test with the next ID since a del output was done prior. - validate_trace $EVENT_NAME $TRACE_PATH/$HOSTNAME/snapshot-2* + validate_trace $EVENT_NAME $TRACE_PATH/$HOSTNAME/$SESSION_NAME*/snapshot-2* if [ $? -ne 0 ]; then return $? fi @@ -89,7 +89,7 @@ function test_kernel_default_name() stop_lttng_tracing_ok $SESSION_NAME destroy_lttng_session_ok $SESSION_NAME # Validate test - validate_trace $EVENT_NAME $TRACE_PATH/$HOSTNAME/snapshot-1* + validate_trace $EVENT_NAME $TRACE_PATH/$HOSTNAME/$SESSION_NAME*/snapshot-1* out=$? return $out @@ -111,10 +111,10 @@ function test_kernel_custom_name() stop_lttng_tracing_ok $SESSION_NAME destroy_lttng_session_ok $SESSION_NAME - if ls $TRACE_PATH/$HOSTNAME/$name* &> /dev/null; then + if ls $TRACE_PATH/$HOSTNAME/$SESSION_NAME*/$name* &> /dev/null; then ok 0 "Custom name snapshot exists" # Validate test - validate_trace $EVENT_NAME $TRACE_PATH/$HOSTNAME/$name-* + validate_trace $EVENT_NAME $TRACE_PATH/$HOSTNAME/$SESSION_NAME*/$name-* out=$? else fail "No custom name snapshot found" @@ -137,7 +137,7 @@ function test_kernel_n_snapshot() start_lttng_tracing_ok $SESSION_NAME lttng_snapshot_record $SESSION_NAME stop_lttng_tracing_ok $SESSION_NAME - validate_trace $EVENT_NAME $TRACE_PATH/$HOSTNAME/snapshot-1* + validate_trace $EVENT_NAME $TRACE_PATH/$HOSTNAME/$SESSION_NAME*/snapshot-1* if [ $? -ne 0 ]; then return 1 fi diff --git a/tests/regression/tools/snapshots/test_ust_streaming b/tests/regression/tools/snapshots/test_ust_streaming index 20d4f7f6b..eecd0253b 100755 --- a/tests/regression/tools/snapshots/test_ust_streaming +++ b/tests/regression/tools/snapshots/test_ust_streaming @@ -99,8 +99,7 @@ function test_ust_default_name_with_del() lttng_snapshot_record $SESSION_NAME # Validate test - echo $TRACE_PATH/$HOSTNAME/snapshot-1 - validate_trace $EVENT_NAME $TRACE_PATH/$HOSTNAME/snapshot-1* + validate_trace $EVENT_NAME $TRACE_PATH/$HOSTNAME/$SESSION_NAME*/snapshot-1* if [ $? -ne 0 ]; then stop_test_apps return $? @@ -111,7 +110,7 @@ function test_ust_default_name_with_del() lttng_snapshot_record $SESSION_NAME # Validate test with the next ID since a del output was done prior. - validate_trace $EVENT_NAME $TRACE_PATH/$HOSTNAME/snapshot-2* + validate_trace $EVENT_NAME $TRACE_PATH/$HOSTNAME/$SESSION_NAME*/snapshot-2* if [ $? -ne 0 ]; then stop_test_apps return $? @@ -141,7 +140,7 @@ function test_ust_default_name() stop_lttng_tracing_ok $SESSION_NAME destroy_lttng_session_ok $SESSION_NAME # Validate test - validate_trace $EVENT_NAME $TRACE_PATH/$HOSTNAME/snapshot-1* + validate_trace $EVENT_NAME $TRACE_PATH/$HOSTNAME/$SESSION_NAME*/snapshot-1* out=$? stop_test_apps @@ -164,7 +163,7 @@ function test_ust_default_name_custom_uri() stop_lttng_tracing_ok $SESSION_NAME destroy_lttng_session_ok $SESSION_NAME # Validate test - validate_trace $EVENT_NAME $TRACE_PATH/$HOSTNAME/snapshot-1* + validate_trace $EVENT_NAME $TRACE_PATH/$HOSTNAME/$SESSION_NAME*/snapshot-1* out=$? stop_test_apps @@ -191,10 +190,10 @@ function test_ust_custom_name() stop_lttng_tracing_ok $SESSION_NAME destroy_lttng_session_ok $SESSION_NAME - if ls $TRACE_PATH/$HOSTNAME/$name* &> /dev/null; then + if ls $TRACE_PATH/$HOSTNAME/$SESSION_NAME*/$name* &> /dev/null; then ok 0 "Custom name snapshot exists" # Validate test - validate_trace $EVENT_NAME $TRACE_PATH/$HOSTNAME/$name-* + validate_trace $EVENT_NAME $TRACE_PATH/$HOSTNAME/$SESSION_NAME*/$name-* out=$? else fail "No custom name snapshot found" @@ -219,7 +218,7 @@ function test_ust_n_snapshot() start_lttng_tracing_ok $SESSION_NAME lttng_snapshot_record $SESSION_NAME stop_lttng_tracing_ok $SESSION_NAME - validate_trace $EVENT_NAME $TRACE_PATH/$HOSTNAME/snapshot-1* + validate_trace $EVENT_NAME $TRACE_PATH/$HOSTNAME/$SESSION_NAME*/snapshot-1* if [ $? -ne 0 ]; then return 1 fi diff --git a/tests/unit/test_session.c b/tests/unit/test_session.c index 5db1e758f..fa3e367d2 100644 --- a/tests/unit/test_session.c +++ b/tests/unit/test_session.c @@ -128,27 +128,36 @@ static void empty_session_list(void) static int create_one_session(char *name) { int ret; + enum lttng_error_code ret_code; + struct ltt_session *session = NULL; - ret = session_create(name, geteuid(), getegid()); - if (ret == LTTNG_OK) { + session_lock_list(); + ret_code = session_create(name, geteuid(), getegid(), &session); + session_put(session); + if (ret_code == LTTNG_OK) { /* Validate */ ret = find_session_name(name); if (ret < 0) { /* Session not found by name */ printf("session not found after creation\n"); - return -1; + ret = -1; + goto end; } else { /* Success */ - return 0; + ret = 0; + goto end; } } else { - if (ret == LTTNG_ERR_EXIST_SESS) { + if (ret_code == LTTNG_ERR_EXIST_SESS) { printf("(session already exists) "); } - return -1; + ret = -1; + goto end; } - - return 0; + ret = 0; +end: + session_unlock_list(); + return ret; } /* @@ -266,13 +275,29 @@ void test_duplicate_session(void) "Duplicate session creation"); } -void test_bogus_session_param(void) +void test_session_name_generation(void) { - ok(create_one_session(NULL) < 0, - "Create session with bogus param: NULL should fail"); + struct ltt_session *session = NULL; + enum lttng_error_code ret_code; + const char *expected_session_name_prefix = DEFAULT_SESSION_NAME; - ok(session_list_count() == 0, - "Create session with bogus param: session list empty"); + session_lock_list(); + ret_code = session_create(NULL, geteuid(), getegid(), &session); + ok(ret_code == LTTNG_OK, + "Create session with a NULL name (auto-generate a name)"); + if (!session) { + skip(1, "Skipping session name generation tests as session_create() failed."); + goto end; + } + diag("Automatically-generated session name: %s", *session->name ? + session->name : "ERROR"); + ok(*session->name && !strncmp(expected_session_name_prefix, session->name, + sizeof(DEFAULT_SESSION_NAME) - 1), + "Auto-generated session name starts with %s", + DEFAULT_SESSION_NAME); +end: + session_put(session); + session_unlock_list(); } void test_large_session_number(void) @@ -340,7 +365,7 @@ int main(int argc, char **argv) empty_session_list(); - test_bogus_session_param(); + test_session_name_generation(); test_large_session_number(); -- 2.34.1