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