Implement run_as wrappers for mkdir/mkdir_recursive/open
[lttng-tools.git] / lttng-sessiond / utils.c
1 /*
2 * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
3 * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; only version 2 of the License.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16 * Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19 #define _GNU_SOURCE
20 #include <errno.h>
21 #include <limits.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/wait.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30
31 #include <lttngerr.h>
32
33 #include "utils.h"
34
35 struct mkdir_data {
36 const char *path;
37 mode_t mode;
38 };
39
40 struct open_data {
41 const char *path;
42 int flags;
43 mode_t mode;
44 };
45
46 /*
47 * Write to writable pipe used to notify a thread.
48 */
49 int notify_thread_pipe(int wpipe)
50 {
51 int ret;
52
53 ret = write(wpipe, "!", 1);
54 if (ret < 0) {
55 PERROR("write poll pipe");
56 }
57
58 return ret;
59 }
60
61 /*
62 * Return pointer to home directory path using the env variable HOME.
63 *
64 * No home, NULL is returned.
65 */
66 const char *get_home_dir(void)
67 {
68 return ((const char *) getenv("HOME"));
69 }
70
71 /*
72 * Create recursively directory using the FULL path.
73 */
74 static
75 int _mkdir_recursive(void *_data)
76 {
77 struct mkdir_data *data = _data;
78 const char *path;
79 char *p, tmp[PATH_MAX];
80 struct stat statbuf;
81 mode_t mode;
82 size_t len;
83 int ret;
84
85 path = data->path;
86 mode = data->mode;
87
88 ret = snprintf(tmp, sizeof(tmp), "%s", path);
89 if (ret < 0) {
90 PERROR("snprintf mkdir");
91 goto error;
92 }
93
94 len = ret;
95 if (tmp[len - 1] == '/') {
96 tmp[len - 1] = 0;
97 }
98
99 for (p = tmp + 1; *p; p++) {
100 if (*p == '/') {
101 *p = 0;
102 ret = stat(tmp, &statbuf);
103 if (ret < 0) {
104 ret = mkdir(tmp, mode);
105 if (ret < 0) {
106 if (!(errno == EEXIST)) {
107 PERROR("mkdir recursive");
108 ret = -errno;
109 goto error;
110 }
111 }
112 }
113 *p = '/';
114 }
115 }
116
117 ret = mkdir(tmp, mode);
118 if (ret < 0) {
119 if (!(errno == EEXIST)) {
120 PERROR("mkdir recursive last piece");
121 ret = -errno;
122 } else {
123 ret = 0;
124 }
125 }
126
127 error:
128 return ret;
129 }
130
131 static
132 int _mkdir(void *_data)
133 {
134 struct mkdir_data *data = _data;
135 return mkdir(data->path, data->mode);
136 }
137
138 static
139 int _open(void *_data)
140 {
141 struct open_data *data = _data;
142 return open(data->path, data->flags, data->mode);
143 }
144
145 static
146 int run_as(int (*cmd)(void *data), void *data, uid_t uid, gid_t gid)
147 {
148 int ret = 0;
149 pid_t pid;
150
151 /*
152 * If we are non-root, we can only deal with our own uid.
153 */
154 if (geteuid() != 0) {
155 if (uid != geteuid()) {
156 ERR("Client (%d)/Server (%d) UID mismatch (and sessiond is not root)",
157 uid, geteuid());
158 return -EPERM;
159 }
160 return (*cmd)(data);
161 }
162
163 pid = fork();
164 if (pid > 0) {
165 int status;
166
167 /*
168 * Parent: wait for child to return, in which case the
169 * shared memory map will have been created.
170 */
171 pid = wait(&status);
172 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
173 ret = -1;
174 goto end;
175 }
176 goto end;
177 } else if (pid == 0) {
178 /* Child */
179 setegid(gid);
180 if (ret < 0) {
181 perror("setegid");
182 exit(EXIT_FAILURE);
183 }
184 ret = seteuid(uid);
185 if (ret < 0) {
186 perror("seteuid");
187 exit(EXIT_FAILURE);
188 }
189 umask(0);
190 ret = (*cmd)(data);
191 if (!ret)
192 exit(EXIT_SUCCESS);
193 else
194 exit(EXIT_FAILURE);
195 } else {
196 return -1;
197 }
198 end:
199 return ret;
200 }
201
202 int mkdir_recursive_run_as(const char *path, mode_t mode, uid_t uid, gid_t gid)
203 {
204 struct mkdir_data data;
205
206 DBG3("mkdir() recursive %s with mode %d for uid %d and gid %d",
207 path, mode, uid, gid);
208 data.path = path;
209 data.mode = mode;
210 return run_as(_mkdir_recursive, &data, uid, gid);
211 }
212
213 int mkdir_run_as(const char *path, mode_t mode, uid_t uid, gid_t gid)
214 {
215 struct mkdir_data data;
216
217 DBG3("mkdir() %s with mode %d for uid %d and gid %d",
218 path, mode, uid, gid);
219 data.path = path;
220 data.mode = mode;
221 return run_as(_mkdir, &data, uid, gid);
222 }
223
224 int open_run_as(const char *path, int flags, mode_t mode, uid_t uid, gid_t gid)
225 {
226 struct open_data data;
227
228 DBG3("open() %s with flags %d mode %d for uid %d and gid %d",
229 path, flags, mode, uid, gid);
230 data.path = path;
231 data.flags = flags;
232 data.mode = mode;
233 return run_as(_open, &data, uid, gid);
234 }
This page took 0.03305 seconds and 4 git commands to generate.