Move stream file rotation functions to utils
[lttng-tools.git] / src / common / utils.c
1 /*
2 * Copyright (C) 2012 - David Goulet <dgoulet@efficios.com>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License, version 2 only, as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License along with
14 * this program; if not, write to the Free Software Foundation, Inc., 51
15 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 */
17
18 #define _GNU_SOURCE
19 #include <assert.h>
20 #include <ctype.h>
21 #include <fcntl.h>
22 #include <limits.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 #include <inttypes.h>
29
30 #include <common/common.h>
31 #include <common/runas.h>
32
33 #include "utils.h"
34
35 /*
36 * Return the realpath(3) of the path even if the last directory token does not
37 * exist. For example, with /tmp/test1/test2, if test2/ does not exist but the
38 * /tmp/test1 does, the real path is returned. In normal time, realpath(3)
39 * fails if the end point directory does not exist.
40 */
41 LTTNG_HIDDEN
42 char *utils_expand_path(const char *path)
43 {
44 const char *end_path = path;
45 char *next, *cut_path = NULL, *expanded_path = NULL;
46
47 /* Safety net */
48 if (path == NULL) {
49 goto error;
50 }
51
52 /* Find last token delimited by '/' */
53 while ((next = strpbrk(end_path + 1, "/"))) {
54 end_path = next;
55 }
56
57 /* Cut last token from original path */
58 cut_path = strndup(path, end_path - path);
59
60 expanded_path = zmalloc(PATH_MAX);
61 if (expanded_path == NULL) {
62 PERROR("zmalloc expand path");
63 goto error;
64 }
65
66 expanded_path = realpath((char *)cut_path, expanded_path);
67 if (expanded_path == NULL) {
68 switch (errno) {
69 case ENOENT:
70 ERR("%s: No such file or directory", cut_path);
71 break;
72 default:
73 PERROR("realpath utils expand path");
74 break;
75 }
76 goto error;
77 }
78
79 /* Add end part to expanded path */
80 strncat(expanded_path, end_path, PATH_MAX - strlen(expanded_path) - 1);
81
82 free(cut_path);
83 return expanded_path;
84
85 error:
86 free(expanded_path);
87 free(cut_path);
88 return NULL;
89 }
90
91 /*
92 * Create a pipe in dst.
93 */
94 LTTNG_HIDDEN
95 int utils_create_pipe(int *dst)
96 {
97 int ret;
98
99 if (dst == NULL) {
100 return -1;
101 }
102
103 ret = pipe(dst);
104 if (ret < 0) {
105 PERROR("create pipe");
106 }
107
108 return ret;
109 }
110
111 /*
112 * Create pipe and set CLOEXEC flag to both fd.
113 *
114 * Make sure the pipe opened by this function are closed at some point. Use
115 * utils_close_pipe().
116 */
117 LTTNG_HIDDEN
118 int utils_create_pipe_cloexec(int *dst)
119 {
120 int ret, i;
121
122 if (dst == NULL) {
123 return -1;
124 }
125
126 ret = utils_create_pipe(dst);
127 if (ret < 0) {
128 goto error;
129 }
130
131 for (i = 0; i < 2; i++) {
132 ret = fcntl(dst[i], F_SETFD, FD_CLOEXEC);
133 if (ret < 0) {
134 PERROR("fcntl pipe cloexec");
135 goto error;
136 }
137 }
138
139 error:
140 return ret;
141 }
142
143 /*
144 * Close both read and write side of the pipe.
145 */
146 LTTNG_HIDDEN
147 void utils_close_pipe(int *src)
148 {
149 int i, ret;
150
151 if (src == NULL) {
152 return;
153 }
154
155 for (i = 0; i < 2; i++) {
156 /* Safety check */
157 if (src[i] < 0) {
158 continue;
159 }
160
161 ret = close(src[i]);
162 if (ret) {
163 PERROR("close pipe");
164 }
165 }
166 }
167
168 /*
169 * Create a new string using two strings range.
170 */
171 LTTNG_HIDDEN
172 char *utils_strdupdelim(const char *begin, const char *end)
173 {
174 char *str;
175
176 str = zmalloc(end - begin + 1);
177 if (str == NULL) {
178 PERROR("zmalloc strdupdelim");
179 goto error;
180 }
181
182 memcpy(str, begin, end - begin);
183 str[end - begin] = '\0';
184
185 error:
186 return str;
187 }
188
189 /*
190 * Set CLOEXEC flag to the give file descriptor.
191 */
192 LTTNG_HIDDEN
193 int utils_set_fd_cloexec(int fd)
194 {
195 int ret;
196
197 if (fd < 0) {
198 ret = -EINVAL;
199 goto end;
200 }
201
202 ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
203 if (ret < 0) {
204 PERROR("fcntl cloexec");
205 ret = -errno;
206 }
207
208 end:
209 return ret;
210 }
211
212 /*
213 * Create pid file to the given path and filename.
214 */
215 LTTNG_HIDDEN
216 int utils_create_pid_file(pid_t pid, const char *filepath)
217 {
218 int ret;
219 FILE *fp;
220
221 assert(filepath);
222
223 fp = fopen(filepath, "w");
224 if (fp == NULL) {
225 PERROR("open pid file %s", filepath);
226 ret = -1;
227 goto error;
228 }
229
230 ret = fprintf(fp, "%d\n", pid);
231 if (ret < 0) {
232 PERROR("fprintf pid file");
233 }
234
235 fclose(fp);
236 DBG("Pid %d written in file %s", pid, filepath);
237 error:
238 return ret;
239 }
240
241 /*
242 * Recursively create directory using the given path and mode.
243 *
244 * On success, return 0 else a negative error code.
245 */
246 LTTNG_HIDDEN
247 int utils_mkdir_recursive(const char *path, mode_t mode)
248 {
249 char *p, tmp[PATH_MAX];
250 struct stat statbuf;
251 size_t len;
252 int ret;
253
254 assert(path);
255
256 ret = snprintf(tmp, sizeof(tmp), "%s", path);
257 if (ret < 0) {
258 PERROR("snprintf mkdir");
259 goto error;
260 }
261
262 len = ret;
263 if (tmp[len - 1] == '/') {
264 tmp[len - 1] = 0;
265 }
266
267 for (p = tmp + 1; *p; p++) {
268 if (*p == '/') {
269 *p = 0;
270 if (tmp[strlen(tmp) - 1] == '.' &&
271 tmp[strlen(tmp) - 2] == '.' &&
272 tmp[strlen(tmp) - 3] == '/') {
273 ERR("Using '/../' is not permitted in the trace path (%s)",
274 tmp);
275 ret = -1;
276 goto error;
277 }
278 ret = stat(tmp, &statbuf);
279 if (ret < 0) {
280 ret = mkdir(tmp, mode);
281 if (ret < 0) {
282 if (errno != EEXIST) {
283 PERROR("mkdir recursive");
284 ret = -errno;
285 goto error;
286 }
287 }
288 }
289 *p = '/';
290 }
291 }
292
293 ret = mkdir(tmp, mode);
294 if (ret < 0) {
295 if (errno != EEXIST) {
296 PERROR("mkdir recursive last piece");
297 ret = -errno;
298 } else {
299 ret = 0;
300 }
301 }
302
303 error:
304 return ret;
305 }
306
307 /*
308 * Create the stream tracefile on disk.
309 *
310 * Return 0 on success or else a negative value.
311 */
312 int utils_create_stream_file(char *path_name, char *file_name, uint64_t size,
313 uint64_t count, int uid, int gid)
314 {
315 int ret, out_fd;
316 char full_path[PATH_MAX], *path_name_id = NULL, *path;
317
318 assert(path_name);
319 assert(file_name);
320
321 ret = snprintf(full_path, sizeof(full_path), "%s/%s",
322 path_name, file_name);
323 if (ret < 0) {
324 PERROR("snprintf create output file");
325 goto error;
326 }
327
328 /*
329 * If we split the trace in multiple files, we have to add the count at the
330 * end of the tracefile name
331 */
332 if (size > 0) {
333 ret = asprintf(&path_name_id, "%s_%" PRIu64, full_path, count);
334 if (ret < 0) {
335 PERROR("Allocating path name ID");
336 goto error;
337 }
338 path = path_name_id;
339 } else {
340 path = full_path;
341 }
342
343 out_fd = run_as_open(path, O_WRONLY | O_CREAT | O_TRUNC,
344 S_IRWXU | S_IRWXG | S_IRWXO, uid, gid);
345 if (out_fd < 0) {
346 PERROR("open stream path %s", path);
347 goto error_open;
348 }
349 ret = out_fd;
350
351 error_open:
352 free(path_name_id);
353 error:
354 return ret;
355 }
356
357 /*
358 * Change the output tracefile according to the given size and count The
359 * new_count pointer is set during this operation.
360 *
361 * From the consumer, the stream lock MUST be held before calling this function
362 * because we are modifying the stream status.
363 *
364 * Return 0 on success or else a negative value.
365 */
366 int utils_rotate_stream_file(char *path_name, char *file_name, uint64_t size,
367 uint64_t count, int uid, int gid, int out_fd, uint64_t *new_count)
368 {
369 int ret;
370
371 ret = close(out_fd);
372 if (ret < 0) {
373 PERROR("Closing tracefile");
374 goto error;
375 }
376
377 if (count > 0) {
378 *new_count = (*new_count + 1) % count;
379 } else {
380 (*new_count)++;
381 }
382
383 return utils_create_stream_file(path_name, file_name, size, *new_count,
384 uid, gid);
385 error:
386 return ret;
387 }
This page took 0.055321 seconds and 5 git commands to generate.