1/*
2 * Copyright 2005, Axel D��rfler, axeld@pinc-software.de
3 * All rights reserved. Distributed under the terms of the MIT License.
4 *
5 * Copyright 2010-2012 Haiku, Inc. All rights reserved.
6 * Distributed under the terms of the MIT License.
7 *
8 * Authors:
9 *      Hamish Morrison, hamish@lavabit.com
10 *      Alexander von Gluck, kallisti5@unixzen.com
11 */
12
13
14#include "Settings.h"
15
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19
20#include <AutoDeleter.h>
21#include <AutoDeleterDrivers.h>
22#include <File.h>
23#include <FindDirectory.h>
24#include <Path.h>
25#include <VolumeRoster.h>
26
27#include <driver_settings.h>
28
29
30static const char* const kWindowSettingsFile = "virtualmemory_preferences";
31static const char* const kVirtualMemorySettings = "virtual_memory";
32static const off_t kMegaByte = 1024 * 1024;
33static const off_t kGigaByte = kMegaByte * 1024;
34
35
36Settings::Settings()
37{
38	fDefaultSettings.enabled = true;
39	fDefaultSettings.automatic = true;
40
41	system_info sysInfo;
42	get_system_info(&sysInfo);
43
44	fDefaultSettings.size = (off_t)sysInfo.max_pages * B_PAGE_SIZE;
45	if (fDefaultSettings.size <= kGigaByte) {
46		// Memory under 1GB? double the swap
47		// This matches the behaviour of the kernel
48		fDefaultSettings.size *= 2;
49	}
50
51	fDefaultSettings.volume = dev_for_path("/boot");
52}
53
54
55void
56Settings::SetSwapEnabled(bool enabled, bool revertable)
57{
58	fCurrentSettings.enabled = enabled;
59	if (!revertable)
60		fInitialSettings.enabled = enabled;
61}
62
63
64void
65Settings::SetSwapAutomatic(bool automatic, bool revertable)
66{
67	fCurrentSettings.automatic = automatic;
68	if (!revertable)
69		fInitialSettings.automatic = automatic;
70}
71
72
73void
74Settings::SetSwapSize(off_t size, bool revertable)
75{
76	fCurrentSettings.size = size;
77	if (!revertable)
78		fInitialSettings.size = size;
79}
80
81
82void
83Settings::SetSwapVolume(dev_t volume, bool revertable)
84{
85	fCurrentSettings.volume = volume;
86	if (!revertable)
87		fInitialSettings.volume = volume;
88
89}
90
91
92void
93Settings::SetWindowPosition(BPoint position)
94{
95	fWindowPosition = position;
96}
97
98
99status_t
100Settings::ReadWindowSettings()
101{
102	BPath path;
103	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
104		return B_ERROR;
105
106	path.Append(kWindowSettingsFile);
107	BFile file;
108	if (file.SetTo(path.Path(), B_READ_ONLY) != B_OK)
109		return B_ERROR;
110
111	if (file.Read(&fWindowPosition, sizeof(BPoint)) == sizeof(BPoint))
112		return B_OK;
113
114	return B_ERROR;
115}
116
117
118status_t
119Settings::WriteWindowSettings()
120{
121	BPath path;
122	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) < B_OK)
123		return B_ERROR;
124
125	path.Append(kWindowSettingsFile);
126
127	BFile file;
128	if (file.SetTo(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE)
129		!= B_OK)
130		return B_ERROR;
131
132	file.Write(&fWindowPosition, sizeof(BPoint));
133	return B_OK;
134}
135
136
137status_t
138Settings::ReadSwapSettings()
139{
140	DriverSettingsUnloader settings(
141		load_driver_settings(kVirtualMemorySettings));
142	if (!settings.IsSet())
143		return kErrorSettingsNotFound;
144
145	const char* enabled = get_driver_parameter(settings.Get(),
146		"vm", NULL, NULL);
147	const char* automatic = get_driver_parameter(settings.Get(),
148		"swap_auto", NULL, NULL);
149	const char* size = get_driver_parameter(settings.Get(),
150		"swap_size", NULL, NULL);
151	const char* volume = get_driver_parameter(settings.Get(),
152		"swap_volume_name", NULL, NULL);
153	const char* device = get_driver_parameter(settings.Get(),
154		"swap_volume_device", NULL, NULL);
155	const char* filesystem = get_driver_parameter(settings.Get(),
156		"swap_volume_filesystem", NULL, NULL);
157	const char* capacity = get_driver_parameter(settings.Get(),
158		"swap_volume_capacity", NULL, NULL);
159
160	if (enabled == NULL	|| automatic == NULL || size == NULL || device == NULL
161		|| volume == NULL || capacity == NULL || filesystem == NULL)
162		return kErrorSettingsInvalid;
163
164	off_t volCapacity = atoll(capacity);
165
166	SetSwapEnabled(get_driver_boolean_parameter(settings.Get(),
167		"vm", true, false));
168	SetSwapAutomatic(get_driver_boolean_parameter(settings.Get(),
169		"swap_auto", true, false));
170	SetSwapSize(atoll(size));
171
172	int32 bestScore = -1;
173	dev_t bestVol = -1;
174
175	BVolume vol;
176	fs_info volStat;
177	BVolumeRoster roster;
178	while (roster.GetNextVolume(&vol) == B_OK) {
179		if (!vol.IsPersistent() || vol.IsReadOnly() || vol.IsRemovable()
180			|| vol.IsShared())
181			continue;
182		if (fs_stat_dev(vol.Device(), &volStat) == 0) {
183			int32 score = 0;
184			if (strcmp(volume, volStat.volume_name) == 0)
185				score += 4;
186			if (strcmp(device, volStat.device_name) == 0)
187				score += 3;
188			if (volCapacity == volStat.total_blocks * volStat.block_size)
189				score += 2;
190			if (strcmp(filesystem, volStat.fsh_name) == 0)
191				score += 1;
192			if (score >= 4 && score > bestScore) {
193				bestVol = vol.Device();
194				bestScore = score;
195			}
196		}
197	}
198
199	SetSwapVolume(bestVol);
200	fInitialSettings = fCurrentSettings;
201
202	if (bestVol < 0)
203		return kErrorVolumeNotFound;
204
205	return B_OK;
206}
207
208
209status_t
210Settings::WriteSwapSettings()
211{
212	BPath path;
213	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
214		return B_ERROR;
215
216	path.Append("kernel/drivers");
217	path.Append(kVirtualMemorySettings);
218
219	BFile file;
220	if (file.SetTo(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE)
221		!= B_OK)
222		return B_ERROR;
223
224	fs_info info;
225	if (fs_stat_dev(SwapVolume(), &info) != 0)
226		return B_ERROR;
227
228	char buffer[1024];
229	snprintf(buffer, sizeof(buffer), "vm %s\nswap_auto %s\nswap_size %"
230		B_PRIdOFF "\nswap_volume_name %s\nswap_volume_device %s\n"
231		"swap_volume_filesystem %s\nswap_volume_capacity %" B_PRIdOFF "\n",
232		SwapEnabled() ? "on" : "off", SwapAutomatic() ? "yes" : "no",
233		SwapSize(), info.volume_name, info.device_name, info.fsh_name,
234		info.total_blocks * info.block_size);
235
236	file.Write(buffer, strlen(buffer));
237	return B_OK;
238}
239
240
241bool
242Settings::IsRevertable()
243{
244	return SwapEnabled() != fInitialSettings.enabled
245		|| SwapAutomatic() != fInitialSettings.automatic
246		|| SwapSize() != fInitialSettings.size
247		|| SwapVolume() != fInitialSettings.volume;
248}
249
250
251void
252Settings::RevertSwapSettings()
253{
254	SetSwapEnabled(fInitialSettings.enabled);
255	SetSwapAutomatic(fInitialSettings.automatic);
256	SetSwapSize(fInitialSettings.size);
257	SetSwapVolume(fInitialSettings.volume);
258}
259
260
261bool
262Settings::IsDefaultable()
263{
264	return SwapEnabled() != fDefaultSettings.enabled
265		|| SwapAutomatic() != fDefaultSettings.automatic
266		|| SwapSize() != fDefaultSettings.size
267		|| SwapVolume() != fDefaultSettings.volume;
268}
269
270
271void
272Settings::DefaultSwapSettings(bool revertable)
273{
274	SetSwapEnabled(fDefaultSettings.enabled);
275	SetSwapAutomatic(fDefaultSettings.automatic);
276	SetSwapSize(fDefaultSettings.size);
277	SetSwapVolume(fDefaultSettings.volume);
278	if (!revertable)
279		fInitialSettings = fDefaultSettings;
280}
281