]>
Commit | Line | Data |
---|---|---|
1815bff5 A |
1 | /* File created by Chris Youngworth, Apple Computer 2/11/99 */ |
2 | ||
3 | ||
4 | #define mig_external | |
5 | ||
6 | #include <mach/port.h> | |
7 | #include <mach/mach_error.h> | |
8 | #include <mach/mach_traps.h> | |
9 | #include <mach/mach.h> | |
10 | #ifndef MACH_BSD | |
11 | #define MACH_BSD | |
12 | #endif | |
1815bff5 | 13 | #include <mach/mach_syscalls.h> |
ef8ad44b | 14 | #include <mach/mach_traps.h> |
1815bff5 A |
15 | #include <mach/mig_errors.h> |
16 | #include <sys/param.h> | |
c3a08f59 | 17 | #include <sys/mount.h> |
1815bff5 A |
18 | #include <sys/file.h> |
19 | #include <sys/mman.h> | |
20 | #include <sys/stat.h> | |
21 | #include <sys/sysctl.h> | |
22 | #include <sys/gmon.h> | |
34d340d7 | 23 | #include <sys/types.h> |
1815bff5 | 24 | #include <errno.h> |
1815bff5 A |
25 | #include <limits.h> |
26 | #include <stdio.h> | |
27 | #include <stdlib.h> | |
28 | #include <string.h> | |
29 | #include <nlist.h> | |
30 | #include <ctype.h> | |
31 | #include <unistd.h> | |
32 | #include <paths.h> | |
34d340d7 A |
33 | #include <dirent.h> |
34 | ||
ef8ad44b | 35 | #include <CoreFoundation/CoreFoundation.h> |
1815bff5 A |
36 | |
37 | #include <default_pager/default_pager_types.h> | |
aaff5f01 | 38 | #include <default_pager_alertsServer.h> |
1815bff5 | 39 | #include <backing_store_alerts.h> |
aaff5f01 | 40 | #include <backing_store_triggersServer.h> |
1815bff5 | 41 | |
c3a08f59 A |
42 | /* |
43 | * HI_WATER_DEFAULT set to this funny value to | |
44 | * match the size that the low space application | |
45 | * is asking for... need to keep MINIMUM_SIZE | |
46 | * above this value. | |
47 | */ | |
48 | #define HI_WATER_DEFAULT 40000000 | |
49 | #define MINIMUM_SIZE (1024 * 1024 * 64) | |
50 | #define MAXIMUM_SIZE (1024 * 1024 * 1024) | |
51 | ||
52 | #define MAX_LIMITS 8 | |
53 | ||
8459d725 A |
54 | #if TARGET_OS_EMBEDDED |
55 | ||
56 | #include <System/sys/content_protection.h> | |
57 | ||
58 | #define USE_SYSTEMCONFIGURATION_PRIVATE_HEADERS 1 | |
59 | #include <SystemConfiguration/SystemConfiguration.h> | |
60 | ||
61 | enum { | |
62 | VM_PAGING_MODE_DISABLED = 0, | |
63 | VM_PAGING_MODE_FREEZE, | |
64 | VM_PAGING_MODE_DYNAMIC | |
65 | }; | |
66 | ||
67 | #define VM_FREEZE_SYSCTL "vm.freeze_enabled" | |
68 | #define FREEZE_FIXED_SWAP_SIZE (128 * 1024 * 1024) | |
69 | #endif | |
c3a08f59 A |
70 | |
71 | struct limit { | |
72 | unsigned int size; | |
73 | unsigned int low_water; | |
74 | } limits[MAX_LIMITS]; | |
75 | ||
76 | ||
1815bff5 | 77 | int debug = 0; |
c3a08f59 | 78 | int max_valid = 0; |
1815bff5 | 79 | int file_count = 0; |
c3a08f59 A |
80 | unsigned int hi_water; |
81 | unsigned int local_hi_water; | |
1815bff5 | 82 | int priority = 0; |
c3a08f59 | 83 | int options = 0; |
1815bff5 A |
84 | char fileroot[512]; |
85 | ||
86 | ||
87 | /* global parameters for application notification option */ | |
756446ec A |
88 | mach_port_t trigger_port = MACH_PORT_NULL; |
89 | mach_port_t notify_port = MACH_PORT_NULL; | |
c3a08f59 A |
90 | unsigned int notify_high = 0; |
91 | unsigned int bs_recovery; | |
1815bff5 A |
92 | |
93 | /* | |
94 | void setprof __P((struct kvmvars *kvp, int state)); | |
95 | void dumpstate __P((struct kvmvars *kvp)); | |
96 | void reset __P((struct kvmvars *kvp)); | |
97 | */ | |
98 | ||
99 | ||
100 | ||
101 | mach_msg_return_t | |
102 | server_alert_loop( | |
103 | mach_msg_size_t max_size, | |
104 | mach_port_t rcv_name, | |
105 | mach_msg_options_t options) | |
106 | { | |
107 | mig_reply_error_t *bufRequest = 0, *bufReply = 0; | |
108 | register mach_msg_return_t mr; | |
109 | register kern_return_t kr; | |
110 | ||
111 | if ((kr = vm_allocate(mach_task_self(), | |
112 | (vm_address_t *)&bufRequest, | |
113 | max_size + MAX_TRAILER_SIZE, | |
114 | TRUE)) != KERN_SUCCESS) | |
115 | return kr; | |
116 | mlock(bufRequest, max_size + MAX_TRAILER_SIZE); | |
117 | if ((kr = vm_allocate(mach_task_self(), | |
118 | (vm_address_t *)&bufReply, | |
119 | max_size + MAX_TRAILER_SIZE, | |
120 | TRUE)) != KERN_SUCCESS) | |
121 | return kr; | |
122 | mlock(bufReply, max_size + MAX_TRAILER_SIZE); | |
123 | while(TRUE) { | |
83f6dbe8 A |
124 | mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG|options, |
125 | 0, max_size, rcv_name, | |
126 | MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); | |
1815bff5 A |
127 | if (mr == MACH_MSG_SUCCESS) { |
128 | /* we have a request message */ | |
129 | ||
130 | if(!(default_pager_alerts_server( | |
131 | &bufRequest->Head, &bufReply->Head))) | |
132 | backing_store_triggers_server( | |
133 | &bufRequest->Head, &bufReply->Head); | |
134 | ||
135 | if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && | |
136 | bufReply->RetCode != KERN_SUCCESS) { | |
137 | if (bufReply->RetCode == MIG_NO_REPLY) | |
138 | /* | |
139 | * This return code is a little tricky-- | |
140 | * it appears that the demux routine found an | |
141 | * error of some sort, but since that error | |
142 | * would not normally get returned either to | |
143 | * the local user or the remote one, we pretend it's | |
144 | * ok. | |
145 | */ | |
146 | ||
147 | bufRequest->Head.msgh_remote_port = MACH_PORT_NULL; | |
148 | mach_msg_destroy(&bufRequest->Head); | |
149 | continue; | |
150 | } | |
151 | ||
152 | if (bufReply->Head.msgh_remote_port == MACH_PORT_NULL) { | |
153 | /* no reply port, so destroy the reply */ | |
154 | if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) | |
155 | mach_msg_destroy(&bufReply->Head); | |
156 | } else { | |
157 | break; | |
158 | } | |
159 | } else { | |
160 | break; | |
161 | } | |
162 | } | |
163 | ||
164 | (void)vm_deallocate(mach_task_self(), | |
165 | (vm_address_t) bufRequest, | |
166 | max_size + MAX_TRAILER_SIZE); | |
167 | (void)vm_deallocate(mach_task_self(), | |
168 | (vm_address_t) bufReply, | |
169 | max_size + MAX_TRAILER_SIZE); | |
170 | return KERN_FAILURE; | |
171 | ||
172 | } | |
173 | ||
174 | ||
175 | kern_return_t | |
176 | backing_store_triggers(dynamic_pager, hi_wat, flags, port) | |
177 | mach_port_t dynamic_pager; | |
178 | int hi_wat; | |
179 | int flags; | |
180 | mach_port_t port; | |
181 | { | |
c3a08f59 A |
182 | int cur_limits; |
183 | ||
184 | if (file_count > max_valid) | |
185 | cur_limits = max_valid; | |
186 | else | |
187 | cur_limits = file_count; | |
188 | ||
189 | if((hi_wat + limits[cur_limits].size) > limits[cur_limits].low_water) | |
1815bff5 | 190 | return KERN_FAILURE; /* let ipc system clean up port */ |
756446ec A |
191 | |
192 | /* If there was a previous registration, throw it away */ | |
193 | if (notify_port != MACH_PORT_NULL) { | |
194 | mach_port_deallocate(mach_task_self(), notify_port); | |
195 | notify_port = MACH_PORT_NULL; | |
196 | } | |
197 | ||
1815bff5 A |
198 | notify_port = port; |
199 | notify_high = hi_wat; | |
200 | if(hi_water < notify_high) { | |
201 | local_hi_water = notify_high; | |
202 | } else { | |
203 | local_hi_water = hi_water; | |
204 | } | |
205 | if(notify_high > hi_water) { | |
206 | default_pager_space_alert(trigger_port, HI_WAT_ALERT); | |
207 | } | |
208 | return KERN_SUCCESS; | |
209 | } | |
210 | ||
211 | ||
212 | kern_return_t | |
213 | default_pager_space_alert(alert_port, flags) | |
214 | mach_port_t alert_port; | |
215 | int flags; | |
216 | { | |
217 | char subfile[512]; | |
c3a08f59 | 218 | off_t filesize; |
ef8ad44b | 219 | int error=0, fd=0; |
756446ec | 220 | kern_return_t ret; |
c3a08f59 A |
221 | int cur_limits; |
222 | unsigned int cur_size; | |
223 | unsigned int notifications; | |
1815bff5 | 224 | |
c3a08f59 | 225 | |
1815bff5 | 226 | if(flags & HI_WAT_ALERT) { |
c3a08f59 | 227 | |
1815bff5 | 228 | file_count++; |
c3a08f59 A |
229 | |
230 | if (file_count > max_valid) | |
231 | cur_limits = max_valid; | |
232 | else | |
233 | cur_limits = file_count; | |
234 | ||
235 | cur_size = limits[cur_limits].size; | |
236 | filesize = cur_size; | |
237 | ||
238 | /* | |
239 | * because the LO_WAT threshold changes relative to | |
240 | * the size of the swap file we're creating | |
241 | * we need to reset the LO_WAT_ALERT threshold each | |
242 | * time we create a new swap file | |
243 | */ | |
244 | if (limits[cur_limits].low_water) | |
245 | notifications = HI_WAT_ALERT | LO_WAT_ALERT; | |
246 | else | |
247 | notifications = HI_WAT_ALERT; | |
248 | ||
1815bff5 | 249 | sprintf(subfile, "%s%d", fileroot, file_count); |
fc6d9e4b A |
250 | #if TARGET_OS_EMBEDDED |
251 | fd = open_dprotected_np(subfile, O_CREAT|O_EXCL|O_RDWR, PROTECTION_CLASS_F, 0, (mode_t)(S_IRUSR|S_IWUSR)); | |
252 | #else | |
ef8ad44b | 253 | fd = open(subfile, O_CREAT|O_EXCL|O_RDWR,(mode_t)(S_IRUSR|S_IWUSR)); |
fc6d9e4b | 254 | #endif |
ef8ad44b | 255 | if (fd == -1) { |
83f6dbe8 A |
256 | /* force error recovery below */ |
257 | error = -1; | |
8459d725 | 258 | } |
8459d725 A |
259 | |
260 | if(!error) { | |
ef8ad44b | 261 | error = fcntl(fd, F_SETSIZE, &filesize); |
83f6dbe8 | 262 | if(error) { |
ef8ad44b | 263 | error = ftruncate(fd, filesize); |
83f6dbe8 A |
264 | } |
265 | if(error) | |
266 | unlink(subfile); | |
ef8ad44b | 267 | close(fd); |
756446ec | 268 | } |
c3a08f59 | 269 | |
1815bff5 | 270 | if(error == -1) { |
1815bff5 | 271 | file_count--; |
c3a08f59 A |
272 | |
273 | if (file_count > max_valid) | |
274 | cur_limits = max_valid; | |
275 | else | |
276 | cur_limits = file_count; | |
277 | ||
278 | if (limits[cur_limits].low_water) | |
279 | notifications = HI_WAT_ALERT | LO_WAT_ALERT; | |
280 | else | |
281 | notifications = HI_WAT_ALERT; | |
ef8ad44b A |
282 | |
283 | notifications |= SWAP_FILE_CREATION_ERROR; | |
c3a08f59 | 284 | |
1815bff5 A |
285 | local_hi_water = local_hi_water>>2; |
286 | if(notify_high >= (local_hi_water)) { | |
756446ec | 287 | if(notify_port != MACH_PORT_NULL) { |
1815bff5 A |
288 | /* notify monitoring app of */ |
289 | /* backing store shortage */ | |
290 | backing_store_alert(notify_port, | |
291 | HI_WAT_ALERT); | |
292 | mach_port_deallocate(mach_task_self(), | |
293 | notify_port); | |
756446ec | 294 | notify_port = MACH_PORT_NULL; |
1815bff5 | 295 | notify_high = 0; |
1815bff5 A |
296 | } |
297 | } | |
1815bff5 A |
298 | } else { |
299 | if(hi_water < notify_high) { | |
300 | if(local_hi_water < notify_high) { | |
301 | bs_recovery = notify_high - local_hi_water; | |
302 | } | |
303 | local_hi_water = notify_high; | |
304 | } else { | |
305 | if(local_hi_water < hi_water) { | |
306 | bs_recovery = hi_water - local_hi_water; | |
307 | } | |
308 | local_hi_water = hi_water; | |
309 | } | |
ef8ad44b A |
310 | ret = macx_swapon((uint64_t)(uintptr_t)subfile, |
311 | flags, cur_size, priority); | |
c3a08f59 | 312 | |
756446ec A |
313 | if(ret) { |
314 | unlink(subfile); | |
315 | file_count--; | |
c3a08f59 A |
316 | |
317 | if (file_count > max_valid) | |
318 | cur_limits = max_valid; | |
319 | else | |
320 | cur_limits = file_count; | |
321 | ||
322 | if (limits[cur_limits].low_water) | |
323 | notifications = HI_WAT_ALERT | LO_WAT_ALERT; | |
324 | else | |
325 | notifications = HI_WAT_ALERT; | |
326 | ||
756446ec A |
327 | local_hi_water = local_hi_water>>2; |
328 | if(notify_high >= (local_hi_water)) { | |
329 | if(notify_port != MACH_PORT_NULL) { | |
330 | /* notify monitoring app of */ | |
331 | /* backing store shortage */ | |
332 | backing_store_alert( | |
333 | notify_port, | |
334 | HI_WAT_ALERT); | |
335 | mach_port_deallocate( | |
336 | mach_task_self(), | |
337 | notify_port); | |
338 | notify_port = MACH_PORT_NULL; | |
339 | notify_high = 0; | |
340 | } | |
341 | } | |
c3a08f59 | 342 | } else if(bs_recovery <= cur_size) { |
1815bff5 A |
343 | if((bs_recovery != 0) && (notify_port)) { |
344 | backing_store_alert(notify_port, | |
345 | LO_WAT_ALERT); | |
346 | mach_port_deallocate(mach_task_self(), | |
347 | notify_port); | |
756446ec | 348 | notify_port = MACH_PORT_NULL; |
1815bff5 | 349 | notify_high = 0; |
1815bff5 A |
350 | bs_recovery = 0; |
351 | } | |
352 | } else | |
c3a08f59 | 353 | bs_recovery = bs_recovery-cur_size; |
1815bff5 | 354 | } |
c3a08f59 | 355 | macx_triggers(local_hi_water, limits[cur_limits].low_water, notifications, alert_port); |
1815bff5 A |
356 | } |
357 | if(flags & LO_WAT_ALERT) { | |
1815bff5 A |
358 | sprintf(subfile, "%s%d", fileroot, file_count); |
359 | if(hi_water < notify_high) { | |
360 | local_hi_water = notify_high; | |
361 | } else { | |
362 | local_hi_water = hi_water; | |
363 | } | |
756446ec | 364 | if((bs_recovery != 0) && (notify_port != MACH_PORT_NULL)) { |
1815bff5 A |
365 | backing_store_alert(notify_port, LO_WAT_ALERT); |
366 | mach_port_deallocate(mach_task_self(), notify_port); | |
756446ec | 367 | notify_port = MACH_PORT_NULL; |
1815bff5 | 368 | notify_high = 0; |
1815bff5 A |
369 | bs_recovery = 0; |
370 | } | |
ef8ad44b A |
371 | if((error = macx_swapoff((uint64_t)(uintptr_t)subfile, |
372 | flags)) == 0) { | |
c3a08f59 | 373 | |
1815bff5 A |
374 | unlink(subfile); |
375 | file_count--; | |
c3a08f59 A |
376 | |
377 | if (file_count > max_valid) | |
378 | cur_limits = max_valid; | |
379 | else | |
380 | cur_limits = file_count; | |
381 | } else { | |
382 | if (file_count > max_valid) | |
383 | cur_limits = max_valid; | |
384 | else | |
385 | cur_limits = file_count; | |
1815bff5 | 386 | } |
c3a08f59 A |
387 | /* |
388 | * only need to reset the LO_WAT_ALERT... the HI_WAT size is fixed, | |
389 | * it doesn't change even if the swap file size shrinks or grows | |
390 | */ | |
391 | macx_triggers(local_hi_water, limits[cur_limits].low_water, LO_WAT_ALERT, alert_port); | |
1815bff5 A |
392 | } |
393 | return KERN_SUCCESS; | |
394 | } | |
395 | ||
396 | void | |
397 | wait_on_paging_trigger(trigger_port) | |
398 | mach_port_t trigger_port; | |
399 | { | |
400 | kern_return_t result; | |
401 | result = server_alert_loop(4096, trigger_port, MACH_MSG_OPTION_NONE); | |
402 | if (result != KERN_SUCCESS) { | |
403 | fprintf(stderr, "dynamic_pager: default pager alert failed\n"); | |
83f6dbe8 | 404 | exit(EXIT_FAILURE); |
1815bff5 | 405 | } |
83f6dbe8 | 406 | exit(EXIT_SUCCESS); |
1815bff5 A |
407 | } |
408 | ||
409 | void | |
2fc1e207 | 410 | paging_setup(flags, size, priority, low, high, encrypted) |
1815bff5 A |
411 | int flags; |
412 | int size; | |
413 | int priority; | |
414 | int low; | |
415 | int high; | |
2fc1e207 | 416 | boolean_t encrypted; |
1815bff5 A |
417 | { |
418 | off_t filesize = size; | |
419 | char subfile[512]; | |
ef8ad44b | 420 | int error, fd = 0; |
1815bff5 A |
421 | |
422 | file_count = 0; | |
423 | sprintf(subfile, "%s%d", fileroot, file_count); | |
fc6d9e4b A |
424 | #if TARGET_OS_EMBEDDED |
425 | fd = open_dprotected_np(subfile, O_CREAT|O_EXCL|O_RDWR, PROTECTION_CLASS_F, 0, ((mode_t)(S_IRUSR|S_IWUSR))); | |
426 | #else | |
ef8ad44b | 427 | fd = open(subfile, O_CREAT|O_EXCL|O_RDWR, ((mode_t)(S_IRUSR|S_IWUSR))); |
fc6d9e4b | 428 | #endif |
ef8ad44b | 429 | if (fd == -1) { |
83f6dbe8 A |
430 | fprintf(stderr, "dynamic_pager: cannot create paging file %s!\n", |
431 | subfile); | |
432 | exit(EXIT_FAILURE); | |
433 | } | |
83f6dbe8 | 434 | |
8459d725 | 435 | |
ef8ad44b | 436 | error = fcntl(fd, F_SETSIZE, &filesize); |
756446ec | 437 | if(error) { |
ef8ad44b | 438 | error = ftruncate(fd, filesize); |
756446ec | 439 | } |
ef8ad44b | 440 | close(fd); |
83f6dbe8 A |
441 | |
442 | if (error == -1) { | |
443 | fprintf(stderr, "dynamic_pager: cannot extend paging file size %s to %llu!\n", | |
444 | subfile, filesize); | |
445 | exit(EXIT_FAILURE); | |
446 | } | |
1815bff5 | 447 | |
2fc1e207 A |
448 | if (macx_triggers(0, 0, |
449 | (encrypted | |
450 | ? SWAP_ENCRYPT_ON | |
451 | : SWAP_ENCRYPT_OFF), | |
452 | MACH_PORT_NULL) != 0) { | |
453 | fprintf(stderr, | |
454 | "dynamic_pager: warning: " | |
455 | "could not turn encrypted swap %s\n", | |
456 | (encrypted ? "on" : "off")); | |
457 | } | |
458 | ||
ef8ad44b | 459 | macx_swapon((uint64_t)(uintptr_t)subfile, flags, size, priority); |
c3a08f59 | 460 | |
1815bff5 | 461 | if(hi_water) { |
1c51fdde A |
462 | mach_msg_type_name_t poly; |
463 | ||
1815bff5 A |
464 | if (mach_port_allocate(mach_task_self(), |
465 | MACH_PORT_RIGHT_RECEIVE, | |
466 | &trigger_port) != KERN_SUCCESS) { | |
83f6dbe8 A |
467 | fprintf(stderr,"dynamic_pager: allocation of trigger port failed\n"); |
468 | exit(EXIT_FAILURE); | |
1815bff5 | 469 | } |
1c51fdde A |
470 | /* create a send right on our local port */ |
471 | mach_port_extract_right(mach_task_self(), trigger_port, | |
472 | MACH_MSG_TYPE_MAKE_SEND, &trigger_port, &poly); | |
1815bff5 | 473 | macx_triggers(high, low, HI_WAT_ALERT, trigger_port); |
c3a08f59 | 474 | |
1815bff5 | 475 | if(low) { |
c3a08f59 | 476 | macx_triggers(high, low, LO_WAT_ALERT, trigger_port); |
1815bff5 A |
477 | } |
478 | /* register control port for applications wishing to */ | |
479 | /* get backing store notifications or change dynamic */ | |
480 | /* pager settings. */ | |
481 | set_dp_control_port(mach_host_self(), trigger_port); | |
482 | wait_on_paging_trigger(trigger_port); | |
483 | } | |
83f6dbe8 | 484 | exit(EXIT_SUCCESS); |
1815bff5 | 485 | } |
34d340d7 A |
486 | |
487 | static void | |
488 | clean_swap_directory(const char *path) | |
489 | { | |
490 | DIR *dir; | |
491 | struct dirent *entry; | |
492 | char buf[1024]; | |
493 | ||
494 | dir = opendir(path); | |
495 | if (dir == NULL) { | |
496 | fprintf(stderr,"dynamic_pager: cannot open swap directory %s\n", path); | |
497 | exit(EXIT_FAILURE); | |
498 | } | |
499 | ||
500 | while ((entry = readdir(dir)) != NULL) { | |
501 | if (entry->d_namlen>= 4 && strncmp(entry->d_name, "swap", 4) == 0) { | |
502 | snprintf(buf, sizeof buf, "%s/%s", path, entry->d_name); | |
503 | unlink(buf); | |
504 | } | |
505 | } | |
506 | ||
507 | closedir(dir); | |
508 | } | |
509 | ||
8459d725 A |
510 | #define VM_PREFS_PLIST "/Library/Preferences/com.apple.virtualMemory.plist" |
511 | #define VM_PREFS_DISABLE_ENCRYPT_SWAP_KEY "DisableEncryptedSwap" | |
34d340d7 A |
512 | |
513 | static boolean_t | |
514 | should_encrypt_swap(void) | |
515 | { | |
516 | CFPropertyListRef propertyList; | |
517 | CFTypeID propertyListType; | |
518 | CFStringRef errorString; | |
519 | CFDataRef resourceData; | |
520 | SInt32 errorCode; | |
521 | CFURLRef fileURL; | |
8459d725 A |
522 | CFTypeRef disable_encrypted_swap; |
523 | boolean_t should_encrypt = TRUE; | |
524 | boolean_t explicit_value = FALSE; | |
34d340d7 A |
525 | |
526 | fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)VM_PREFS_PLIST, strlen(VM_PREFS_PLIST), false); | |
527 | if (fileURL == NULL) { | |
528 | /*fprintf(stderr, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), VM_PREFS_PLIST);*/ | |
529 | goto done; | |
530 | } | |
531 | ||
532 | if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, fileURL, &resourceData, NULL, NULL, &errorCode)) { | |
533 | /*fprintf(stderr, "%s: CFURLCreateDataAndPropertiesFromResource(%s) failed: %d\n", getprogname(), VM_PREFS_PLIST, (int)errorCode);*/ | |
534 | CFRelease(fileURL); | |
535 | goto done; | |
536 | } | |
537 | ||
538 | CFRelease(fileURL); | |
539 | propertyList = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resourceData, kCFPropertyListMutableContainers, &errorString); | |
540 | if (propertyList == NULL) { | |
541 | /*fprintf(stderr, "%s: cannot get XML propertyList %s\n", getprogname(), VM_PREFS_PLIST);*/ | |
542 | CFRelease(resourceData); | |
543 | goto done; | |
544 | } | |
545 | ||
546 | propertyListType = CFGetTypeID(propertyList); | |
547 | ||
548 | if (propertyListType == CFDictionaryGetTypeID()) { | |
8459d725 A |
549 | disable_encrypted_swap = (CFTypeRef) CFDictionaryGetValue((CFDictionaryRef) propertyList, CFSTR(VM_PREFS_DISABLE_ENCRYPT_SWAP_KEY)); |
550 | if (disable_encrypted_swap == NULL) { | |
551 | /* no value: use the default value i.e. encrypted swap ON */ | |
552 | } else if (CFGetTypeID(disable_encrypted_swap) != CFBooleanGetTypeID()) { | |
34d340d7 | 553 | fprintf(stderr, "%s: wrong type for key \"%s\"\n", |
8459d725 | 554 | getprogname(), VM_PREFS_DISABLE_ENCRYPT_SWAP_KEY); |
34d340d7 | 555 | /* bogus value, assume it's "true" for safety's sake */ |
8459d725 A |
556 | should_encrypt = TRUE; |
557 | explicit_value = TRUE; | |
34d340d7 | 558 | } else { |
8459d725 A |
559 | should_encrypt = CFBooleanGetValue((CFBooleanRef)disable_encrypted_swap) ? FALSE : TRUE; |
560 | explicit_value = TRUE; | |
34d340d7 A |
561 | } |
562 | } | |
563 | else { | |
564 | /*fprintf(stderr, "%s: invalid propertyList type %d (not a dictionary)\n", getprogname(), propertyListType);*/ | |
565 | } | |
566 | CFRelease(resourceData); | |
567 | CFRelease(propertyList); | |
568 | ||
569 | done: | |
570 | if (! explicit_value) { | |
ef8ad44b A |
571 | #if TARGET_OS_EMBEDDED |
572 | should_encrypt = FALSE; | |
ef8ad44b | 573 | #endif |
34d340d7 A |
574 | } |
575 | ||
576 | return should_encrypt; | |
577 | } | |
578 | ||
8459d725 A |
579 | #if TARGET_OS_EMBEDDED |
580 | ||
581 | #define VM_PREFS_PAGING_MODE_PLIST "com.apple.virtualMemoryMode.plist" | |
582 | #define VM_PREFS_PAGING_MODE_KEY "Mode" | |
583 | ||
584 | static uint32_t | |
585 | get_paging_mode(void) | |
586 | { | |
587 | SCPreferencesRef paging_prefs; | |
588 | uint32_t paging_mode; | |
589 | ||
590 | paging_mode = VM_PAGING_MODE_FREEZE; /* default */ | |
591 | ||
592 | paging_prefs = SCPreferencesCreate(NULL, CFSTR("dynamic_pager"), CFSTR(VM_PREFS_PAGING_MODE_PLIST)); | |
593 | if (paging_prefs) { | |
594 | CFNumberRef value; | |
595 | ||
596 | value = SCPreferencesGetValue(paging_prefs, CFSTR(VM_PREFS_PAGING_MODE_KEY)); | |
597 | ||
598 | if (value && (CFGetTypeID( value ) == CFNumberGetTypeID() ) ) { | |
599 | CFNumberGetValue((CFNumberRef)value, kCFNumberIntType, &paging_mode); | |
600 | } | |
601 | ||
602 | CFRelease(paging_prefs); | |
603 | } | |
604 | ||
605 | return paging_mode; | |
606 | } | |
607 | ||
608 | #endif | |
609 | ||
1815bff5 A |
610 | int |
611 | main(int argc, char **argv) | |
612 | { | |
613 | extern char *optarg; | |
614 | extern int optind; | |
615 | char default_filename[] = "/private/var/vm/swapfile"; | |
616 | int ch; | |
ef8ad44b | 617 | int variable_sized = 1,flags=0; |
34d340d7 | 618 | boolean_t encrypted_swap; |
8459d725 A |
619 | static char tmp[1024]; |
620 | struct statfs sfs; | |
621 | char *q; | |
622 | #if TARGET_OS_EMBEDDED | |
623 | int err; | |
624 | uint32_t paging_mode; | |
625 | size_t paging_mode_length; | |
626 | #endif | |
34d340d7 A |
627 | |
628 | /* | |
629 | setlinebuf(stdout); | |
630 | setlinebuf(stderr); | |
631 | */ | |
1815bff5 A |
632 | |
633 | seteuid(getuid()); | |
634 | strcpy(fileroot, default_filename); | |
635 | ||
ef8ad44b | 636 | retry: |
c3a08f59 A |
637 | limits[0].size = 20000000; |
638 | limits[0].low_water = 0; | |
639 | ||
640 | hi_water = 0; | |
641 | local_hi_water = 0; | |
642 | ||
34d340d7 | 643 | encrypted_swap = should_encrypt_swap(); |
c3a08f59 | 644 | |
34d340d7 | 645 | while ((ch = getopt(argc, argv, "EF:L:H:S:P:QO:")) != EOF) { |
1815bff5 A |
646 | switch((char)ch) { |
647 | ||
2fc1e207 A |
648 | case 'E': |
649 | encrypted_swap = TRUE; | |
650 | break; | |
651 | ||
1815bff5 A |
652 | case 'F': |
653 | strncpy(fileroot, optarg, 500); | |
654 | break; | |
655 | ||
656 | case 'L': | |
c3a08f59 A |
657 | variable_sized = 0; |
658 | limits[0].low_water = atoi(optarg); | |
1815bff5 A |
659 | break; |
660 | case 'H': | |
c3a08f59 | 661 | variable_sized = 0; |
1815bff5 A |
662 | hi_water = atoi(optarg); |
663 | break; | |
664 | case 'S': | |
c3a08f59 A |
665 | variable_sized = 0; |
666 | limits[0].size = atoi(optarg); | |
1815bff5 A |
667 | break; |
668 | case 'P': | |
669 | priority = atoi(optarg); | |
670 | break; | |
671 | ||
34d340d7 A |
672 | case 'Q': |
673 | /* just query for "encrypted swap" default value */ | |
674 | fprintf(stdout, | |
675 | "dynamic_pager: encrypted swap will be %s\n", | |
676 | encrypted_swap ? "ON": "OFF"); | |
677 | exit(0); | |
678 | ||
1815bff5 A |
679 | default: |
680 | (void)fprintf(stderr, | |
681 | "usage: dynamic_pager [-F filename] [-L low water alert trigger] [-H high water alert trigger] [-S file size] [-P priority]\n"); | |
83f6dbe8 | 682 | exit(EXIT_FAILURE); |
1815bff5 A |
683 | } |
684 | } | |
c3a08f59 | 685 | |
8459d725 A |
686 | /* |
687 | * get rid of the filename at the end of the swap file specification | |
688 | * we only want the portion of the pathname that should already exist | |
689 | */ | |
690 | strcpy(tmp, fileroot); | |
691 | if ((q = strrchr(tmp, '/'))) | |
692 | *q = 0; | |
693 | ||
694 | /* | |
695 | * Remove all files in the swap directory. | |
696 | */ | |
697 | clean_swap_directory(tmp); | |
698 | ||
699 | #if TARGET_OS_EMBEDDED | |
700 | paging_mode = get_paging_mode(); | |
701 | paging_mode_length = sizeof(paging_mode); | |
702 | ||
703 | switch (paging_mode) { | |
704 | case VM_PAGING_MODE_DISABLED: | |
705 | /* Paging disabled; nothing to do here so exit */ | |
706 | exit(EXIT_SUCCESS); | |
707 | ||
708 | case VM_PAGING_MODE_FREEZE: | |
709 | /* Freeze mode; one swap file of fixed size, so enable and override defaults if set */ | |
710 | err = sysctlbyname(VM_FREEZE_SYSCTL, NULL, 0, &paging_mode, paging_mode_length); | |
711 | if (err < 0) { | |
712 | (void)fprintf(stderr, "dynamic_pager: cannot set %s\n", VM_FREEZE_SYSCTL); | |
713 | exit(EXIT_FAILURE); | |
714 | } | |
715 | variable_sized = 0; | |
716 | limits[0].size = FREEZE_FIXED_SWAP_SIZE; | |
717 | break; | |
718 | ||
719 | case VM_PAGING_MODE_DYNAMIC: | |
720 | /* Dynamic paging selected; proceed normally */ | |
721 | break; | |
722 | ||
723 | default: | |
724 | /* Invalid option */ | |
725 | (void)fprintf(stderr, "dynamic_pager: invalid paging_mode %d\n", paging_mode); | |
726 | exit(EXIT_FAILURE); | |
727 | } | |
728 | #endif | |
729 | ||
730 | if (statfs(tmp, &sfs) == -1) { | |
731 | /* | |
732 | * Setup the swap directory. | |
733 | */ | |
734 | if (mkdir(tmp, 0755) == -1) { | |
735 | (void)fprintf(stderr, "dynamic_pager: cannot create swap directory %s\n", tmp); | |
736 | exit(EXIT_FAILURE); | |
737 | } | |
738 | } | |
739 | chown(tmp, 0, 0); | |
740 | ||
c3a08f59 | 741 | if (variable_sized) { |
c3a08f59 A |
742 | int i; |
743 | int mib[4]; | |
744 | size_t len; | |
745 | unsigned int size; | |
746 | u_int64_t memsize; | |
34d340d7 | 747 | u_int64_t fs_limit = 0; |
c3a08f59 A |
748 | |
749 | /* | |
750 | * if we get here, then none of the following options were specified... -L, H, or -S | |
751 | * drop into a new mode that scales the size of the swap file based on how much free | |
752 | * space is left on the volume being used for swap and the amount of physical ram | |
753 | * installed on the system... | |
754 | * basically, we'll pick a maximum size that doesn't exceed the following limits... | |
83f6dbe8 | 755 | * 1/8 the remaining free space of the swap volume |
c3a08f59 A |
756 | * the size of phsyical ram |
757 | * MAXIMUM_SIZE - currently set to 1 Gbyte... | |
758 | * once we have the maximum, we'll create a list of sizes and low_water limits | |
759 | * we'll start with 2 files of MINIMUM_SIZE - currently 64 Mbytes... | |
760 | * subsequent entries will double in size up to the calculated maximum... the low_water | |
761 | * limit will be the sum of the current file size and the previous file size for each entry... | |
762 | * as we add or delete files, we'll use the current file_count as an index into this | |
763 | * table... if it's beyond the table size, we'll use the last entry | |
764 | * the table entry will determine the size of the file to be created and the new low_water mark... | |
765 | * the high_water mark is set to HI_WATER_DEFAULT which must be smaller than MINIMUM_SIZE... | |
766 | * currently it is set to 40,000,000 to match the size being requested by the application | |
767 | * monitoring low space conditions... having it set to the same size keeps us from creating | |
768 | * an additional swap file when it really isn't necessary | |
769 | */ | |
770 | ||
8459d725 | 771 | if (statfs(tmp, &sfs) == -1) { |
34d340d7 | 772 | /* |
8459d725 A |
773 | * We really can't get filesystem status, |
774 | * so let's not limit the swap files... | |
34d340d7 | 775 | */ |
8459d725 A |
776 | fs_limit = (u_int64_t) -1; |
777 | } | |
34d340d7 | 778 | |
34d340d7 A |
779 | if (fs_limit != (u_int64_t) -1) { |
780 | /* | |
83f6dbe8 | 781 | * Limit the maximum size of a swap file to 1/8 the free |
c3a08f59 | 782 | * space available on the filesystem where the swap files |
83f6dbe8 A |
783 | * are to reside. This will allow us to allocate and |
784 | * deallocate in finer increments on systems without much | |
785 | * free space. | |
c3a08f59 | 786 | */ |
83f6dbe8 | 787 | fs_limit = ((u_int64_t)sfs.f_bfree * (u_int64_t)sfs.f_bsize) / 8; |
c3a08f59 | 788 | } |
34d340d7 | 789 | |
c3a08f59 A |
790 | mib[0] = CTL_HW; |
791 | mib[1] = HW_MEMSIZE; | |
792 | len = sizeof(u_int64_t); | |
793 | ||
794 | if (sysctl(mib, 2, &memsize, &len, NULL, 0) < 0) { | |
795 | /* | |
796 | * if the sysctl fails for some reason | |
797 | * use the starting size as the default | |
798 | */ | |
799 | memsize = MINIMUM_SIZE; | |
800 | } | |
801 | if (memsize > fs_limit) | |
802 | /* | |
803 | * clip based on filesystem space available | |
804 | */ | |
805 | memsize = fs_limit; | |
806 | ||
807 | /* | |
808 | * further limit the maximum size of a swap file | |
809 | */ | |
ef8ad44b A |
810 | if (memsize <= MINIMUM_SIZE) { |
811 | (void)fprintf(stderr, "dynamic_pager: Need more space on the disk to enable swapping.\n"); | |
812 | sleep(30); | |
813 | goto retry; | |
814 | } else if (memsize <= (MINIMUM_SIZE*2)) { | |
815 | (void)fprintf(stderr, "dynamic_pager: Activating emergency swap file immediately.\n"); | |
816 | flags |= USE_EMERGENCY_SWAP_FILE_FIRST; | |
817 | } else if (memsize > MAXIMUM_SIZE) { | |
c3a08f59 | 818 | memsize = MAXIMUM_SIZE; |
ef8ad44b | 819 | } |
c3a08f59 A |
820 | |
821 | size = MINIMUM_SIZE; | |
822 | ||
823 | /* | |
824 | * start small and work our way up to the maximum | |
825 | * sized allowed... this way, we don't tie up too | |
826 | * much disk space if we never do any real paging | |
827 | */ | |
828 | for (max_valid = 0, i = 0; i < MAX_LIMITS; i++) { | |
829 | limits[i].size = size; | |
830 | ||
831 | if (i == 0) | |
832 | limits[i].low_water = size * 2; | |
833 | else { | |
834 | if ((limits[i - 1].size / 2) > HI_WATER_DEFAULT) | |
835 | limits[i].low_water = size + (limits[i - 1].size / 2); | |
836 | else | |
837 | limits[i].low_water = size + limits[i - 1].size; | |
838 | } | |
c3a08f59 A |
839 | |
840 | if (i) { | |
841 | /* | |
842 | * make the first 2 files the same size | |
843 | */ | |
844 | size = size * 2; | |
83f6dbe8 A |
845 | if (size > memsize) |
846 | break; | |
c3a08f59 A |
847 | } |
848 | max_valid++; | |
849 | } | |
850 | if (max_valid >= MAX_LIMITS) | |
851 | max_valid = MAX_LIMITS - 1; | |
852 | ||
853 | hi_water = HI_WATER_DEFAULT; | |
854 | } | |
1815bff5 | 855 | local_hi_water = hi_water; |
c3a08f59 A |
856 | |
857 | if((limits[0].low_water != 0) && (limits[0].low_water <= (limits[0].size + hi_water))) { | |
83f6dbe8 A |
858 | (void)fprintf(stderr, "dynamic_pager: low water trigger must be larger than size + hi_water\n"); |
859 | exit(EXIT_FAILURE); | |
1815bff5 A |
860 | } |
861 | argc -= optind; | |
862 | argv += optind; | |
c3a08f59 | 863 | |
ef8ad44b | 864 | paging_setup(flags, limits[0].size, priority, limits[0].low_water, hi_water, |
2fc1e207 | 865 | encrypted_swap); |
c3a08f59 | 866 | |
1815bff5 A |
867 | return (0); |
868 | } |