⛏️ index : haiku.git

/*
 * Copyright 2005, Axel DΓΆrfler, axeld@pinc-software.de
 * All rights reserved. Distributed under the terms of the MIT License.
 *
 * Copyright 2010-2012 Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *      Hamish Morrison, hamish@lavabit.com
 *      Alexander von Gluck, kallisti5@unixzen.com
 */


#include "Settings.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <AutoDeleter.h>
#include <AutoDeleterDrivers.h>
#include <File.h>
#include <FindDirectory.h>
#include <Path.h>
#include <VolumeRoster.h>

#include <driver_settings.h>


static const char* const kWindowSettingsFile = "virtualmemory_preferences";
static const char* const kVirtualMemorySettings = "virtual_memory";
static const off_t kMegaByte = 1024 * 1024;
static const off_t kGigaByte = kMegaByte * 1024;


Settings::Settings()
{
	fDefaultSettings.enabled = true;
	fDefaultSettings.automatic = true;

	system_info sysInfo;
	get_system_info(&sysInfo);

	fDefaultSettings.size = (off_t)sysInfo.max_pages * B_PAGE_SIZE;
	if (fDefaultSettings.size <= kGigaByte) {
		// Memory under 1GB? double the swap
		// This matches the behaviour of the kernel
		fDefaultSettings.size *= 2;
	}

	fDefaultSettings.volume = dev_for_path("/boot");
}


void
Settings::SetSwapEnabled(bool enabled, bool revertable)
{
	fCurrentSettings.enabled = enabled;
	if (!revertable)
		fInitialSettings.enabled = enabled;
}


void
Settings::SetSwapAutomatic(bool automatic, bool revertable)
{
	fCurrentSettings.automatic = automatic;
	if (!revertable)
		fInitialSettings.automatic = automatic;
}


void
Settings::SetSwapSize(off_t size, bool revertable)
{
	fCurrentSettings.size = size;
	if (!revertable)
		fInitialSettings.size = size;
}


void
Settings::SetSwapVolume(dev_t volume, bool revertable)
{
	fCurrentSettings.volume = volume;
	if (!revertable)
		fInitialSettings.volume = volume;

}


void
Settings::SetWindowPosition(BPoint position)
{
	fWindowPosition = position;
}


status_t
Settings::ReadWindowSettings()
{
	BPath path;
	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
		return B_ERROR;

	path.Append(kWindowSettingsFile);
	BFile file;
	if (file.SetTo(path.Path(), B_READ_ONLY) != B_OK)
		return B_ERROR;

	if (file.Read(&fWindowPosition, sizeof(BPoint)) == sizeof(BPoint))
		return B_OK;

	return B_ERROR;
}


status_t
Settings::WriteWindowSettings()
{
	BPath path;
	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) < B_OK)
		return B_ERROR;

	path.Append(kWindowSettingsFile);

	BFile file;
	if (file.SetTo(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE)
		!= B_OK)
		return B_ERROR;

	file.Write(&fWindowPosition, sizeof(BPoint));
	return B_OK;
}


