Add mkdir_recursive function to libcommon 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
29 #include <common/common.h>
30
31 #include "utils.h"
32
33 /*
34 * Return the realpath(3) of the path even if the last directory token does not
35 * exist. For example, with /tmp/test1/test2, if test2/ does not exist but the
36 * /tmp/test1 does, the real path is returned. In normal time, realpath(3)
37 * fails if the end point directory does not exist.
38 */
39 __attribute__((visibility("hidden")))
40 char *utils_expand_path(const char *path)
41 {
42 const char *end_path = path;
43 char *next, *cut_path = NULL, *expanded_path = NULL;
44
45 /* Safety net */
46 if (path == NULL) {
47 goto error;
48 }
49
50 /* Find last token delimited by '/' */
51 while ((next = strpbrk(end_path + 1, "/"))) {
52 end_path = next;
53 }
54
55 /* Cut last token from original path */
56 cut_path = strndup(path, end_path - path);
57
58 expanded_path = zmalloc(PATH_MAX);
59 if (expanded_path == NULL) {
60 PERROR("zmalloc expand path");
61 goto error;
62 }
63
64 expanded_path = realpath((char *)cut_path, expanded_path);
65 if (expanded_path == NULL) {
66 switch (errno) {
67 case ENOENT:
68 ERR("%s: No such file or directory", cut_path);
69 break;
70 default:
71 PERROR("realpath utils expand path");
72 break;
73 }
74 goto error;
75 }
76
77 /* Add end part to expanded path */
78 strncat(expanded_path, end_path, PATH_MAX - strlen(expanded_path) - 1);
79
80 free(cut_path);
81 return expanded_path;
82
83 error:
84 free(expanded_path);
85 free(cut_path);
86 return NULL;
87 }
88
89 /*
90 * Create a pipe in dst.
91 */
92 __attribute__((visibility("hidden")))
93 int utils_create_pipe(int *dst)
94 {
95 int ret;
96
97 if (dst == NULL) {
98 return -1;
99 }
100
101 ret = pipe(dst);
102 if (ret < 0) {
103 PERROR("create pipe");
104 }
105
106 return ret;
107 }
108
109 /*
110 * Create pipe and set CLOEXEC flag to both fd.
111 *
112 * Make sure the pipe opened by this function are closed at some point. Use
113 * utils_close_pipe().
114 */
115 __attribute__((visibility("hidden")))
116 int utils_create_pipe_cloexec(int *dst)
117 {
118 int ret, i;
119
120 if (dst == NULL) {
121 return -1;
122 }
123
124 ret = utils_create_pipe(dst);
125 if (ret < 0) {
126 goto error;
127 }
128
129 for (i = 0; i < 2; i++) {
130 ret = fcntl(dst[i], F_SETFD, FD_CLOEXEC);
131 if (ret < 0) {
132 PERROR("fcntl pipe cloexec");
133 goto error;
134 }
135 }
136
137 error:
138 return ret;
139 }
140
141 /*
142 * Close both read and write side of the pipe.
143 */
144 __attribute__((visibility("hidden")))
145 void utils_close_pipe(int *src)
146 {
147 int i, ret;
148
149 if (src == NULL) {
150 return;
151 }
152
153 for (i = 0; i < 2; i++) {
154 /* Safety check */
155 if (src[i] < 0) {
156 continue;
157 }
158
159 ret = close(src[i]);
160 if (ret) {
161 PERROR("close pipe");
162 }
163 }
164 }
165
166 /*
167 * Create a new string using two strings range.
168 */
169 __attribute__((visibility("hidden")))
170 char *utils_strdupdelim(const char *begin, const char *end)
171 {
172 char *str;
173
174 str = zmalloc(end - begin + 1);
175 if (str == NULL) {
176 PERROR("zmalloc strdupdelim");
177 goto error;
178 }
179
180 memcpy(str, begin, end - begin);
181 str[end - begin] = '\0';
182
183 error:
184 return str;
185 }
186
187 /*
188 * Set CLOEXEC flag to the give file descriptor.
189 */
190 __attribute__((visibility("hidden")))
191 int utils_set_fd_cloexec(int fd)
192 {
193 int ret;
194
195 if (fd < 0) {
196 ret = -EINVAL;
197 goto end;
198 }
199
200 ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
201 if (ret < 0) {
202 PERROR("fcntl cloexec");
203 ret = -errno;
204 }
205
206 end:
207 return ret;
208 }
209
210 /*
211 * Create pid file to the given path and filename.
212 */
213 __attribute__((visibility("hidden")))
214 int utils_create_pid_file(pid_t pid, const char *filepath)
215 {
216 int ret;
217 FILE *fp;
218
219 assert(filepath);
220
221 fp = fopen(filepath, "w");
222 if (fp == NULL) {
223 PERROR("open pid file %s", filepath);
224 ret = -1;
225 goto error;
226 }
227
228 ret = fprintf(fp, "%d\n", pid);
229 if (ret < 0) {
230 PERROR("fprintf pid file");
231 }
232
233 fclose(fp);
234 DBG("Pid %d written in file %s", pid, filepath);
235 error:
236 return ret;
237 }
238
239 /*
240 * Recursively create directory using the given path and mode.
241 *
242 * On success, return 0 else a negative error code.
243 */
244 __attribute__((visibility("hidden")))
245 int utils_mkdir_recursive(const char *path, mode_t mode)
246 {
247 char *p, tmp[PATH_MAX];
248 struct stat statbuf;
249 size_t len;
250 int ret;
251
252 assert(path);
253
254 ret = snprintf(tmp, sizeof(tmp), "%s", path);
255 if (ret < 0) {
256 PERROR("snprintf mkdir");
257 goto error;
258 }
259
260 len = ret;
261 if (tmp[len - 1] == '/') {
262 tmp[len - 1] = 0;
263 }
264
265 for (p = tmp + 1; *p; p++) {
266 if (*p == '/') {
267 *p = 0;
268 if (tmp[strlen(tmp) - 1] == '.' &&
269 tmp[strlen(tmp) - 2] == '.' &&
270 tmp[strlen(tmp) - 3] == '/') {
271 ERR("Using '/../' is not permitted in the trace path (%s)",
272 tmp);
273 ret = -1;
274 goto error;
275 }
276 ret = stat(tmp, &statbuf);
277 if (ret < 0) {
278 ret = mkdir(tmp, mode);
279 if (ret < 0) {
280 if (errno != EEXIST) {
281 PERROR("mkdir recursive");
282 ret = -errno;
283 goto error;
284 }
285 }
286 }
287 *p = '/';
288 }
289 }
290
291 ret = mkdir(tmp, mode);
292 if (ret < 0) {
293 if (errno != EEXIST) {
294 PERROR("mkdir recursive last piece");
295 ret = -errno;
296 } else {
297 ret = 0;
298 }
299 }
300
301 error:
302 return ret;
303 }
This page took 0.041284 seconds and 6 git commands to generate.