Skip to content
Snippets Groups Projects
Mhwd.cpp 36.1 KiB
Newer Older
Philip Müller's avatar
Philip Müller committed
/*
 *  This file is part of the mhwd - Manjaro Hardware Detection project
 *  
dec's avatar
dec committed
 *  mhwd - Manjaro Hardware Detection
 *  Roland Singer <roland@manjaro.org>
 *  Łukasz Matysiak <december0123@gmail.com>
Philip Müller's avatar
Philip Müller committed
 *  Filipe Marques <eagle.software3@gmail.com>
Philip Müller's avatar
Philip Müller committed
 *
dec's avatar
dec committed
 *  Copyright (C) 2007 Free Software Foundation, Inc.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
Philip Müller's avatar
Philip Müller committed
 */

dec's avatar
dec committed
#include "Mhwd.hpp"
#include "vita/string.hpp"

december0123's avatar
december0123 committed
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
Philip Müller's avatar
Philip Müller committed

#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstdlib>
Philip Müller's avatar
Philip Müller committed
#include <cstring>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <memory>
#include <stdexcept>
Philip Müller's avatar
Philip Müller committed
#include <string>
#include <vector>

bool Mhwd::performTransaction(std::shared_ptr<Config> config, MHWD::TRANSACTIONTYPE transactionType)
Philip Müller's avatar
Philip Müller committed
{
    Transaction transaction (data_, config, transactionType,
            arguments_.FORCE);
december0123's avatar
december0123 committed

    // Print things to do
    if (MHWD::TRANSACTIONTYPE::INSTALL == transactionType)
december0123's avatar
december0123 committed
    {
        // Print conflicts
        if (!transaction.conflictedConfigs_.empty())
        {
            std::string conflicts;

            for (auto&& conflictedConfig : transaction.conflictedConfigs_)
december0123's avatar
december0123 committed
            {
                conflicts += " " + conflictedConfig->name_;
            }

december0123's avatar
december0123 committed
            printer_.printError("config '" + config->name_ + "' conflicts with config(s):" +
                    conflicts);
december0123's avatar
december0123 committed
            return false;
        }

        // Print dependencies
december0123's avatar
december0123 committed
        else if (!transaction.dependencyConfigs_.empty())
december0123's avatar
december0123 committed
        {
            std::string dependencies;

            for (auto&& dependencyConfig : transaction.dependencyConfigs_)
december0123's avatar
december0123 committed
            {
                dependencies += " " + dependencyConfig->name_;
            }

december0123's avatar
december0123 committed
            printer_.printStatus("Dependencies to install:" + dependencies +
                    "\nProceed with installation? [Y/n]");
            std::string input;
            std::getline(std::cin, input);
            return proceedWithInstallation(input);
december0123's avatar
december0123 committed
        }
    }
    else if (MHWD::TRANSACTIONTYPE::REMOVE == transactionType)
december0123's avatar
december0123 committed
    {
        // Print requirements
        if (!transaction.configsRequirements_.empty())
        {
            std::string requirements;

            for (auto&& requirement : transaction.configsRequirements_)
december0123's avatar
december0123 committed
            {
                requirements += " " + requirement->name_;
            }

december0123's avatar
december0123 committed
            printer_.printError("config '" + config->name_ + "' is required by config(s):" +
                    requirements);
december0123's avatar
december0123 committed
            return false;
        }
    }

    MHWD::STATUS status = performTransaction(transaction);
    switch (status)
    {
    	case MHWD::STATUS::SUCCESS:
    		break;
    	case MHWD::STATUS::ERROR_CONFLICTS:
    		printer_.printError("config '" + config->name_ +
    				"' conflicts with installed config(s)!");
    		break;
    	case MHWD::STATUS::ERROR_REQUIREMENTS:
    		printer_.printError("config '" + config->name_ +
    				"' is required by installed config(s)!");
    		break;
    	case MHWD::STATUS::ERROR_NOT_INSTALLED:
    		printer_.printError("config '" + config->name_ + "' is not installed!");
    		break;
    	case MHWD::STATUS::ERROR_ALREADY_INSTALLED:
            printer_.printWarning("a version of config '" + config->name_ +
					"' is already installed!\nUse -f/--force to force installation...");
            break;
    	case MHWD::STATUS::ERROR_NO_MATCH_LOCAL_CONFIG:
    		printer_.printError("passed config does not match with installed config!");
    		break;
    	case MHWD::STATUS::ERROR_SCRIPT_FAILED:
    		printer_.printError("script failed!");
    		break;
    	case MHWD::STATUS::ERROR_SET_DATABASE:
    		printer_.printError("failed to set database!");
    		break;
december0123's avatar
december0123 committed
    }

    data_.updateInstalledConfigData();

    return (MHWD::STATUS::SUCCESS == status);
Philip Müller's avatar
Philip Müller committed
}

