Commit | Line | Data |
---|---|---|
81b86775 DG |
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 | |
35f90c40 | 19 | #include <assert.h> |
81b86775 DG |
20 | #include <ctype.h> |
21 | #include <fcntl.h> | |
22 | #include <limits.h> | |
23 | #include <stdlib.h> | |
24 | #include <string.h> | |
2d851108 DG |
25 | #include <sys/types.h> |
26 | #include <sys/stat.h> | |
27 | #include <unistd.h> | |
fe4477ee | 28 | #include <inttypes.h> |
81b86775 DG |
29 | |
30 | #include <common/common.h> | |
fe4477ee | 31 | #include <common/runas.h> |
81b86775 DG |
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 | */ | |
90e535ef | 41 | LTTNG_HIDDEN |
81b86775 DG |
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 */ | |
c30ce0b3 | 80 | strncat(expanded_path, end_path, PATH_MAX - strlen(expanded_path) - 1); |
81b86775 DG |
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 | */ | |
90e535ef | 94 | LTTNG_HIDDEN |
81b86775 DG |
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 | */ | |
90e535ef | 117 | LTTNG_HIDDEN |
81b86775 DG |
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 | */ | |
90e535ef | 146 | LTTNG_HIDDEN |
81b86775 DG |
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 | } | |
a4b92340 DG |
167 | |
168 | /* | |
169 | * Create a new string using two strings range. | |
170 | */ | |
90e535ef | 171 | LTTNG_HIDDEN |
a4b92340 DG |
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 | } | |
b662582b DG |
188 | |
189 | /* | |
190 | * Set CLOEXEC flag to the give file descriptor. | |
191 | */ | |
90e535ef | 192 | LTTNG_HIDDEN |
b662582b DG |
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 | } | |
35f90c40 DG |
211 | |
212 | /* | |
213 | * Create pid file to the given path and filename. | |
214 | */ | |
90e535ef | 215 | LTTNG_HIDDEN |
35f90c40 DG |
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 | } | |
2d851108 DG |
240 | |
241 | /* | |
242 | * Recursively create directory using the given path and mode. | |
243 | * | |
244 | * On success, return 0 else a negative error code. | |
245 | */ | |
90e535ef | 246 | LTTNG_HIDDEN |
2d851108 DG |
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 | } | |
fe4477ee JD |
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 | ||
0f907de1 | 343 | /* Open with 660 mode */ |
fe4477ee | 344 | out_fd = run_as_open(path, O_WRONLY | O_CREAT | O_TRUNC, |
0f907de1 | 345 | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, uid, gid); |
fe4477ee JD |
346 | if (out_fd < 0) { |
347 | PERROR("open stream path %s", path); | |
348 | goto error_open; | |
349 | } | |
350 | ret = out_fd; | |
351 | ||
352 | error_open: | |
353 | free(path_name_id); | |
354 | error: | |
355 | return ret; | |
356 | } | |
357 | ||
358 | /* | |
359 | * Change the output tracefile according to the given size and count The | |
360 | * new_count pointer is set during this operation. | |
361 | * | |
362 | * From the consumer, the stream lock MUST be held before calling this function | |
363 | * because we are modifying the stream status. | |
364 | * | |
365 | * Return 0 on success or else a negative value. | |
366 | */ | |
367 | int utils_rotate_stream_file(char *path_name, char *file_name, uint64_t size, | |
368 | uint64_t count, int uid, int gid, int out_fd, uint64_t *new_count) | |
369 | { | |
370 | int ret; | |
371 | ||
372 | ret = close(out_fd); | |
373 | if (ret < 0) { | |
374 | PERROR("Closing tracefile"); | |
375 | goto error; | |
376 | } | |
377 | ||
378 | if (count > 0) { | |
379 | *new_count = (*new_count + 1) % count; | |
380 | } else { | |
381 | (*new_count)++; | |
382 | } | |
383 | ||
384 | return utils_create_stream_file(path_name, file_name, size, *new_count, | |
385 | uid, gid); | |
386 | error: | |
387 | return ret; | |
388 | } |