Commit | Line | Data |
---|---|---|
0fdd1e2c DG |
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 <fcntl.h> | |
21 | #include <limits.h> | |
22 | #include <sys/mman.h> | |
23 | #include <sys/stat.h> | |
24 | #include <sys/types.h> | |
25 | #include <sys/wait.h> | |
26 | #include <unistd.h> | |
27 | #include <urcu.h> | |
28 | ||
29 | #include <lttngerr.h> | |
30 | ||
31 | #include "shm.h" | |
32 | ||
33 | /* | |
34 | * Using fork to set umask in the child process (not multi-thread safe). We | |
35 | * deal with the shm_open vs ftruncate race (happening when the sessiond owns | |
36 | * the shm and does not let everybody modify it, to ensure safety against | |
37 | * shm_unlink) by simply letting the mmap fail and retrying after a few | |
38 | * seconds. For global shm, everybody has rw access to it until the sessiond | |
39 | * starts. | |
40 | */ | |
41 | static int get_wait_shm(char *shm_path, size_t mmap_size, int global) | |
42 | { | |
43 | int wait_shm_fd, ret; | |
44 | pid_t pid; | |
45 | mode_t mode; | |
46 | ||
47 | /* Default permissions */ | |
48 | mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; | |
49 | ||
50 | /* Change owner of the shm path */ | |
51 | if (global) { | |
52 | ret = chown(shm_path, 0, 0); | |
53 | if (ret < 0) { | |
54 | if (errno != ENOENT) { | |
55 | perror("chown wait shm"); | |
56 | goto error; | |
57 | } | |
58 | } | |
59 | ||
60 | /* | |
61 | * If global session daemon, any application can register so the shm | |
62 | * needs to be set in read-only mode for others. | |
63 | */ | |
64 | mode |= S_IROTH; | |
65 | } else { | |
66 | ret = chown(shm_path, getuid(), getgid()); | |
67 | if (ret < 0) { | |
68 | if (errno != ENOENT) { | |
69 | perror("chown wait shm"); | |
70 | goto error; | |
71 | } | |
72 | } | |
73 | } | |
74 | ||
75 | /* | |
76 | * Set permissions to the shm even if we did not create the shm. | |
77 | */ | |
78 | ret = chmod(shm_path, mode); | |
79 | if (ret < 0) { | |
80 | if (errno != ENOENT) { | |
81 | perror("chmod wait shm"); | |
82 | goto error; | |
83 | } | |
84 | } | |
85 | ||
86 | /* | |
87 | * If the open failed because the file did not exist, try creating it | |
88 | * ourself. | |
89 | */ | |
90 | pid = fork(); | |
91 | if (pid > 0) { | |
92 | int status; | |
93 | /* | |
94 | * Parent: wait for child to return, in which case the shared memory | |
95 | * map will have been created. | |
96 | */ | |
97 | pid = wait(&status); | |
98 | if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { | |
99 | goto error; | |
100 | } | |
101 | ||
102 | /* | |
103 | * Try to open read-only again after creation. | |
104 | */ | |
105 | wait_shm_fd = shm_open(shm_path, O_RDWR, 0); | |
106 | if (wait_shm_fd < 0) { | |
107 | /* | |
108 | * Real-only open did not work. It's a failure that prohibits using | |
109 | * shm. | |
110 | */ | |
111 | ERR("Error opening shm %s", shm_path); | |
112 | goto error; | |
113 | } | |
114 | goto end; | |
115 | } else if (pid == 0) { | |
116 | /* | |
117 | * We're alone in a child process, so we can modify the process-wide | |
118 | * umask. | |
119 | */ | |
120 | umask(~mode); | |
121 | ||
122 | /* | |
123 | * Try creating shm (or get rw access). We don't do an exclusive open, | |
124 | * because we allow other processes to create+ftruncate it | |
125 | * concurrently. | |
126 | */ | |
127 | wait_shm_fd = shm_open(shm_path, O_RDWR | O_CREAT, mode); | |
128 | if (wait_shm_fd >= 0) { | |
129 | ret = ftruncate(wait_shm_fd, mmap_size); | |
130 | if (ret < 0) { | |
131 | perror("ftruncate wait shm"); | |
132 | exit(EXIT_FAILURE); | |
133 | } | |
134 | ||
135 | ret = fchmod(wait_shm_fd, mode); | |
136 | if (ret < 0) { | |
137 | perror("fchmod"); | |
138 | exit(EXIT_FAILURE); | |
139 | } | |
140 | exit(EXIT_SUCCESS); | |
141 | } | |
142 | ERR("Error opening shm %s", shm_path); | |
143 | exit(EXIT_FAILURE); | |
144 | } else { | |
145 | return -1; | |
146 | } | |
147 | ||
148 | end: | |
149 | DBG("Got the wait shm fd %d", wait_shm_fd); | |
150 | ||
151 | return wait_shm_fd; | |
152 | ||
153 | error: | |
154 | DBG("Failing to get the wait shm fd"); | |
155 | ||
156 | return -1; | |
157 | } | |
158 | ||
159 | /* | |
160 | * Return the wait shm mmap for UST application notification. The global | |
161 | * variable is used to indicate if the the session daemon is global | |
162 | * (root:tracing) or running with an unprivileged user. | |
163 | * | |
164 | * This returned value is used by futex_wait_update() in futex.c to WAKE all | |
165 | * waiters which are UST application waiting for a session daemon. | |
166 | */ | |
167 | char *shm_ust_get_mmap(char *shm_path, int global) | |
168 | { | |
169 | size_t mmap_size = sysconf(_SC_PAGE_SIZE); | |
170 | int wait_shm_fd, ret; | |
171 | char *wait_shm_mmap; | |
172 | ||
173 | wait_shm_fd = get_wait_shm(shm_path, mmap_size, global); | |
174 | if (wait_shm_fd < 0) { | |
175 | goto error; | |
176 | } | |
177 | ||
178 | wait_shm_mmap = mmap(NULL, mmap_size, PROT_WRITE | PROT_READ, | |
179 | MAP_SHARED, wait_shm_fd, 0); | |
180 | /* close shm fd immediately after taking the mmap reference */ | |
181 | ret = close(wait_shm_fd); | |
182 | if (ret) { | |
183 | perror("Error closing fd"); | |
184 | } | |
185 | ||
186 | if (wait_shm_mmap == MAP_FAILED) { | |
187 | DBG("mmap error (can be caused by race with ust)."); | |
188 | goto error; | |
189 | } | |
190 | ||
191 | return wait_shm_mmap; | |
192 | ||
193 | error: | |
194 | return NULL; | |
195 | } |