bool Mhwd::proceedWithInstallation(const std::string& input) const
    if ((input.length() == 1) && (('y' == input[0]) || ('Y' == input[0])))
    else if (0 == input.length())
Philip Müller's avatar
Philip Müller committed
bool Mhwd::isUserRoot() const
{
december0123's avatar
december0123 committed
    constexpr unsigned short ROOT_UID = 0;
    if (ROOT_UID != getuid())
december0123's avatar
december0123 committed
    {
        return false;
    }
    return true;
Philip Müller's avatar
Philip Müller committed
}

std::vector<std::string> Mhwd::checkEnvironment() const
Philip Müller's avatar
Philip Müller committed
{
    if (!dirExists(MHWD_USB_CONFIG_DIR))
december0123's avatar
december0123 committed
    {
    	missingDirs.emplace_back(MHWD_USB_CONFIG_DIR);
december0123's avatar
december0123 committed
    }
    if (!dirExists(MHWD_PCI_CONFIG_DIR))
december0123's avatar
december0123 committed
    {
    	missingDirs.emplace_back(MHWD_PCI_CONFIG_DIR);
december0123's avatar
december0123 committed
    }
    if (!dirExists(MHWD_USB_DATABASE_DIR))
december0123's avatar
december0123 committed
    {
    	missingDirs.emplace_back(MHWD_USB_DATABASE_DIR);
december0123's avatar
december0123 committed
    }
    if (!dirExists(MHWD_PCI_DATABASE_DIR))
december0123's avatar
december0123 committed
    {
    	missingDirs.emplace_back(MHWD_PCI_DATABASE_DIR);
Philip Müller's avatar
Philip Müller committed
}

december0123's avatar
december0123 committed
std::shared_ptr<Config> Mhwd::getInstalledConfig(const std::string& configName,
december0123's avatar
december0123 committed
        const std::string& configType)
Philip Müller's avatar
Philip Müller committed
{
december0123's avatar
december0123 committed
    std::vector<std::shared_ptr<Config>>* installedConfigs;
december0123's avatar
december0123 committed

    // Get the right configs
    if ("USB" == configType)
december0123's avatar
december0123 committed
    {
        installedConfigs = &data_.installedUSBConfigs;
    }
    else
    {
        installedConfigs = &data_.installedPCIConfigs;
    }

    auto installedConfig = std::find_if(installedConfigs->begin(), installedConfigs->end(),
            [configName](const std::shared_ptr<Config>& config) {
                return configName == config->name_;
            });

    if (installedConfig != installedConfigs->end())
december0123's avatar
december0123 committed
    {
        return *installedConfig;
december0123's avatar
december0123 committed
    }
    return nullptr;
Philip Müller's avatar
Philip Müller committed
}

december0123's avatar
december0123 committed
std::shared_ptr<Config> Mhwd::getDatabaseConfig(const std::string& configName,
        const std::string& configType)
Philip Müller's avatar
Philip Müller committed
{
december0123's avatar
december0123 committed
    std::vector<std::shared_ptr<Config>>* allConfigs;
december0123's avatar
december0123 committed

    // Get the right configs
    if ("USB" == configType)
december0123's avatar
december0123 committed
    {
        allConfigs = &data_.allUSBConfigs;
    }
    else
    {
        allConfigs = &data_.allPCIConfigs;
    }

    auto config = std::find_if(allConfigs->begin(), allConfigs->end(),
    		[configName](const std::shared_ptr<Config>& config) {
                return config->name_ == configName;
            });
    if (config != allConfigs->end())
december0123's avatar
december0123 committed
    {
        return *config;
december0123's avatar
december0123 committed
    }
    return nullptr;
Philip Müller's avatar
Philip Müller committed
}

december0123's avatar
december0123 committed
std::shared_ptr<Config> Mhwd::getAvailableConfig(const std::string& configName,
        const std::string& configType)
Philip Müller's avatar
Philip Müller committed
{
december0123's avatar
december0123 committed
    std::vector<std::shared_ptr<Device>> *devices;
december0123's avatar
december0123 committed

    // Get the right devices
    if ("USB" == configType)
december0123's avatar
december0123 committed
    {
        devices = &data_.USBDevices;
    }
    else
    {
        devices = &data_.PCIDevices;
    }

    for (auto&& device = devices->begin(); device != devices->end();
december0123's avatar
december0123 committed
    {
december0123's avatar
december0123 committed
        if ((*device)->availableConfigs_.empty())
december0123's avatar
december0123 committed
        {
            continue;
        }
        else
        {
            auto& availableConfigs = (*device)->availableConfigs_;
            auto availableConfig = std::find_if(availableConfigs.begin(), availableConfigs.end(),
                    [configName](const std::shared_ptr<Config>& config){
                        return config->name_ == configName;
                    });
            if (availableConfig != availableConfigs.end())
december0123's avatar
december0123 committed
            {
                return *availableConfig;
december0123's avatar
december0123 committed
            }
        }
    }
    return nullptr;
Philip Müller's avatar
Philip Müller committed
}

MHWD::STATUS Mhwd::performTransaction(const Transaction& transaction)
Philip Müller's avatar
Philip Müller committed
{
    if ((MHWD::TRANSACTIONTYPE::INSTALL == transaction.type_) &&
            !transaction.conflictedConfigs_.empty())
december0123's avatar
december0123 committed
    {
        return MHWD::STATUS::ERROR_CONFLICTS;
    }
    else if ((MHWD::TRANSACTIONTYPE::REMOVE == transaction.type_)
            && !transaction.configsRequirements_.empty())
december0123's avatar
december0123 committed
    {
        return MHWD::STATUS::ERROR_REQUIREMENTS;
    }
december0123's avatar
december0123 committed
    else
    {
        // Check if already installed
        std::shared_ptr<Config> installedConfig{getInstalledConfig(transaction.config_->name_,
                transaction.config_->type_)};
december0123's avatar
december0123 committed
        MHWD::STATUS status = MHWD::STATUS::SUCCESS;
        if ((MHWD::TRANSACTIONTYPE::REMOVE == transaction.type_)
                || (installedConfig != nullptr && transaction.isAllowedToReinstall()))
december0123's avatar
december0123 committed
        {
            if (nullptr == installedConfig)
december0123's avatar
december0123 committed
            {
december0123's avatar
december0123 committed
                return MHWD::STATUS::ERROR_NOT_INSTALLED;
december0123's avatar
december0123 committed
                printer_.printMessage(MHWD::MESSAGETYPE::REMOVE_START, installedConfig->name_);
                if (MHWD::STATUS::SUCCESS != (status = uninstallConfig(installedConfig.get())))
december0123's avatar
december0123 committed
                {
                    return status;
                }
                else
                {
december0123's avatar
december0123 committed
                    printer_.printMessage(MHWD::MESSAGETYPE::REMOVE_END, installedConfig->name_);
december0123's avatar
december0123 committed
        }
        if (MHWD::TRANSACTIONTYPE::INSTALL == transaction.type_)
december0123's avatar
december0123 committed
        {
            // Check if already installed but not allowed to reinstall
            if ((nullptr != installedConfig) && !transaction.isAllowedToReinstall())
december0123's avatar
december0123 committed
            {
december0123's avatar
december0123 committed
                return MHWD::STATUS::ERROR_ALREADY_INSTALLED;
december0123's avatar
december0123 committed
                // Install all dependencies first
                for (auto&& dependencyConfig = transaction.dependencyConfigs_.end() - 1;
                        dependencyConfig != transaction.dependencyConfigs_.begin() - 1;
december0123's avatar
december0123 committed
                        --dependencyConfig)
                {
                    printer_.printMessage(MHWD::MESSAGETYPE::INSTALLDEPENDENCY_START,
                            (*dependencyConfig)->name_);
                    if (MHWD::STATUS::SUCCESS != (status = installConfig((*dependencyConfig))))
december0123's avatar
december0123 committed
                    {
                        return status;
                    }
                    else
                    {
                        printer_.printMessage(MHWD::MESSAGETYPE::INSTALLDEPENDENCY_END,
                                (*dependencyConfig)->name_);
                    }
                }

                printer_.printMessage(MHWD::MESSAGETYPE::INSTALL_START, transaction.config_->name_);
                if (MHWD::STATUS::SUCCESS != (status = installConfig(transaction.config_)))
december0123's avatar
december0123 committed
                {
                    return status;
                }
                else
                {
                    printer_.printMessage(MHWD::MESSAGETYPE::INSTALL_END,
                            transaction.config_->name_);
december0123's avatar
december0123 committed
                }
december0123's avatar
december0123 committed
        return status;
    }
Philip Müller's avatar
Philip Müller committed
}

bool Mhwd::copyDirectory(const std::string& source, const std::string& destination)
Philip Müller's avatar
Philip Müller committed
{
december0123's avatar
december0123 committed
    struct stat filestatus;

    if (0 != lstat(destination.c_str(), &filestatus))
december0123's avatar
december0123 committed
    {
        if (!createDir(destination))
        {
            return false;
        }
    }
    else if (S_ISREG(filestatus.st_mode))
    {
        return false;
    }
    else if (S_ISDIR(filestatus.st_mode))
    {
        if (!removeDirectory(destination))
        {
            return false;
        }

        if (!createDir(destination))
        {
            return false;
        }
    }
    struct dirent *dir;
    DIR *d = opendir(source.c_str());

    if (!d)
    {
        return false;
    }
december0123's avatar
december0123 committed
    {
        bool success = true;
        while ((dir = readdir(d)) != nullptr)
december0123's avatar
december0123 committed
        {
            if (("." == filename) || (".." == filename) || ("" == filename))
december0123's avatar
december0123 committed
            {
december0123's avatar
december0123 committed
            }
december0123's avatar
december0123 committed
            {
Łukasz Matysiak's avatar
Łukasz Matysiak committed
                std::string sourcePath {source + "/" + filename};
                std::string destinationPath {destination + "/" + filename};
december0123's avatar
december0123 committed
                lstat(sourcePath.c_str(), &filestatus);

                if (S_ISREG(filestatus.st_mode))
                {
december0123's avatar
december0123 committed
                    if (!copyFile(sourcePath, destinationPath))
                    {
                        success = false;
                    }
                }
                else if (S_ISDIR(filestatus.st_mode))
                {
december0123's avatar
december0123 committed
                    if (!copyDirectory(sourcePath, destinationPath))
        return success;
december0123's avatar
december0123 committed
    }
Philip Müller's avatar
Philip Müller committed
}

bool Mhwd::copyFile(const std::string& source, const std::string destination, const mode_t mode)
Philip Müller's avatar
Philip Müller committed
{
december0123's avatar
december0123 committed
    std::ifstream src(source, std::ios::binary);
    std::ofstream dst(destination, std::ios::binary);
    if (!src || !dst)
december0123's avatar
december0123 committed
    {
december0123's avatar
december0123 committed
    }

    dst << src.rdbuf();
    mode_t process_mask = umask(0);
    chmod(destination.c_str(), mode);
    umask(process_mask);
    return true;
Philip Müller's avatar
Philip Müller committed
}

bool Mhwd::removeDirectory(const std::string& directory)
Philip Müller's avatar
Philip Müller committed
{
december0123's avatar
december0123 committed
    DIR *d = opendir(directory.c_str());

    if (!d)
    {
        return false;
    }
    else
    {
        bool success = true;
        struct dirent *dir;
        while ((dir = readdir(d)) != nullptr)
        {
            std::string filename {dir->d_name};
            if (("." == filename) || (".." == filename) || ("" == filename))
december0123's avatar
december0123 committed
            {
                continue;
            }
            else
            {
                std::string filepath {directory + "/" + filename};
december0123's avatar
december0123 committed
                struct stat filestatus;
                lstat(filepath.c_str(), &filestatus);

                if (S_ISREG(filestatus.st_mode))
                {
                    if (0 != unlink(filepath.c_str()))
december0123's avatar
december0123 committed
                    {
                        success = false;
                    }
                }
                else if (S_ISDIR(filestatus.st_mode))
                {
                    if (!removeDirectory(filepath))
                    {
                        success = false;
                    }
                }
            }
        }
        closedir(d);

        if (0 != rmdir(directory.c_str()))
december0123's avatar
december0123 committed
        {
            success = false;
        }
        return success;
    }
Philip Müller's avatar
Philip Müller committed
}

bool Mhwd::dirExists(const std::string& path) const
Philip Müller's avatar
Philip Müller committed
{
december0123's avatar
december0123 committed
    struct stat filestatus;
    if (0 != stat(path.c_str(), &filestatus))
december0123's avatar
december0123 committed
    {
        return false;
    }
Philip Müller's avatar
Philip Müller committed
}

bool Mhwd::createDir(const std::string& path, const mode_t mode)
Philip Müller's avatar
Philip Müller committed
{
december0123's avatar
december0123 committed
    mode_t process_mask = umask(0);
    int ret = mkdir(path.c_str(), mode);
    umask(process_mask);
Philip Müller's avatar
Philip Müller committed

december0123's avatar
december0123 committed
    constexpr unsigned short SUCCESS = 0;
    return (SUCCESS == ret);
Philip Müller's avatar
Philip Müller committed
}

december0123's avatar
december0123 committed
MHWD::STATUS Mhwd::installConfig(std::shared_ptr<Config> config)
Philip Müller's avatar
Philip Müller committed
{
december0123's avatar
december0123 committed
    std::string databaseDir;
    if ("USB" == config->type_)
december0123's avatar
december0123 committed
    {
        databaseDir = MHWD_USB_DATABASE_DIR;
    }
    else
    {
        databaseDir = MHWD_PCI_DATABASE_DIR;
    }

    if (!runScript(config, MHWD::TRANSACTIONTYPE::INSTALL))
    {
        return MHWD::STATUS::ERROR_SCRIPT_FAILED;
    }

    if (!copyDirectory(config->basePath_, databaseDir + "/" + config->name_))
    {
        return MHWD::STATUS::ERROR_SET_DATABASE;
    }

    // Installed config vectors have to be updated manual with updateInstalledConfigData(Data*)

    return MHWD::STATUS::SUCCESS;
Philip Müller's avatar
Philip Müller committed
}

MHWD::STATUS Mhwd::uninstallConfig(Config *config)
{
    std::shared_ptr<Config> installedConfig{getInstalledConfig(config->name_, config->type_)};
december0123's avatar
december0123 committed

    // Check if installed
    if (nullptr == installedConfig)
december0123's avatar
december0123 committed
    {
        return MHWD::STATUS::ERROR_NOT_INSTALLED;
    }
    else if (installedConfig->basePath_ != config->basePath_)
    {
        return MHWD::STATUS::ERROR_NO_MATCH_LOCAL_CONFIG;
    }
    else
december0123's avatar
december0123 committed
    {
        // TODO: Should we check for local requirements here?
        // Run script
        if (!runScript(installedConfig, MHWD::TRANSACTIONTYPE::REMOVE))
        {
            return MHWD::STATUS::ERROR_SCRIPT_FAILED;
        }
        if (!removeDirectory(installedConfig->basePath_))
        {
            return MHWD::STATUS::ERROR_SET_DATABASE;
        }
        // Installed config vectors have to be updated manual with updateInstalledConfigData(Data*)

        return MHWD::STATUS::SUCCESS;
    }
Philip Müller's avatar
Philip Müller committed
}

december0123's avatar
december0123 committed
bool Mhwd::runScript(std::shared_ptr<Config> config, MHWD::TRANSACTIONTYPE operationType)
Philip Müller's avatar
Philip Müller committed
{
december0123's avatar
december0123 committed
    std::string cmd = "exec " + std::string(MHWD_SCRIPT_PATH);

    if (MHWD::TRANSACTIONTYPE::REMOVE == operationType)
december0123's avatar
december0123 committed
    {
        cmd += " --remove";
    }
    else
    {
        cmd += " --install";
    }

    if (data_.environment.syncPackageManagerDatabase)
    {
        cmd += " --sync";
    }

    cmd += " --cachedir \"" + data_.environment.PMCachePath + "\"";
    cmd += " --pmconfig \"" + data_.environment.PMConfigPath + "\"";
    cmd += " --pmroot \"" + data_.environment.PMRootPath + "\"";
    cmd += " --config \"" + config->configPath_ + "\"";

    // Set all config devices as argument
december0123's avatar
december0123 committed
    std::vector<std::shared_ptr<Device>> foundDevices;
    std::vector<std::shared_ptr<Device>> devices;
    data_.getAllDevicesOfConfig(config, foundDevices);
    for (auto&& foundDevice = foundDevices.begin();
            foundDevice != foundDevices.end(); ++foundDevice)
december0123's avatar
december0123 committed
    {
        bool found = false;

        // Check if already in list
        for (auto&& dev = devices.begin(); dev != devices.end(); ++dev)
december0123's avatar
december0123 committed
        {
            if ((*foundDevice)->sysfsBusID_ == (*dev)->sysfsBusID_
                    && (*foundDevice)->sysfsID_ == (*dev)->sysfsID_)
december0123's avatar
december0123 committed
            {
                found = true;
                break;
            }
        }

        if (!found)
        {
            devices.push_back(std::shared_ptr<Device>{*foundDevice});
    for (auto&& dev = devices.begin(); dev != devices.end(); ++dev)
december0123's avatar
december0123 committed
    {
december0123's avatar
december0123 committed
        Vita::string busID = (*dev)->sysfsBusID_;
        if ("PCI" == config->type_)
december0123's avatar
december0123 committed
        {
            std::vector<Vita::string> split = Vita::string(busID).replace(".", ":").explode(":");
dec's avatar
dec committed
            const unsigned long size = split.size();
december0123's avatar
december0123 committed

            if (size >= 3)
            {
                // Convert to int to remove leading 0
                busID = Vita::string::toStr<int>(std::stoi(split[size - 3], nullptr, 16));
                busID += ":" + Vita::string::toStr<int>(std::stoi(split[size - 2], nullptr, 16));
                busID += ":" + Vita::string::toStr<int>(std::stoi(split[size - 1], nullptr, 16));
december0123's avatar
december0123 committed
        cmd += " --device \"" + (*dev)->classID_ + "|" + (*dev)->vendorID_ + "|" + (*dev)->deviceID_
december0123's avatar
december0123 committed
                + "|" + busID + "\"";
    }

    cmd += " 2>&1";

    FILE *in;

    if (!(in = popen(cmd.c_str(), "r")))
    {
        return false;
    }
    else
    {
        char buff[512];
        while (fgets(buff, sizeof(buff), in) != nullptr)
        {
            printer_.printMessage(MHWD::MESSAGETYPE::CONSOLE_OUTPUT, buff);
december0123's avatar
december0123 committed
        }

        int stat = pclose(in);

        if (WEXITSTATUS(stat) != 0)
        {
            return false;
        }
        else
        {
            // Only one database sync is required
            if (MHWD::TRANSACTIONTYPE::INSTALL == operationType)
december0123's avatar
december0123 committed
            {
                data_.environment.syncPackageManagerDatabase = false;
            }
            return true;
        }
    }
Philip Müller's avatar
Philip Müller committed
}

void Mhwd::setVersionMhwd(std::string versionOfSoftware, std::string yearCopyright)
	version_ = versionOfSoftware;
	year_ = yearCopyright;
void Mhwd::tryToParseCmdLineOptions(int argc, char* argv[], bool& autoConfigureNonFreeDriver,
		std::string& operationType, std::string& autoConfigureClassID)
{
	if (argc <= 1)
	{
		arguments_.LIST_AVAILABLE = true;
	}
	for (int nArg = 1; nArg < argc; ++nArg)
	{
		const std::string option{ argv[nArg] };
		if (("-h" == option) || ("--help" == option))
		{
			printer_.printHelp();
		}
		else if (("-v" == option) || ("--version" == option))
        {
            printer_.printVersion(version_, year_);
        }
		else if (("-f" == option) || ("--force" == option))
		{
			arguments_.FORCE = true;
		}
		else if (("-d" == option) || ("--detail" == option))
		{
			arguments_.DETAIL = true;
		}
		else if (("-la" == option) || ("--listall" == option))
		{
			arguments_.LIST_ALL = true;
		}
		else if (("-li" == option) || ("--listinstalled" == option))
		{
			arguments_.LIST_INSTALLED = true;
		}
		else if (("-l" == option) || ("--list" == option))
		{
			arguments_.LIST_AVAILABLE = true;
		}
		else if (("-lh" == option) || ("--listhardware" == option))
		{
			arguments_.LIST_HARDWARE = true;
		}
		else if ("--pci" == option)
		{
			arguments_.SHOW_PCI = true;
		}
		else if ("--usb" == option)
		{
			arguments_.SHOW_USB = true;
		}
		else if (("-a" == option) || ("--auto" == option))
		{
			if ((nArg + 3) >= argc)
			{
				throw std::runtime_error{"invalid use of option: -a/--auto\n"};
			}
			else
			{
				const std::string deviceType{ argv[nArg + 1] };
				const std::string driverType{ argv[nArg + 2] };
				const std::string classID{ argv[nArg + 3] };
				if ((("pci" != deviceType) && ("usb" != deviceType))
						|| (("free" != driverType) && ("nonfree" != driverType)))
				{
					throw std::runtime_error{"invalid use of option: -a/--auto\n"};
				}
				else
				{
					operationType = Vita::string{ deviceType }.toUpper();
					autoConfigureNonFreeDriver = ("nonfree" == driverType);
					autoConfigureClassID = Vita::string(classID).toLower().trim();
					arguments_.AUTOCONFIGURE = true;
					nArg += 3;
				}
			}
		}
		else if (("-ic" == option) || ("--installcustom" == option))
		{
			if ((nArg + 1) >= argc)
			{
				throw std::runtime_error{"invalid use of option: -ic/--installcustom\n"};
			}
			else
			{
				const std::string deviceType{ argv[++nArg] };
				if (("pci" != deviceType) && ("usb" != deviceType))
				{
					throw std::runtime_error{"invalid use of option: -ic/--installcustom\n"};
				}
				else
				{
					operationType = Vita::string{ deviceType }.toUpper();
					arguments_.CUSTOM_INSTALL = true;
				}
			}
		}
		else if (("-i" == option) || ("--install" == option))
		{
			if ((nArg + 1) >= argc)
			{
				throw std::runtime_error{"invalid use of option: -i/--install\n"};
			}
			else
			{
				const std::string deviceType{ argv[++nArg] };
				if (("pci" != deviceType) && ("usb" != deviceType))
				{
					throw std::runtime_error{"invalid use of option: -i/--install\n"};
				}
				else
				{
					operationType = Vita::string{ deviceType }.toUpper();
					arguments_.INSTALL = true;
				}
			}
		}
		else if (("-r" == option) || ("--remove" == option))
		{
			if ((nArg + 1) >= argc)
			{
				throw std::runtime_error{"invalid use of option: -r/--remove\n"};
			}
			else
			{
				const std::string deviceType{ argv[++nArg] };
				if (("pci" != deviceType) && ("usb" != deviceType))
				{
					throw std::runtime_error{"invalid use of option: -r/--remove\n"};
				}
				else
				{
					operationType = Vita::string{ deviceType }.toUpper();
					arguments_.REMOVE = true;
				}
			}
		}
		else if ("--pmcachedir" == option)
		{
			if (nArg + 1 >= argc)
			{
				throw std::runtime_error{"invalid use of option: --pmcachedir\n"};
			}
			else
			{
				data_.environment.PMCachePath = Vita::string(argv[++nArg]).trim("\"").trim();
			}
		}
		else if ("--pmconfig" == option)
		{
			if (nArg + 1 >= argc)
			{
				throw std::runtime_error{"invalid use of option: --pmconfig\n"};
			}
			else
			{
				data_.environment.PMConfigPath = Vita::string(argv[++nArg]).trim("\"").trim();
			}
		}
		else if ("--pmroot" == option)
		{
			if (nArg + 1 >= argc)
			{
				throw std::runtime_error{"invalid use of option: --pmroot\n"};
			}
			else
			{
				data_.environment.PMRootPath = Vita::string(argv[++nArg]).trim("\"").trim();
			}
		}
		else if (arguments_.INSTALL || arguments_.REMOVE)
		{
			bool found = false;
			std::string name;
			if (arguments_.CUSTOM_INSTALL)
			{
				name = std::string{ argv[nArg] };
			}
			else
			{
				name = Vita::string(argv[nArg]).toLower();
			}
			for (const auto& config : configs_)
			{
				if (config == name)
				{
					found = true;
					break;
				}
			}
			if (!found)
			{
				configs_.push_back(name);
			}
		}
		else
		{
			throw std::runtime_error{"invalid option: " + std::string(argv[nArg]) + "\n"};
		}
	}
	if (!arguments_.SHOW_PCI && !arguments_.SHOW_USB)
	{
		arguments_.SHOW_USB = true;
		arguments_.SHOW_PCI = true;
	}
}

bool Mhwd::optionsDontInterfereWithEachOther() const
{
	if (arguments_.INSTALL && arguments_.REMOVE)
	{
    	printer_.printError("install and remove options can only be used separately!\n");
    	printer_.printHelp();
    	return false;
	}
	else if ((arguments_.INSTALL || arguments_.REMOVE) && arguments_.AUTOCONFIGURE)
	{
    	printer_.printError("auto option can't be combined with install and remove options!\n");
    	printer_.printHelp();
    	return false;
	}
	else if ((arguments_.REMOVE || arguments_.INSTALL) && configs_.empty())
	{
    	printer_.printError("nothing to do?!\n");
    	printer_.printHelp();
    	return false;
	}

	return true;
}

Philip Müller's avatar
Philip Müller committed
int Mhwd::launch(int argc, char *argv[])
{
    std::vector<std::string> missingDirs { checkEnvironment() };
    if (!missingDirs.empty())
    {
    	printer_.printError("Following directories do not exist:");
    	for (const auto& dir : missingDirs)
    	{
    		printer_.printStatus(dir);
    	}
        return 1;
    }

december0123's avatar
december0123 committed
    std::string operationType;
    bool autoConfigureNonFreeDriver = false;
december0123's avatar
december0123 committed
    std::string autoConfigureClassID;

december0123's avatar
december0123 committed
    {
    	tryToParseCmdLineOptions(argc, argv, autoConfigureNonFreeDriver, operationType,
    			autoConfigureClassID);
december0123's avatar
december0123 committed
    }
    catch(const std::runtime_error& e)
december0123's avatar
december0123 committed
    {
    	printer_.printError(e.what());
    	printer_.printHelp();
    	return 1;
    if (!optionsDontInterfereWithEachOther())
december0123's avatar
december0123 committed
    {
december0123's avatar
december0123 committed
    }

    // Check for invalid configs
    for (auto&& invalidConfig : data_.invalidConfigs)
december0123's avatar
december0123 committed
    {
        printer_.printWarning("config '" + invalidConfig->configPath_ + "' is invalid!");
    }

    // > Perform operations:

    // List all configs
    if (arguments_.LIST_ALL && arguments_.SHOW_PCI)
december0123's avatar
december0123 committed
    {
        if (!data_.allPCIConfigs.empty())
        {
            printer_.listConfigs(data_.allPCIConfigs, "All PCI configs:");
        }
        else
        {
            printer_.printWarning("No PCI configs found!");
        }
december0123's avatar
december0123 committed
    }
    if (arguments_.LIST_ALL && arguments_.SHOW_USB)
december0123's avatar
december0123 committed
    {
        if (!data_.allUSBConfigs.empty())
        {
            printer_.listConfigs(data_.allUSBConfigs, "All USB configs:");
        }
        else
        {
            printer_.printWarning("No USB configs found!");
        }
december0123's avatar
december0123 committed
    }

    // List installed configs
    if (arguments_.LIST_INSTALLED && arguments_.SHOW_PCI)
december0123's avatar
december0123 committed
    {
        if (arguments_.DETAIL)
december0123's avatar
december0123 committed
        {
            printer_.printInstalledConfigs("PCI", data_.installedPCIConfigs);
        }
        else