Rotate command
[lttng-tools.git] / src / bin / lttng-sessiond / rotate.c
1 /*
2 * Copyright (C) 2017 - Julien Desfossez <jdesfossez@efficios.com>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License, version 2 only, as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License along with
14 * this program; if not, write to the Free Software Foundation, Inc., 51
15 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 */
17
18 #define _LGPL_SOURCE
19 #include <lttng/trigger/trigger.h>
20 #include <common/error.h>
21 #include <common/config/session-config.h>
22 #include <common/defaults.h>
23 #include <common/utils.h>
24 #include <common/futex.h>
25 #include <common/align.h>
26 #include <common/time.h>
27 #include <common/hashtable/utils.h>
28 #include <common/kernel-ctl/kernel-ctl.h>
29 #include <sys/eventfd.h>
30 #include <sys/stat.h>
31 #include <time.h>
32 #include <signal.h>
33 #include <inttypes.h>
34
35 #include "session.h"
36 #include "rotate.h"
37 #include "rotation-thread.h"
38 #include "lttng-sessiond.h"
39 #include "health-sessiond.h"
40 #include "cmd.h"
41 #include "utils.h"
42
43 #include <urcu.h>
44 #include <urcu/list.h>
45 #include <urcu/rculfhash.h>
46
47 unsigned long hash_channel_key(struct rotation_channel_key *key)
48 {
49 return hash_key_u64(&key->key, lttng_ht_seed) ^ hash_key_ulong(
50 (void *) (unsigned long) key->domain, lttng_ht_seed);
51 }
52
53 int rotate_add_channel_pending(uint64_t key, enum lttng_domain_type domain,
54 struct ltt_session *session)
55 {
56 int ret;
57 struct rotation_channel_info *new_info;
58 struct rotation_channel_key channel_key = { .key = key,
59 .domain = domain };
60
61 new_info = zmalloc(sizeof(struct rotation_channel_info));
62 if (!new_info) {
63 goto error;
64 }
65
66 new_info->channel_key.key = key;
67 new_info->channel_key.domain = domain;
68 new_info->session_id = session->id;
69 cds_lfht_node_init(&new_info->rotate_channels_ht_node);
70
71 session->nr_chan_rotate_pending++;
72 cds_lfht_add(channel_pending_rotate_ht,
73 hash_channel_key(&channel_key),
74 &new_info->rotate_channels_ht_node);
75
76 ret = 0;
77 goto end;
78
79 error:
80 ret = -1;
81 end:
82 return ret;
83 }
84
85 /* The session's lock must be held by the caller. */
86 static
87 int session_rename_chunk(struct ltt_session *session, char *current_path,
88 char *new_path)
89 {
90 int ret;
91 struct consumer_socket *socket;
92 struct consumer_output *output;
93 struct lttng_ht_iter iter;
94 uid_t uid;
95 gid_t gid;
96
97 DBG("Renaming session chunk path of session \"%s\" from %s to %s",
98 session->name, current_path, new_path);
99
100 /*
101 * Either one of the sessions is enough to find the consumer_output
102 * and uid/gid.
103 */
104 if (session->kernel_session) {
105 output = session->kernel_session->consumer;
106 uid = session->kernel_session->uid;
107 gid = session->kernel_session->gid;
108 } else if (session->ust_session) {
109 output = session->ust_session->consumer;
110 uid = session->ust_session->uid;
111 gid = session->ust_session->gid;
112 } else {
113 assert(0);
114 }
115
116 if (!output || !output->socks) {
117 ERR("No consumer output found for session \"%s\"",
118 session->name);
119 ret = -1;
120 goto end;
121 }
122
123 rcu_read_lock();
124 /*
125 * We have to iterate to find a socket, but we only need to send the
126 * rename command to one consumer, so we break after the first one.
127 */
128 cds_lfht_for_each_entry(output->socks->ht, &iter.iter, socket, node.node) {
129 pthread_mutex_lock(socket->lock);
130 ret = consumer_rotate_rename(socket, session->id, output,
131 current_path, new_path, uid, gid);
132 pthread_mutex_unlock(socket->lock);
133 if (ret) {
134 ret = -1;
135 goto end_unlock;
136 }
137 break;
138 }
139
140 ret = 0;
141
142 end_unlock:
143 rcu_read_unlock();
144 end:
145 return ret;
146 }
147
148 /* The session's lock must be held by the caller. */
149 static
150 int rename_first_chunk(struct ltt_session *session,
151 struct consumer_output *consumer, char *new_path)
152 {
153 int ret;
154 char current_full_path[LTTNG_PATH_MAX], new_full_path[LTTNG_PATH_MAX];
155
156 /* Current domain path: <session>/kernel */
157 if (session->net_handle > 0) {
158 ret = snprintf(current_full_path, sizeof(current_full_path), "%s/%s",
159 consumer->dst.net.base_dir, consumer->subdir);
160 if (ret < 0 || ret >= sizeof(current_full_path)) {
161 ERR("Failed to initialize current full path while renaming first rotation chunk of session \"%s\"",
162 session->name);
163 ret = -1;
164 goto error;
165 }
166 } else {
167 ret = snprintf(current_full_path, sizeof(current_full_path), "%s/%s",
168 consumer->dst.session_root_path, consumer->subdir);
169 if (ret < 0 || ret >= sizeof(current_full_path)) {
170 ERR("Failed to initialize current full path while renaming first rotation chunk of session \"%s\"",
171 session->name);
172 ret = -1;
173 goto error;
174 }
175 }
176 /* New domain path: <session>/<start-date>-<end-date>-<rotate-count>/kernel */
177 ret = snprintf(new_full_path, sizeof(new_full_path), "%s/%s",
178 new_path, consumer->subdir);
179 if (ret < 0 || ret >= sizeof(new_full_path)) {
180 ERR("Failed to initialize new full path while renaming first rotation chunk of session \"%s\"",
181 session->name);
182 ret = -1;
183 goto error;
184 }
185 /*
186 * Move the per-domain fcurrenter inside the first rotation
187 * fcurrenter.
188 */
189 ret = session_rename_chunk(session, current_full_path, new_full_path);
190 if (ret < 0) {
191 ret = -LTTNG_ERR_UNK;
192 goto error;
193 }
194
195 ret = 0;
196
197 error:
198 return ret;
199 }
200
201 /*
202 * Rename a chunk folder after a rotation is complete.
203 * session_lock_list and session lock must be held.
204 *
205 * Returns 0 on success, a negative value on error.
206 */
207 int rename_complete_chunk(struct ltt_session *session, time_t ts)
208 {
209 struct tm *timeinfo;
210 char datetime[16], start_datetime[16];
211 char new_path[LTTNG_PATH_MAX];
212 int ret;
213 size_t strf_ret;
214
215 DBG("Renaming completed chunk for session %s", session->name);
216 timeinfo = localtime(&ts);
217 if (!timeinfo) {
218 ERR("Failed to retrieve local time while renaming completed chunk");
219 ret = -1;
220 goto end;
221 }
222 strf_ret = strftime(datetime, sizeof(datetime), "%Y%m%d-%H%M%S",
223 timeinfo);
224 if (strf_ret == 0) {
225 ERR("Failed to format timestamp while renaming completed session chunk");
226 ret = -1;
227 goto end;
228 }
229
230 if (session->rotate_count == 1) {
231 char start_time[16];
232
233 timeinfo = localtime(&session->last_chunk_start_ts);
234 if (!timeinfo) {
235 ERR("Failed to retrieve local time while renaming completed chunk");
236 ret = -1;
237 goto end;
238 }
239
240 strf_ret = strftime(start_time, sizeof(start_time),
241 "%Y%m%d-%H%M%S", timeinfo);
242 if (strf_ret == 0) {
243 ERR("Failed to format timestamp while renaming completed session chunk");
244 ret = -1;
245 goto end;
246 }
247
248 /*
249 * On the first rotation, the current_rotate_path is the
250 * session_root_path, so we need to create the chunk folder
251 * and move the domain-specific folders inside it.
252 */
253 ret = snprintf(new_path, sizeof(new_path), "%s/%s-%s-%" PRIu64,
254 session->rotation_chunk.current_rotate_path,
255 start_time,
256 datetime, session->rotate_count);
257 if (ret < 0 || ret >= sizeof(new_path)) {
258 ERR("Failed to format new chunk path while renaming session \"%s\"'s first chunk",
259 session->name);
260 ret = -1;
261 goto end;
262 }
263
264 if (session->kernel_session) {
265 ret = rename_first_chunk(session,
266 session->kernel_session->consumer,
267 new_path);
268 if (ret) {
269 ERR("Failed to rename kernel session trace folder to %s", new_path);
270 /*
271 * This is not a fatal error for the rotation
272 * thread, we just need to inform the client
273 * that a problem occurred with the rotation.
274 * Returning 0, same for the other errors
275 * below.
276 */
277 ret = 0;
278 goto error;
279 }
280 }
281 if (session->ust_session) {
282 ret = rename_first_chunk(session,
283 session->ust_session->consumer,
284 new_path);
285 if (ret) {
286 ERR("Failed to rename userspace session trace folder to %s", new_path);
287 ret = 0;
288 goto error;
289 }
290 }
291 } else {
292 /*
293 * After the first rotation, all the trace data is already in
294 * its own chunk folder, we just need to append the suffix.
295 */
296 /* Recreate the session->rotation_chunk.current_rotate_path */
297 timeinfo = localtime(&session->last_chunk_start_ts);
298 if (!timeinfo) {
299 ERR("Failed to retrieve local time while renaming completed chunk");
300 ret = -1;
301 goto end;
302 }
303 strf_ret = strftime(start_datetime, sizeof(start_datetime), "%Y%m%d-%H%M%S", timeinfo);
304 if (!strf_ret) {
305 ERR("Failed to format timestamp while renaming completed session chunk");
306 ret = -1;
307 goto end;
308 }
309 ret = snprintf(new_path, sizeof(new_path), "%s/%s-%s-%" PRIu64,
310 session_get_base_path(session),
311 start_datetime,
312 datetime, session->rotate_count);
313 if (ret < 0 || ret >= sizeof(new_path)) {
314 ERR("Failed to format new chunk path while renaming chunk of session \"%s\"",
315 session->name);
316 ret = -1;
317 goto error;
318 }
319 ret = session_rename_chunk(session,
320 session->rotation_chunk.current_rotate_path,
321 new_path);
322 if (ret) {
323 ERR("Failed to rename session trace folder from %s to %s",
324 session->rotation_chunk.current_rotate_path,
325 new_path);
326 ret = 0;
327 goto error;
328 }
329 }
330
331 /*
332 * Store the path where the readable chunk is. This path is valid
333 * and can be queried by the client with rotate_pending until the next
334 * rotation is started.
335 */
336 ret = lttng_strncpy(session->rotation_chunk.current_rotate_path,
337 new_path,
338 sizeof(session->rotation_chunk.current_rotate_path));
339 if (ret) {
340 ERR("Failed the current chunk's path of session \"%s\"",
341 session->name);
342 ret = -1;
343 goto error;
344 }
345
346 goto end;
347
348 error:
349 session->rotation_status = LTTNG_ROTATION_STATUS_ERROR;
350 end:
351 return ret;
352 }
This page took 0.035896 seconds and 4 git commands to generate.