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