root / osdep.c @ 1d2699ae
History | View | Annotate | Download (7.3 kB)
1 | ea88812f | bellard | /*
|
---|---|---|---|
2 | ea88812f | bellard | * QEMU low level functions
|
3 | 5fafdf24 | ths | *
|
4 | ea88812f | bellard | * Copyright (c) 2003 Fabrice Bellard
|
5 | 5fafdf24 | ths | *
|
6 | ea88812f | bellard | * Permission is hereby granted, free of charge, to any person obtaining a copy
|
7 | ea88812f | bellard | * of this software and associated documentation files (the "Software"), to deal
|
8 | ea88812f | bellard | * in the Software without restriction, including without limitation the rights
|
9 | ea88812f | bellard | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10 | ea88812f | bellard | * copies of the Software, and to permit persons to whom the Software is
|
11 | ea88812f | bellard | * furnished to do so, subject to the following conditions:
|
12 | ea88812f | bellard | *
|
13 | ea88812f | bellard | * The above copyright notice and this permission notice shall be included in
|
14 | ea88812f | bellard | * all copies or substantial portions of the Software.
|
15 | ea88812f | bellard | *
|
16 | ea88812f | bellard | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17 | ea88812f | bellard | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18 | ea88812f | bellard | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
19 | ea88812f | bellard | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20 | ea88812f | bellard | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21 | ea88812f | bellard | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22 | ea88812f | bellard | * THE SOFTWARE.
|
23 | ea88812f | bellard | */
|
24 | ea88812f | bellard | #include <stdlib.h> |
25 | ea88812f | bellard | #include <stdio.h> |
26 | ea88812f | bellard | #include <stdarg.h> |
27 | ea88812f | bellard | #include <string.h> |
28 | ea88812f | bellard | #include <errno.h> |
29 | ea88812f | bellard | #include <unistd.h> |
30 | aa26bb2d | ths | #include <fcntl.h> |
31 | dfe5fff3 | Juan Quintela | #ifdef CONFIG_SOLARIS
|
32 | 605686cd | ths | #include <sys/types.h> |
33 | 605686cd | ths | #include <sys/statvfs.h> |
34 | 605686cd | ths | #endif
|
35 | ea88812f | bellard | |
36 | 71e72a19 | Juan Quintela | /* Needed early for CONFIG_BSD etc. */
|
37 | d40cdb10 | blueswir1 | #include "config-host.h" |
38 | d40cdb10 | blueswir1 | |
39 | 6e4255f6 | bellard | #ifdef _WIN32
|
40 | 6e4255f6 | bellard | #include <windows.h> |
41 | 71e72a19 | Juan Quintela | #elif defined(CONFIG_BSD)
|
42 | 194884dd | bellard | #include <stdlib.h> |
43 | 194884dd | bellard | #else
|
44 | 49b470eb | bellard | #include <malloc.h> |
45 | 194884dd | bellard | #endif
|
46 | 49b470eb | bellard | |
47 | 511d2b14 | blueswir1 | #include "qemu-common.h" |
48 | 511d2b14 | blueswir1 | #include "sysemu.h" |
49 | 03ff3ca3 | aliguori | #include "qemu_socket.h" |
50 | 03ff3ca3 | aliguori | |
51 | d741429a | Blue Swirl | #if !defined(_POSIX_C_SOURCE) || defined(_WIN32) || defined(__sun__)
|
52 | d644f8be | malc | static void *oom_check(void *ptr) |
53 | d644f8be | malc | { |
54 | d644f8be | malc | if (ptr == NULL) { |
55 | d2d5adcb | Stefan Weil | #if defined(_WIN32)
|
56 | d2d5adcb | Stefan Weil | fprintf(stderr, "Failed to allocate memory: %lu\n", GetLastError());
|
57 | d2d5adcb | Stefan Weil | #else
|
58 | d2d5adcb | Stefan Weil | fprintf(stderr, "Failed to allocate memory: %s\n", strerror(errno));
|
59 | d2d5adcb | Stefan Weil | #endif
|
60 | d644f8be | malc | abort(); |
61 | d644f8be | malc | } |
62 | d644f8be | malc | return ptr;
|
63 | d644f8be | malc | } |
64 | d644f8be | malc | #endif
|
65 | d644f8be | malc | |
66 | 6e4255f6 | bellard | #if defined(_WIN32)
|
67 | 33f00271 | balrog | void *qemu_memalign(size_t alignment, size_t size)
|
68 | 33f00271 | balrog | { |
69 | d644f8be | malc | if (!size) {
|
70 | d644f8be | malc | abort(); |
71 | d644f8be | malc | } |
72 | d644f8be | malc | return oom_check(VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE)); |
73 | 33f00271 | balrog | } |
74 | 6e4255f6 | bellard | |
75 | 6e4255f6 | bellard | void *qemu_vmalloc(size_t size)
|
76 | 6e4255f6 | bellard | { |
77 | 6e4255f6 | bellard | /* FIXME: this is not exactly optimal solution since VirtualAlloc
|
78 | 6e4255f6 | bellard | has 64Kb granularity, but at least it guarantees us that the
|
79 | 6e4255f6 | bellard | memory is page aligned. */
|
80 | d644f8be | malc | if (!size) {
|
81 | d644f8be | malc | abort(); |
82 | d644f8be | malc | } |
83 | d644f8be | malc | return oom_check(VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE)); |
84 | 6e4255f6 | bellard | } |
85 | 6e4255f6 | bellard | |
86 | 6e4255f6 | bellard | void qemu_vfree(void *ptr) |
87 | 6e4255f6 | bellard | { |
88 | 6e4255f6 | bellard | VirtualFree(ptr, 0, MEM_RELEASE);
|
89 | 6e4255f6 | bellard | } |
90 | 6e4255f6 | bellard | |
91 | 6cb7ee85 | pbrook | #else
|
92 | 6cb7ee85 | pbrook | |
93 | 33f00271 | balrog | void *qemu_memalign(size_t alignment, size_t size)
|
94 | 33f00271 | balrog | { |
95 | d741429a | Blue Swirl | #if defined(_POSIX_C_SOURCE) && !defined(__sun__)
|
96 | 33f00271 | balrog | int ret;
|
97 | 33f00271 | balrog | void *ptr;
|
98 | 33f00271 | balrog | ret = posix_memalign(&ptr, alignment, size); |
99 | d2d5adcb | Stefan Weil | if (ret != 0) { |
100 | d2d5adcb | Stefan Weil | fprintf(stderr, "Failed to allocate %zu B: %s\n",
|
101 | d2d5adcb | Stefan Weil | size, strerror(ret)); |
102 | d644f8be | malc | abort(); |
103 | d2d5adcb | Stefan Weil | } |
104 | 33f00271 | balrog | return ptr;
|
105 | 71e72a19 | Juan Quintela | #elif defined(CONFIG_BSD)
|
106 | d644f8be | malc | return oom_check(valloc(size));
|
107 | 33f00271 | balrog | #else
|
108 | d644f8be | malc | return oom_check(memalign(alignment, size));
|
109 | 33f00271 | balrog | #endif
|
110 | 33f00271 | balrog | } |
111 | 33f00271 | balrog | |
112 | 49b470eb | bellard | /* alloc shared memory pages */
|
113 | 49b470eb | bellard | void *qemu_vmalloc(size_t size)
|
114 | 49b470eb | bellard | { |
115 | 48253bd8 | malc | return qemu_memalign(getpagesize(), size);
|
116 | 49b470eb | bellard | } |
117 | 49b470eb | bellard | |
118 | 49b470eb | bellard | void qemu_vfree(void *ptr) |
119 | 49b470eb | bellard | { |
120 | 49b470eb | bellard | free(ptr); |
121 | 49b470eb | bellard | } |
122 | 49b470eb | bellard | |
123 | 49b470eb | bellard | #endif
|
124 | 49b470eb | bellard | |
125 | aa26bb2d | ths | int qemu_create_pidfile(const char *filename) |
126 | aa26bb2d | ths | { |
127 | aa26bb2d | ths | char buffer[128]; |
128 | aa26bb2d | ths | int len;
|
129 | aa26bb2d | ths | #ifndef _WIN32
|
130 | aa26bb2d | ths | int fd;
|
131 | aa26bb2d | ths | |
132 | 40ff6d7e | Kevin Wolf | fd = qemu_open(filename, O_RDWR | O_CREAT, 0600);
|
133 | aa26bb2d | ths | if (fd == -1) |
134 | aa26bb2d | ths | return -1; |
135 | aa26bb2d | ths | |
136 | aa26bb2d | ths | if (lockf(fd, F_TLOCK, 0) == -1) |
137 | aa26bb2d | ths | return -1; |
138 | aa26bb2d | ths | |
139 | aa26bb2d | ths | len = snprintf(buffer, sizeof(buffer), "%ld\n", (long)getpid()); |
140 | aa26bb2d | ths | if (write(fd, buffer, len) != len)
|
141 | aa26bb2d | ths | return -1; |
142 | aa26bb2d | ths | #else
|
143 | aa26bb2d | ths | HANDLE file; |
144 | aa26bb2d | ths | OVERLAPPED overlap; |
145 | aa26bb2d | ths | BOOL ret; |
146 | 099fe236 | Juha Riihimรคki | memset(&overlap, 0, sizeof(overlap)); |
147 | aa26bb2d | ths | |
148 | 099fe236 | Juha Riihimรคki | file = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL,
|
149 | aa26bb2d | ths | OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
150 | aa26bb2d | ths | |
151 | aa26bb2d | ths | if (file == INVALID_HANDLE_VALUE)
|
152 | aa26bb2d | ths | return -1; |
153 | aa26bb2d | ths | |
154 | aa26bb2d | ths | len = snprintf(buffer, sizeof(buffer), "%ld\n", (long)getpid()); |
155 | 5fafdf24 | ths | ret = WriteFileEx(file, (LPCVOID)buffer, (DWORD)len, |
156 | aa26bb2d | ths | &overlap, NULL);
|
157 | aa26bb2d | ths | if (ret == 0) |
158 | aa26bb2d | ths | return -1; |
159 | aa26bb2d | ths | #endif
|
160 | aa26bb2d | ths | return 0; |
161 | aa26bb2d | ths | } |
162 | 29b3a662 | pbrook | |
163 | 29b3a662 | pbrook | #ifdef _WIN32
|
164 | 29b3a662 | pbrook | |
165 | 29b3a662 | pbrook | /* Offset between 1/1/1601 and 1/1/1970 in 100 nanosec units */
|
166 | 29b3a662 | pbrook | #define _W32_FT_OFFSET (116444736000000000ULL) |
167 | 29b3a662 | pbrook | |
168 | 29b3a662 | pbrook | int qemu_gettimeofday(qemu_timeval *tp)
|
169 | 29b3a662 | pbrook | { |
170 | 29b3a662 | pbrook | union {
|
171 | 29b3a662 | pbrook | unsigned long long ns100; /*time since 1 Jan 1601 in 100ns units */ |
172 | 29b3a662 | pbrook | FILETIME ft; |
173 | 29b3a662 | pbrook | } _now; |
174 | 29b3a662 | pbrook | |
175 | 29b3a662 | pbrook | if(tp)
|
176 | 29b3a662 | pbrook | { |
177 | 29b3a662 | pbrook | GetSystemTimeAsFileTime (&_now.ft); |
178 | 29b3a662 | pbrook | tp->tv_usec=(long)((_now.ns100 / 10ULL) % 1000000ULL ); |
179 | 29b3a662 | pbrook | tp->tv_sec= (long)((_now.ns100 - _W32_FT_OFFSET) / 10000000ULL); |
180 | 29b3a662 | pbrook | } |
181 | 29b3a662 | pbrook | /* Always return 0 as per Open Group Base Specifications Issue 6.
|
182 | 29b3a662 | pbrook | Do not set errno on error. */
|
183 | 29b3a662 | pbrook | return 0; |
184 | 29b3a662 | pbrook | } |
185 | 29b3a662 | pbrook | #endif /* _WIN32 */ |
186 | 03ff3ca3 | aliguori | |
187 | 03ff3ca3 | aliguori | |
188 | 03ff3ca3 | aliguori | #ifdef _WIN32
|
189 | 03ff3ca3 | aliguori | void socket_set_nonblock(int fd) |
190 | 03ff3ca3 | aliguori | { |
191 | 03ff3ca3 | aliguori | unsigned long opt = 1; |
192 | 03ff3ca3 | aliguori | ioctlsocket(fd, FIONBIO, &opt); |
193 | 03ff3ca3 | aliguori | } |
194 | 03ff3ca3 | aliguori | |
195 | 03ff3ca3 | aliguori | int inet_aton(const char *cp, struct in_addr *ia) |
196 | 03ff3ca3 | aliguori | { |
197 | 03ff3ca3 | aliguori | uint32_t addr = inet_addr(cp); |
198 | 03ff3ca3 | aliguori | if (addr == 0xffffffff) |
199 | 03ff3ca3 | aliguori | return 0; |
200 | 03ff3ca3 | aliguori | ia->s_addr = addr; |
201 | 03ff3ca3 | aliguori | return 1; |
202 | 03ff3ca3 | aliguori | } |
203 | 40ff6d7e | Kevin Wolf | |
204 | 40ff6d7e | Kevin Wolf | void qemu_set_cloexec(int fd) |
205 | 40ff6d7e | Kevin Wolf | { |
206 | 40ff6d7e | Kevin Wolf | } |
207 | 40ff6d7e | Kevin Wolf | |
208 | 03ff3ca3 | aliguori | #else
|
209 | 40ff6d7e | Kevin Wolf | |
210 | 03ff3ca3 | aliguori | void socket_set_nonblock(int fd) |
211 | 03ff3ca3 | aliguori | { |
212 | 03ff3ca3 | aliguori | int f;
|
213 | 03ff3ca3 | aliguori | f = fcntl(fd, F_GETFL); |
214 | 03ff3ca3 | aliguori | fcntl(fd, F_SETFL, f | O_NONBLOCK); |
215 | 03ff3ca3 | aliguori | } |
216 | 40ff6d7e | Kevin Wolf | |
217 | 40ff6d7e | Kevin Wolf | void qemu_set_cloexec(int fd) |
218 | 40ff6d7e | Kevin Wolf | { |
219 | 40ff6d7e | Kevin Wolf | int f;
|
220 | 40ff6d7e | Kevin Wolf | f = fcntl(fd, F_GETFD); |
221 | 40ff6d7e | Kevin Wolf | fcntl(fd, F_SETFD, f | FD_CLOEXEC); |
222 | 40ff6d7e | Kevin Wolf | } |
223 | 40ff6d7e | Kevin Wolf | |
224 | 40ff6d7e | Kevin Wolf | #endif
|
225 | 40ff6d7e | Kevin Wolf | |
226 | 40ff6d7e | Kevin Wolf | /*
|
227 | 40ff6d7e | Kevin Wolf | * Opens a file with FD_CLOEXEC set
|
228 | 40ff6d7e | Kevin Wolf | */
|
229 | 40ff6d7e | Kevin Wolf | int qemu_open(const char *name, int flags, ...) |
230 | 40ff6d7e | Kevin Wolf | { |
231 | 40ff6d7e | Kevin Wolf | int ret;
|
232 | 40ff6d7e | Kevin Wolf | int mode = 0; |
233 | 40ff6d7e | Kevin Wolf | |
234 | 40ff6d7e | Kevin Wolf | if (flags & O_CREAT) {
|
235 | 40ff6d7e | Kevin Wolf | va_list ap; |
236 | 40ff6d7e | Kevin Wolf | |
237 | 40ff6d7e | Kevin Wolf | va_start(ap, flags); |
238 | 40ff6d7e | Kevin Wolf | mode = va_arg(ap, int);
|
239 | 40ff6d7e | Kevin Wolf | va_end(ap); |
240 | 40ff6d7e | Kevin Wolf | } |
241 | 40ff6d7e | Kevin Wolf | |
242 | 40ff6d7e | Kevin Wolf | #ifdef O_CLOEXEC
|
243 | 40ff6d7e | Kevin Wolf | ret = open(name, flags | O_CLOEXEC, mode); |
244 | 40ff6d7e | Kevin Wolf | #else
|
245 | 40ff6d7e | Kevin Wolf | ret = open(name, flags, mode); |
246 | 40ff6d7e | Kevin Wolf | if (ret >= 0) { |
247 | 40ff6d7e | Kevin Wolf | qemu_set_cloexec(ret); |
248 | 40ff6d7e | Kevin Wolf | } |
249 | 03ff3ca3 | aliguori | #endif
|
250 | 40ff6d7e | Kevin Wolf | |
251 | 40ff6d7e | Kevin Wolf | return ret;
|
252 | 40ff6d7e | Kevin Wolf | } |
253 | 40ff6d7e | Kevin Wolf | |
254 | 7b5f699d | Kirill A. Shutemov | /*
|
255 | 7b5f699d | Kirill A. Shutemov | * A variant of write(2) which handles partial write.
|
256 | 7b5f699d | Kirill A. Shutemov | *
|
257 | 7b5f699d | Kirill A. Shutemov | * Return the number of bytes transferred.
|
258 | 7b5f699d | Kirill A. Shutemov | * Set errno if fewer than `count' bytes are written.
|
259 | 7b5f699d | Kirill A. Shutemov | */
|
260 | 7b5f699d | Kirill A. Shutemov | ssize_t qemu_write_full(int fd, const void *buf, size_t count) |
261 | 7b5f699d | Kirill A. Shutemov | { |
262 | 7b5f699d | Kirill A. Shutemov | ssize_t ret = 0;
|
263 | 7b5f699d | Kirill A. Shutemov | ssize_t total = 0;
|
264 | 7b5f699d | Kirill A. Shutemov | |
265 | 7b5f699d | Kirill A. Shutemov | while (count) {
|
266 | 7b5f699d | Kirill A. Shutemov | ret = write(fd, buf, count); |
267 | 7b5f699d | Kirill A. Shutemov | if (ret < 0) { |
268 | 7b5f699d | Kirill A. Shutemov | if (errno == EINTR)
|
269 | 7b5f699d | Kirill A. Shutemov | continue;
|
270 | 7b5f699d | Kirill A. Shutemov | break;
|
271 | 7b5f699d | Kirill A. Shutemov | } |
272 | 7b5f699d | Kirill A. Shutemov | |
273 | 7b5f699d | Kirill A. Shutemov | count -= ret; |
274 | 7b5f699d | Kirill A. Shutemov | buf += ret; |
275 | 7b5f699d | Kirill A. Shutemov | total += ret; |
276 | 7b5f699d | Kirill A. Shutemov | } |
277 | 7b5f699d | Kirill A. Shutemov | |
278 | 7b5f699d | Kirill A. Shutemov | return total;
|
279 | 7b5f699d | Kirill A. Shutemov | } |
280 | 7b5f699d | Kirill A. Shutemov | |
281 | 40ff6d7e | Kevin Wolf | #ifndef _WIN32
|
282 | 40ff6d7e | Kevin Wolf | /*
|
283 | 40ff6d7e | Kevin Wolf | * Creates a pipe with FD_CLOEXEC set on both file descriptors
|
284 | 40ff6d7e | Kevin Wolf | */
|
285 | 40ff6d7e | Kevin Wolf | int qemu_pipe(int pipefd[2]) |
286 | 40ff6d7e | Kevin Wolf | { |
287 | 40ff6d7e | Kevin Wolf | int ret;
|
288 | 40ff6d7e | Kevin Wolf | |
289 | 40ff6d7e | Kevin Wolf | #ifdef CONFIG_PIPE2
|
290 | 40ff6d7e | Kevin Wolf | ret = pipe2(pipefd, O_CLOEXEC); |
291 | 3a03bfa5 | Andre Przywara | if (ret != -1 || errno != ENOSYS) { |
292 | 3a03bfa5 | Andre Przywara | return ret;
|
293 | 3a03bfa5 | Andre Przywara | } |
294 | 3a03bfa5 | Andre Przywara | #endif
|
295 | 40ff6d7e | Kevin Wolf | ret = pipe(pipefd); |
296 | 40ff6d7e | Kevin Wolf | if (ret == 0) { |
297 | 40ff6d7e | Kevin Wolf | qemu_set_cloexec(pipefd[0]);
|
298 | 40ff6d7e | Kevin Wolf | qemu_set_cloexec(pipefd[1]);
|
299 | 40ff6d7e | Kevin Wolf | } |
300 | 40ff6d7e | Kevin Wolf | |
301 | 40ff6d7e | Kevin Wolf | return ret;
|
302 | 40ff6d7e | Kevin Wolf | } |
303 | 40ff6d7e | Kevin Wolf | #endif
|
304 | 40ff6d7e | Kevin Wolf | |
305 | 40ff6d7e | Kevin Wolf | /*
|
306 | 40ff6d7e | Kevin Wolf | * Opens a socket with FD_CLOEXEC set
|
307 | 40ff6d7e | Kevin Wolf | */
|
308 | 40ff6d7e | Kevin Wolf | int qemu_socket(int domain, int type, int protocol) |
309 | 40ff6d7e | Kevin Wolf | { |
310 | 40ff6d7e | Kevin Wolf | int ret;
|
311 | 40ff6d7e | Kevin Wolf | |
312 | 40ff6d7e | Kevin Wolf | #ifdef SOCK_CLOEXEC
|
313 | 40ff6d7e | Kevin Wolf | ret = socket(domain, type | SOCK_CLOEXEC, protocol); |
314 | 3a03bfa5 | Andre Przywara | if (ret != -1 || errno != EINVAL) { |
315 | 3a03bfa5 | Andre Przywara | return ret;
|
316 | 3a03bfa5 | Andre Przywara | } |
317 | 3a03bfa5 | Andre Przywara | #endif
|
318 | 40ff6d7e | Kevin Wolf | ret = socket(domain, type, protocol); |
319 | 40ff6d7e | Kevin Wolf | if (ret >= 0) { |
320 | 40ff6d7e | Kevin Wolf | qemu_set_cloexec(ret); |
321 | 40ff6d7e | Kevin Wolf | } |
322 | 40ff6d7e | Kevin Wolf | |
323 | 40ff6d7e | Kevin Wolf | return ret;
|
324 | 40ff6d7e | Kevin Wolf | } |
325 | 40ff6d7e | Kevin Wolf | |
326 | 40ff6d7e | Kevin Wolf | /*
|
327 | 40ff6d7e | Kevin Wolf | * Accept a connection and set FD_CLOEXEC
|
328 | 40ff6d7e | Kevin Wolf | */
|
329 | 40ff6d7e | Kevin Wolf | int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen) |
330 | 40ff6d7e | Kevin Wolf | { |
331 | 40ff6d7e | Kevin Wolf | int ret;
|
332 | 40ff6d7e | Kevin Wolf | |
333 | 40ff6d7e | Kevin Wolf | #ifdef CONFIG_ACCEPT4
|
334 | 40ff6d7e | Kevin Wolf | ret = accept4(s, addr, addrlen, SOCK_CLOEXEC); |
335 | 347ed55c | Kevin Wolf | if (ret != -1 || errno != ENOSYS) { |
336 | 3a03bfa5 | Andre Przywara | return ret;
|
337 | 3a03bfa5 | Andre Przywara | } |
338 | 3a03bfa5 | Andre Przywara | #endif
|
339 | 40ff6d7e | Kevin Wolf | ret = accept(s, addr, addrlen); |
340 | 40ff6d7e | Kevin Wolf | if (ret >= 0) { |
341 | 40ff6d7e | Kevin Wolf | qemu_set_cloexec(ret); |
342 | 40ff6d7e | Kevin Wolf | } |
343 | 40ff6d7e | Kevin Wolf | |
344 | 40ff6d7e | Kevin Wolf | return ret;
|
345 | 40ff6d7e | Kevin Wolf | } |