status_t
Settings::ReadSwapSettings()
{
	DriverSettingsUnloader settings(
		load_driver_settings(kVirtualMemorySettings));
	if (!settings.IsSet())
		return kErrorSettingsNotFound;

	const char* enabled = get_driver_parameter(settings.Get(),
		"vm", NULL, NULL);
	const char* automatic = get_driver_parameter(settings.Get(),
		"swap_auto", NULL, NULL);
	const char* size = get_driver_parameter(settings.Get(),
		"swap_size", NULL, NULL);
	const char* volume = get_driver_parameter(settings.Get(),
		"swap_volume_name", NULL, NULL);
	const char* device = get_driver_parameter(settings.Get(),
		"swap_volume_device", NULL, NULL);
	const char* filesystem = get_driver_parameter(settings.Get(),
		"swap_volume_filesystem", NULL, NULL);
	const char* capacity = get_driver_parameter(settings.Get(),
		"swap_volume_capacity", NULL, NULL);

	if (enabled == NULL	|| automatic == NULL || size == NULL || device == NULL
		|| volume == NULL || capacity == NULL || filesystem == NULL)
		return kErrorSettingsInvalid;

	off_t volCapacity = atoll(capacity);

	SetSwapEnabled(get_driver_boolean_parameter(settings.Get(),
		"vm", true, false));
	SetSwapAutomatic(get_driver_boolean_parameter(settings.Get(),
		"swap_auto", true, false));
	SetSwapSize(atoll(size));

	int32 bestScore = -1;
	dev_t bestVol = -1;

	BVolume vol;
	fs_info volStat;
	BVolumeRoster roster;
	while (roster.GetNextVolume(&vol) == B_OK) {
		if (!vol.IsPersistent() || vol.IsReadOnly() || vol.IsRemovable()
			|| vol.IsShared())
			continue;
		if (fs_stat_dev(vol.Device(), &volStat) == 0) {
			int32 score = 0;
			if (strcmp(volume, volStat.volume_name) == 0)
				score += 4;
			if (strcmp(device, volStat.device_name) == 0)
				score += 3;
			if (volCapacity == volStat.total_blocks * volStat.block_size)
				score += 2;
			if (strcmp(filesystem, volStat.fsh_name) == 0)
				score += 1;
			if (score >= 4 && score > bestScore) {
				bestVol = vol.Device();
				bestScore = score;
			}
		}
	}

	SetSwapVolume(bestVol);
	fInitialSettings = fCurrentSettings;

	if (bestVol < 0)
		return kErrorVolumeNotFound;

	return B_OK;
}


status_t
Settings::WriteSwapSettings()
{
	BPath path;
	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
		return B_ERROR;

	path.Append("kernel/drivers");
	path.Append(kVirtualMemorySettings);

	BFile file;
	if (file.SetTo(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE)
		!= B_OK)
		return B_ERROR;

	fs_info info;
	if (fs_stat_dev(SwapVolume(), &info) != 0)
		return B_ERROR;

	char buffer[1024];
	snprintf(buffer, sizeof(buffer), "vm %s\nswap_auto %s\nswap_size %"
		B_PRIdOFF "\nswap_volume_name %s\nswap_volume_device %s\n"
		"swap_volume_filesystem %s\nswap_volume_capacity %" B_PRIdOFF "\n",
		SwapEnabled() ? "on" : "off", SwapAutomatic() ? "yes" : "no",
		SwapSize(), info.volume_name, info.device_name, info.fsh_name,
		info.total_blocks * info.block_size);

	file.Write(buffer, strlen(buffer));
	return B_OK;
}


bool
Settings::IsRevertable()
{
	return SwapEnabled() != fInitialSettings.enabled
		|| SwapAutomatic() != fInitialSettings.automatic
		|| SwapSize() != fInitialSettings.size
		|| SwapVolume() != fInitialSettings.volume;
}


void
Settings::RevertSwapSettings()
{
	SetSwapEnabled(fInitialSettings.enabled);
	SetSwapAutomatic(fInitialSettings.automatic);
	SetSwapSize(fInitialSettings.size);
	SetSwapVolume(fInitialSettings.volume);
}


bool
Settings::IsDefaultable()
{
	return SwapEnabled() != fDefaultSettings.enabled
		|| SwapAutomatic() != fDefaultSettings.automatic
		|| SwapSize() != fDefaultSettings.size
		|| SwapVolume() != fDefaultSettings.volume;
}


void
Settings::DefaultSwapSettings(bool revertable)
{
	SetSwapEnabled(fDefaultSettings.enabled);
	SetSwapAutomatic(fDefaultSettings.automatic);
	SetSwapSize(fDefaultSettings.size);
	SetSwapVolume(fDefaultSettings.volume);
	if (!revertable)
		fInitialSettings = fDefaultSettings;
}