<commit_before>#include <ctrcommon/app.hpp>
#include <ctrcommon/gpu.hpp>
#include <ctrcommon/input.hpp>
#include <ctrcommon/platform.hpp>
#include <ctrcommon/ui.hpp>

#include <sys/dirent.h>
#include <sys/errno.h>
#include <stdio.h>
#include <string.h>

#include <sstream>
#include <iomanip>

typedef enum {
    INSTALL_CIA,
    DELETE_CIA,
    DELETE_TITLE,
    LAUNCH_TITLE
} Mode;

int main(int argc, char **argv) {
    if(!platformInit()) {
        return 0;
    }

    bool ninjhax = platformIsNinjhax();

    std::vector<std::string> extensions;
    extensions.push_back("cia");

    MediaType destination = SD;
    Mode mode = INSTALL_CIA;
    bool exit = false;
    bool netInstall = false;
    u64 freeSpace = fsGetFreeSpace(destination);
    auto onLoop = [&]() {
        if(ninjhax && inputIsPressed(BUTTON_START)) {
            exit = true;
            return true;
        }

        bool breakLoop = false;

        if(inputIsPressed(BUTTON_L)) {
            if(destination == SD) {
                destination = NAND;
            } else {
                destination = SD;
            }

            freeSpace = fsGetFreeSpace(destination);
            if(mode == DELETE_TITLE || mode == LAUNCH_TITLE) {
                breakLoop = true;
            }
        }

        if(inputIsPressed(BUTTON_R)) {
            if(mode == INSTALL_CIA) {
                mode = DELETE_CIA;
            } else if(mode == DELETE_CIA) {
                mode = DELETE_TITLE;
                breakLoop = true;
            } else if(mode == DELETE_TITLE) {
                mode = LAUNCH_TITLE;
            } else if(mode == LAUNCH_TITLE) {
                mode = INSTALL_CIA;
                breakLoop = true;
            }
        }

        if(mode == INSTALL_CIA && inputIsPressed(BUTTON_Y)) {
            netInstall = true;
            breakLoop = true;
        }

        std::stringstream stream;
        stream << "Free Space: " << freeSpace << " bytes (" << std::fixed << std::setprecision(2) << freeSpace / 1024.0f / 1024.0f << "MB)" << "\n";
        stream << "Destination: " << (destination == NAND ? "NAND" : "SD") << ", Mode: " << (mode == INSTALL_CIA ? "Install CIA" : mode == DELETE_CIA ? "Delete CIA" : mode == DELETE_TITLE ? "Delete Title" : "Launch Title") << "\n";
        stream << "L - Switch Destination, R - Switch Mode" << "\n";
        if(mode == INSTALL_CIA) {
            stream << "X - Install all CIAs in the current directory" << "\n";
            stream << "Y - Receive an app over the network" << "\n";
        } else if(mode == DELETE_CIA) {
            stream << "X - Delete all CIAs in the current directory" << "\n";
        }

        if(ninjhax) {
            stream << "START - Exit to launcher" << "\n";
        }

        std::string str = stream.str();
        const std::string title = "FBI v1.3.8";
        gputDrawString(title, (gpuGetViewportWidth() - gputGetStringWidth(title, 16)) / 2, (gpuGetViewportHeight() - gputGetStringHeight(title, 16) + gputGetStringHeight(str, 8)) / 2, 16, 16);
        gputDrawString(str, (gpuGetViewportWidth() - gputGetStringWidth(str, 8)) / 2, 4, 8, 8);

        return breakLoop;
    };

    std::string batchInfo = "";

    int prevProgress = -1;
    auto onProgress = [&](u64 pos, u64 totalSize) {
        u32 progress = (u32) ((pos * 100) / totalSize);
        if(prevProgress != (int) progress) {
            prevProgress = (int) progress;

            std::stringstream details;
            details << batchInfo;
            details << "Press B to cancel.";

            uiDisplayProgress(TOP_SCREEN, "Installing", details.str(), true, progress);
        }

        inputPoll();
        return !inputIsPressed(BUTTON_B);
    };

    bool showNetworkPrompts = true;

    while(platformIsRunning()) {
        std::string fileTarget;
        App appTarget;
        if(mode == INSTALL_CIA || mode == DELETE_CIA) {
            if(netInstall && !exit) {
                gpuClearScreens();

                RemoteFile file = uiAcceptRemoteFile(TOP_SCREEN, [&](std::stringstream& infoStream) {
                    if(inputIsPressed(BUTTON_A)) {
                        showNetworkPrompts = !showNetworkPrompts;
                    }

                    infoStream << "\n";
                    infoStream << "Prompts: " << (showNetworkPrompts ? "Enabled" : "Disabled") << "\n";
                    infoStream << "Press A to toggle prompts." << "\n";
                });

                if(file.fd == NULL) {
                    netInstall = false;
                    continue;
                }

                std::stringstream confirmStream;
                confirmStream << "Install the received application?" << "\n";
                confirmStream << "Size: " << file.fileSize << " bytes (" << std::fixed << std::setprecision(2) << file.fileSize / 1024.0f / 1024.0f << "MB)" << "\n";
                if(!showNetworkPrompts || uiPrompt(TOP_SCREEN, confirmStream.str(), true)) {
                    AppResult ret = appInstall(destination, file.fd, file.fileSize, onProgress);
                    prevProgress = -1;
                    if(showNetworkPrompts || ret != APP_SUCCESS) {
                        std::stringstream resultMsg;
                        resultMsg << "Install ";
                        if(ret == APP_SUCCESS) {
                            resultMsg << "succeeded!";
                        } else {
                            resultMsg << "failed!" << "\n";
                            resultMsg << appGetResultString(ret) << "\n";
                        }

                        uiPrompt(TOP_SCREEN, resultMsg.str(), false);
                    }
                }

                fclose(file.fd);
                continue;
            }

            uiSelectFile(&fileTarget, "/", extensions, [&](const std::string currDirectory, bool inRoot, bool &updateList) {
                if(inputIsPressed(BUTTON_X)) {
                    std::stringstream confirmMsg;
                    if(mode == INSTALL_CIA) {
                        confirmMsg << "Install ";
                    } else {
                        confirmMsg << "Delete ";
                    }

                    confirmMsg << "all CIAs in the current directory?";
                    if(uiPrompt(TOP_SCREEN, confirmMsg.str(), true)) {
                        bool failed = false;
                        std::vector<FileInfo> contents = fsGetDirectoryContents(currDirectory);
                        u32 currItem = 0;
                        for(std::vector<FileInfo>::iterator it = contents.begin(); it != contents.end(); it++) {
                            std::string path = (*it).path;
                            if(!fsIsDirectory(path) && fsHasExtensions(path, extensions)) {
                                std::string displayFileName = (*it).name;
                                if(displayFileName.length() > 40) {
                                    displayFileName.resize(40);
                                    displayFileName += "...";
                                }

                                if(mode == INSTALL_CIA) {
                                    std::stringstream batchInstallStream;
                                    batchInstallStream << displayFileName << " (" << currItem << ")" << "\n";

                                    batchInfo = batchInstallStream.str();
                                    AppResult ret = appInstallFile(destination, path, onProgress);
                                    prevProgress = -1;
                                    batchInfo = "";
                                    if(ret != APP_SUCCESS) {
                                        Error error = platformGetError();
                                        platformSetError(error);

                                        std::stringstream resultMsg;
                                        resultMsg << "Install failed!" << "\n";
                                        resultMsg << displayFileName << "\n";
                                        resultMsg << appGetResultString(ret) << "\n";
                                        uiPrompt(TOP_SCREEN, resultMsg.str(), false);
                                        if(error.module != MODULE_NN_AM || error.description != DESCRIPTION_ALREADY_EXISTS) {
                                            failed = true;
                                            break;
                                        }
                                    }
                                } else {
                                    std::stringstream deleteStream;
                                    deleteStream << "Deleting CIA..." << "\n";
                                    deleteStream << displayFileName << " (" << currItem << ")" << "\n";

                                    uiDisplayMessage(TOP_SCREEN, deleteStream.str());
                                    if(remove(path.c_str()) != 0) {
                                        std::stringstream resultMsg;
                                        resultMsg << "Delete failed!" << "\n";
                                        resultMsg << displayFileName << "\n";
                                        resultMsg << strerror(errno) << "\n";
                                        uiPrompt(TOP_SCREEN, resultMsg.str(), false);
                                        failed = true;
                                        break;
                                    } else {
                                        updateList = true;
                                    }
                                }
                            }

                            currItem++;
                        }

                        if(!failed) {
                            uiPrompt(TOP_SCREEN, "Install succeeded!\n", false);
                        }

                        freeSpace = fsGetFreeSpace(destination);
                    }
                }

                return onLoop();
            }, [&](const std::string path, bool &updateList) {
                std::stringstream confirmMsg;
                if(mode == INSTALL_CIA) {
                    confirmMsg << "Install ";
                } else {
                    confirmMsg << "Delete ";
                }

                confirmMsg << "the selected CIA?";
                if(uiPrompt(TOP_SCREEN, confirmMsg.str(), true)) {
                    std::stringstream resultMsg;
                    if(mode == INSTALL_CIA) {
                        resultMsg << "Install ";
                    } else {
                        resultMsg << "Delete ";
                    }

                    if(mode == INSTALL_CIA) {
                        AppResult ret = appInstallFile(destination, path, onProgress);
                        prevProgress = -1;
                        if(ret == APP_SUCCESS) {
                            resultMsg << "succeeded!";
                        } else {
                            resultMsg << "failed!" << "\n";
                            resultMsg << appGetResultString(ret) << "\n";
                        }
                    } else {
                        uiDisplayMessage(TOP_SCREEN, "Deleting CIA...");
                        if(remove(path.c_str()) != 0) {
                            updateList = true;
                            resultMsg << "succeeded!";
                        } else {
                            resultMsg << "failed!" << "\n";
                            resultMsg << strerror(errno) << "\n";
                        }
                    }

                    uiPrompt(TOP_SCREEN, resultMsg.str(), false);

                    freeSpace = fsGetFreeSpace(destination);
                }

                return false;
            });
        } else if(mode == DELETE_TITLE || mode == LAUNCH_TITLE) {
            uiDisplayMessage(BOTTOM_SCREEN, "Loading title list...");
            uiSelectApp(&appTarget, destination, [&](bool &updateList) {
                return onLoop();
            }, [&](App app, bool &updateList) {
                if(mode == DELETE_TITLE) {
                    if(uiPrompt(TOP_SCREEN, "Delete the selected title?", true) && (destination != NAND || uiPrompt(TOP_SCREEN, "You are about to delete a title from the NAND.\nTHIS HAS THE POTENTIAL TO BRICK YOUR 3DS!\nAre you sure you wish to continue?", true))) {
                        updateList = true;
                        uiDisplayMessage(TOP_SCREEN, "Deleting title...");
                        AppResult ret = appDelete(app);

                        std::stringstream resultMsg;
                        resultMsg << "Delete ";
                        if(ret == APP_SUCCESS) {
                            resultMsg << "succeeded!";
                        } else {
                            resultMsg << "failed!" << "\n";
                            resultMsg << appGetResultString(ret) << "\n";
                        }

                        uiPrompt(TOP_SCREEN, resultMsg.str(), false);

                        freeSpace = fsGetFreeSpace(destination);
                    }
                } else if(mode == LAUNCH_TITLE) {
                    if(uiPrompt(TOP_SCREEN, "Launch the selected title?", true)) {
                        updateList = true;
                        uiDisplayMessage(TOP_SCREEN, "Launching title...");
                        AppResult ret = appLaunch(app);

                        if(ret != APP_SUCCESS) {
                            std::stringstream resultMsg;
                            resultMsg << "Launch failed!" << "\n";
                            resultMsg << appGetResultString(ret) << "\n";
                            uiPrompt(TOP_SCREEN, resultMsg.str(), false);
                        } else {
                            while(true) {
                            }
                        }
                    }
                }

                return false;
            });
        }

        if(exit) {
            break;
        }
    }

    platformCleanup();
    return 0;
}
<commit_msg>Add rxtools 4x rop installer feature.<commit_after>#include <ctrcommon/app.hpp>
#include <ctrcommon/gpu.hpp>
#include <ctrcommon/input.hpp>
#include <ctrcommon/platform.hpp>
#include <ctrcommon/ui.hpp>

#include <sys/dirent.h>
#include <sys/errno.h>
#include <stdio.h>
#include <string.h>

#include <3ds.h>
#include <3ds/services/cfgnor.h>
#include "ropdata.h"

#include <sstream>
#include <iomanip>

typedef enum {
    INSTALL_CIA,
    DELETE_CIA,
    DELETE_TITLE,
    LAUNCH_TITLE
} Mode;

int main(int argc, char **argv) {
    if(!platformInit()) {
        return 0;
    }

    bool ninjhax = platformIsNinjhax();

    std::vector<std::string> extensions;
    extensions.push_back("cia");

    MediaType destination = SD;
    Mode mode = INSTALL_CIA;
    bool exit = false;
    bool netInstall = false;
    int ropinstalled = 0;
    u64 freeSpace = fsGetFreeSpace(destination);
    auto onLoop = [&]() {
        if(ninjhax && inputIsPressed(BUTTON_START)) {
            exit = true;
            return true;
        }

        bool breakLoop = false;

        if(inputIsPressed(BUTTON_L)) {
            if(destination == SD) {
                destination = NAND;
            } else {
                destination = SD;
            }

            freeSpace = fsGetFreeSpace(destination);
            if(mode == DELETE_TITLE || mode == LAUNCH_TITLE) {
                breakLoop = true;
            }
        }

        if(inputIsPressed(BUTTON_R)) {
            if(mode == INSTALL_CIA) {
                mode = DELETE_CIA;
            } else if(mode == DELETE_CIA) {
                mode = DELETE_TITLE;
                breakLoop = true;
            } else if(mode == DELETE_TITLE) {
                mode = LAUNCH_TITLE;
            } else if(mode == LAUNCH_TITLE) {
                mode = INSTALL_CIA;
                breakLoop = true;
            }
        }

        if(mode == INSTALL_CIA && inputIsPressed(BUTTON_Y)) {
            netInstall = true;
            breakLoop = true;
        }

        if(inputIsPressed(BUTTON_SELECT) && ropinstalled==0){
            Result result=0;

			CFGNOR_Initialize( (u8)1 );
			result=CFGNOR_WriteData(0x1FE00, (u32*)mset4x, 0x200);
			CFGNOR_Shutdown();

			if(result){       // 1 is fail install 2 is success install
				ropinstalled=1;
			}
			else{
				ropinstalled=2;
			}
		}

        std::stringstream stream;
        stream << "Free Space: " << freeSpace << " bytes (" << std::fixed << std::setprecision(2) << freeSpace / 1024.0f / 1024.0f << "MB)" << "\n";
        stream << "Destination: " << (destination == NAND ? "NAND" : "SD") << ", Mode: " << (mode == INSTALL_CIA ? "Install CIA" : mode == DELETE_CIA ? "Delete CIA" : mode == DELETE_TITLE ? "Delete Title" : "Launch Title") << "\n";
        stream << "L - Switch Destination, R - Switch Mode" << "\n";
        if(mode == INSTALL_CIA) {
            stream << "X - Install all CIAs in the current directory" << "\n";
            stream << "Y - Receive an app over the network" << "\n";
        } else if(mode == DELETE_CIA) {
            stream << "X - Delete all CIAs in the current directory" << "\n";
        }

        if(ninjhax) {
            stream << "START - Exit to launcher" << "\n";
        }
        stream <<  (ropinstalled == 0 ?  "SELECT - Install rxTools mset: NOT INSTALLED\n" : ropinstalled == 2 ? "SELECT - Install rxTools mset: INSTALLED\n" : "SELECT - Install rxTools mset: FAILED\n");

        std::string str = stream.str();
        const std::string title = "FBI v1.3.8";
        gputDrawString(title, (gpuGetViewportWidth() - gputGetStringWidth(title, 16)) / 2, (gpuGetViewportHeight() - gputGetStringHeight(title, 16) + gputGetStringHeight(str, 8)) / 2, 16, 16);
        gputDrawString(str, (gpuGetViewportWidth() - gputGetStringWidth(str, 8)) / 2, 4, 8, 8);

        return breakLoop;
    };

    std::string batchInfo = "";

    int prevProgress = -1;
    auto onProgress = [&](u64 pos, u64 totalSize) {
        u32 progress = (u32) ((pos * 100) / totalSize);
        if(prevProgress != (int) progress) {
            prevProgress = (int) progress;

            std::stringstream details;
            details << batchInfo;
            details << "Press B to cancel.";

            uiDisplayProgress(TOP_SCREEN, "Installing", details.str(), true, progress);
        }

        inputPoll();
        return !inputIsPressed(BUTTON_B);
    };

    bool showNetworkPrompts = true;

    while(platformIsRunning()) {
        std::string fileTarget;
        App appTarget;
        if(mode == INSTALL_CIA || mode == DELETE_CIA) {
            if(netInstall && !exit) {
                gpuClearScreens();

                RemoteFile file = uiAcceptRemoteFile(TOP_SCREEN, [&](std::stringstream& infoStream) {
                    if(inputIsPressed(BUTTON_A)) {
                        showNetworkPrompts = !showNetworkPrompts;
                    }

                    infoStream << "\n";
                    infoStream << "Prompts: " << (showNetworkPrompts ? "Enabled" : "Disabled") << "\n";
                    infoStream << "Press A to toggle prompts." << "\n";
                });

                if(file.fd == NULL) {
                    netInstall = false;
                    continue;
                }

                std::stringstream confirmStream;
                confirmStream << "Install the received application?" << "\n";
                confirmStream << "Size: " << file.fileSize << " bytes (" << std::fixed << std::setprecision(2) << file.fileSize / 1024.0f / 1024.0f << "MB)" << "\n";
                if(!showNetworkPrompts || uiPrompt(TOP_SCREEN, confirmStream.str(), true)) {
                    AppResult ret = appInstall(destination, file.fd, file.fileSize, onProgress);
                    prevProgress = -1;
                    if(showNetworkPrompts || ret != APP_SUCCESS) {
                        std::stringstream resultMsg;
                        resultMsg << "Install ";
                        if(ret == APP_SUCCESS) {
                            resultMsg << "succeeded!";
                        } else {
                            resultMsg << "failed!" << "\n";
                            resultMsg << appGetResultString(ret) << "\n";
                        }

                        uiPrompt(TOP_SCREEN, resultMsg.str(), false);
                    }
                }

                fclose(file.fd);
                continue;
            }

            uiSelectFile(&fileTarget, "/", extensions, [&](const std::string currDirectory, bool inRoot, bool &updateList) {
                if(inputIsPressed(BUTTON_X)) {
                    std::stringstream confirmMsg;
                    if(mode == INSTALL_CIA) {
                        confirmMsg << "Install ";
                    } else {
                        confirmMsg << "Delete ";
                    }

                    confirmMsg << "all CIAs in the current directory?";
                    if(uiPrompt(TOP_SCREEN, confirmMsg.str(), true)) {
                        bool failed = false;
                        std::vector<FileInfo> contents = fsGetDirectoryContents(currDirectory);
                        u32 currItem = 0;
                        for(std::vector<FileInfo>::iterator it = contents.begin(); it != contents.end(); it++) {
                            std::string path = (*it).path;
                            if(!fsIsDirectory(path) && fsHasExtensions(path, extensions)) {
                                std::string displayFileName = (*it).name;
                                if(displayFileName.length() > 40) {
                                    displayFileName.resize(40);
                                    displayFileName += "...";
                                }

                                if(mode == INSTALL_CIA) {
                                    std::stringstream batchInstallStream;
                                    batchInstallStream << displayFileName << " (" << currItem << ")" << "\n";

                                    batchInfo = batchInstallStream.str();
                                    AppResult ret = appInstallFile(destination, path, onProgress);
                                    prevProgress = -1;
                                    batchInfo = "";
                                    if(ret != APP_SUCCESS) {
                                        Error error = platformGetError();
                                        platformSetError(error);

                                        std::stringstream resultMsg;
                                        resultMsg << "Install failed!" << "\n";
                                        resultMsg << displayFileName << "\n";
                                        resultMsg << appGetResultString(ret) << "\n";
                                        uiPrompt(TOP_SCREEN, resultMsg.str(), false);
                                        if(error.module != MODULE_NN_AM || error.description != DESCRIPTION_ALREADY_EXISTS) {
                                            failed = true;
                                            break;
                                        }
                                    }
                                } else {
                                    std::stringstream deleteStream;
                                    deleteStream << "Deleting CIA..." << "\n";
                                    deleteStream << displayFileName << " (" << currItem << ")" << "\n";

                                    uiDisplayMessage(TOP_SCREEN, deleteStream.str());
                                    if(remove(path.c_str()) != 0) {
                                        std::stringstream resultMsg;
                                        resultMsg << "Delete failed!" << "\n";
                                        resultMsg << displayFileName << "\n";
                                        resultMsg << strerror(errno) << "\n";
                                        uiPrompt(TOP_SCREEN, resultMsg.str(), false);
                                        failed = true;
                                        break;
                                    } else {
                                        updateList = true;
                                    }
                                }
                            }

                            currItem++;
                        }

                        if(!failed) {
                            uiPrompt(TOP_SCREEN, "Install succeeded!\n", false);
                        }

                        freeSpace = fsGetFreeSpace(destination);
                    }
                }

                return onLoop();
            }, [&](const std::string path, bool &updateList) {
                std::stringstream confirmMsg;
                if(mode == INSTALL_CIA) {
                    confirmMsg << "Install ";
                } else {
                    confirmMsg << "Delete ";
                }

                confirmMsg << "the selected CIA?";
                if(uiPrompt(TOP_SCREEN, confirmMsg.str(), true)) {
                    std::stringstream resultMsg;
                    if(mode == INSTALL_CIA) {
                        resultMsg << "Install ";
                    } else {
                        resultMsg << "Delete ";
                    }

                    if(mode == INSTALL_CIA) {
                        AppResult ret = appInstallFile(destination, path, onProgress);
                        prevProgress = -1;
                        if(ret == APP_SUCCESS) {
                            resultMsg << "succeeded!";
                        } else {
                            resultMsg << "failed!" << "\n";
                            resultMsg << appGetResultString(ret) << "\n";
                        }
                    } else {
                        uiDisplayMessage(TOP_SCREEN, "Deleting CIA...");
                        if(remove(path.c_str()) != 0) {
                            updateList = true;
                            resultMsg << "succeeded!";
                        } else {
                            resultMsg << "failed!" << "\n";
                            resultMsg << strerror(errno) << "\n";
                        }
                    }

                    uiPrompt(TOP_SCREEN, resultMsg.str(), false);

                    freeSpace = fsGetFreeSpace(destination);
                }

                return false;
            });
        } else if(mode == DELETE_TITLE || mode == LAUNCH_TITLE) {
            uiDisplayMessage(BOTTOM_SCREEN, "Loading title list...");
            uiSelectApp(&appTarget, destination, [&](bool &updateList) {
                return onLoop();
            }, [&](App app, bool &updateList) {
                if(mode == DELETE_TITLE) {
                    if(uiPrompt(TOP_SCREEN, "Delete the selected title?", true) && (destination != NAND || uiPrompt(TOP_SCREEN, "You are about to delete a title from the NAND.\nTHIS HAS THE POTENTIAL TO BRICK YOUR 3DS!\nAre you sure you wish to continue?", true))) {
                        updateList = true;
                        uiDisplayMessage(TOP_SCREEN, "Deleting title...");
                        AppResult ret = appDelete(app);

                        std::stringstream resultMsg;
                        resultMsg << "Delete ";
                        if(ret == APP_SUCCESS) {
                            resultMsg << "succeeded!";
                        } else {
                            resultMsg << "failed!" << "\n";
                            resultMsg << appGetResultString(ret) << "\n";
                        }

                        uiPrompt(TOP_SCREEN, resultMsg.str(), false);

                        freeSpace = fsGetFreeSpace(destination);
                    }
                } else if(mode == LAUNCH_TITLE) {
                    if(uiPrompt(TOP_SCREEN, "Launch the selected title?", true)) {
                        updateList = true;
                        uiDisplayMessage(TOP_SCREEN, "Launching title...");
                        AppResult ret = appLaunch(app);

                        if(ret != APP_SUCCESS) {
                            std::stringstream resultMsg;
                            resultMsg << "Launch failed!" << "\n";
                            resultMsg << appGetResultString(ret) << "\n";
                            uiPrompt(TOP_SCREEN, resultMsg.str(), false);
                        } else {
                            while(true) {
                            }
                        }
                    }
                }

                return false;
            });
        }

        if(exit) {
            break;
        }
    }

    platformCleanup();
    return 0;
}
<|endoftext|>


<commit_before>/*! \file */

#include <algorithms/AlgorithmLauncher.h>
#include <config/settings.h>
#include <h5pp/h5pp.h>
#include <io/filesystem.h>
#include <math/rnd.h>
#include <math/tenx.h>
#include <tools/common/log.h>

#if defined(OPENBLAS_AVAILABLE)
    #include <openblas/cblas.h>
    #include <openblas/openblas_config.h>
#endif

#if defined(MKL_AVAILABLE)
    #define MKL_Complex8  std::complex<float>
    #define MKL_Complex16 std::complex<double>
    #include <mkl.h>
    #include <mkl_service.h>
#endif
#include <config/loader.h>
#include <cxxopts.hpp>
#include <debug/stacktrace.h>
#include <getopt.h>
#include <gitversion.h>
#include <thread>

#if __has_include(<unistd.h>)
    #include <unistd.h>
#endif

void print_usage() {
    std::printf(
        R"(
==========  DMRG++  ============
Usage                       : DMRG++ [-option <value>].
-h                          : Help. Shows this text.
-b <positive integer>       : Integer whose bitfield sets the initial product state. Negative is unused (default -1)
-c <.cfg or .h5 filename>   : Full or relative path to a config file or hdf5 file from a previous simulation (which has a config file) (default = input.cfg)
-n <stl threads>            : Number of C++11 threads (Used by Eigen::Tensor)
-o <output filename base>   : Full or relative path to the output file. The seed number will be appended to this filename unless -x is passed.
-i <.cfg or .h5 filename>   : Full or relative path to a config file or hdf5 file from a previous simulation (which has a config file) (default = input.cfg)
-s <seed>                   : Positive number that seeds the random number generator (default = 1)
-t <omp threads>            : Number of OpenMP threads
-v                          : Enables trace-level verbosity
-x                          : Do not append seed to the output filename.

)");
}

void clean_up() {
    if(not settings::storage::use_temp_dir) return;
    if(fs::exists(settings::storage::tmp::hdf5_temp_path)) {
        try {
            tools::log->info("Cleaning up temporary file: [{}]", settings::storage::tmp::hdf5_temp_path);
            h5pp::hdf5::moveFile(settings::storage::tmp::hdf5_temp_path, settings::storage::tmp::hdf5_final_path, h5pp::FilePermission::REPLACE);
        } catch(const std::exception &err) { tools::log->info("Cleaning not needed: {}", err.what()); }
    }
    H5garbage_collect();
    H5Eprint(H5E_DEFAULT, stderr);
}

std::string filename_append_number(const std::string &filename, const long number) {
    if(number < 0) return filename;
    // Append the seed_model to the output filename
    h5pp::fs::path oldFileName = filename;
    h5pp::fs::path newFileName = filename;
    if(oldFileName.stem().string().find(std::to_string(number)) != std::string::npos) return filename;
    newFileName.replace_filename(fmt::format("{}_{}{}", oldFileName.stem().string(), number, oldFileName.extension().string()));
    tools::log->info("Appended number [{}] to filename: [{}]", number, newFileName.string());
    return newFileName.string();
}

/*!
    \brief  Main function. Sets simulation parameters and excecutes the desired algorithms.
    \return an integer 0 upon exit success
*/

int main(int argc, char *argv[]) {
    // Register termination codes and what to do in those cases
    debug::register_callbacks();

    // Make sure to move the file back from temp location
    std::atexit(clean_up);
    std::at_quick_exit(clean_up);

    tools::log = tools::Logger::setLogger("DMRG++ main", 0, true);
    using namespace tools;

#if __has_include(<unistd.h>)
    char name[HOST_NAME_MAX];
    auto err = gethostname(name, HOST_NAME_MAX);
    if(err == 0) tools::log->info("Hostname        : {}", name);
#endif

    // print current Git status
    tools::log->info("Git branch      : {}", GIT::BRANCH);
    tools::log->info("    commit hash : {}", GIT::COMMIT_HASH);
    tools::log->info("    revision    : {}", GIT::REVISION);

    cxxopts::Options options("DMRG++", "An MPS-based algorithm to find 1D quantum-states");
    /* clang-format off */
    options.add_options()
    ("b,bitfield",    "Integer whose bitfield sets the initial product state. Negative is unused", cxxopts::value<long>()->default_value("-1")->implicit_value("-1"))
    ("c,config",      "Path to a .cfg or .h5 file from a previous simulation",                     cxxopts::value<std::string>()->default_value("input/input.cfg"))
    ("n,stlthreads",  "Number of C++11 threads (Used by Eigen::Tensor)",                           cxxopts::value<int>()->default_value("-1")->implicit_value("-1"))
    ("o,outfile",     "Path to the output file. The seed number gets appended by default (see -x)",cxxopts::value<std::string>()->default_value("output/output.h5"))
    ("s,seed",        "Positive number seeds the random number generator",                         cxxopts::value<long>()->default_value("-1")->implicit_value("-1"))
    ("t,ompthreads",  "Number of OpenMP threads",                                                  cxxopts::value<int>()->default_value("-1")->implicit_value("-1"))
    ("v,verbose",     "Sets verbosity level",                                                      cxxopts::value<long>()->default_value("-1")->implicit_value("1"))
    ("x,noseedname",  "Do not append seed to the output filename",                                 cxxopts::value<bool>()->default_value("false")->implicit_value("true"));

    auto in = options.parse(argc, argv);

    if(in["config"].count() > 0)    settings::input::config_filename = in["config"].as<std::string>();

    //  Try loading the given config file.
    //  Note that there is a default "input/input.config" if none was given
    Loader dmrg_config(settings::input::config_filename);
    if(dmrg_config.file_exists) {
        dmrg_config.load();
        settings::load(dmrg_config); // B2
    } else
        throw std::runtime_error(fmt::format("Could not find config file: {}", settings::input::config_filename)); // Invalid file

    // Override the other settings
    bool        noseedname = false;
    if(in["bitfield"].count() > 0)  settings::input::bitfield           = in["bitfield"].as<long>();
    if(in["stlthreads"].count() > 0)  settings::input::bitfield         = in["bitfield"].as<int>();
    if(in["outfile"].count() > 0)  settings::storage::output_filepath   = in["outfile"].as<std::string>();
    if(in["seed"].count() > 0)  settings::input::seed                   = in["seed"].as<long>();
    if(in["ompthreads"].count() > 0)  settings::threading::omp_threads  = in["ompthreads"].as<int>();
    if(in["verbose"].count() > 0)  settings::console::verbosity         = in["verbose"].as<long>();
    if(in["noseedname"].count() > 0)  noseedname                        = in["noseedname"].as<bool>();

    tools::log = tools::Logger::setLogger("DMRG++ main", settings::console::verbosity, settings::console::timestamp);
    /* clang-format on */

    // C: Generate the correct output filename based on given seeds
    if(not noseedname) {
        settings::storage::output_filepath = filename_append_number(settings::storage::output_filepath, settings::input::seed);
        settings::storage::output_filepath = filename_append_number(settings::storage::output_filepath, settings::input::bitfield);
    }

    // Seed with random::device initially (This also takes care of srand used by Eigen)
    // This is to make reproducible simulations
    rnd::seed(settings::input::seed);

// Set the number of threads to be used
#if defined(EIGEN_USE_THREADS)
    if(settings::threading::stl_threads <= 0) { settings::threading::stl_threads = (int) std::thread::hardware_concurrency(); }
    tenx::omp::setNumThreads(settings::threading::stl_threads);
    tools::log->info("Using Eigen Tensor with {} C++11 threads", tenx::omp::num_threads);
#else
    if(settings::threading::stl_threads > 1)
        tools::log->warn("EIGEN_USE_THREADS is not defined: "
                         "Failed to enable threading in Eigen::Tensor with stl_threads = {}",
                         settings::threading::stl_threads);
#endif

#if defined(_OPENMP)
    if(settings::threading::omp_threads <= 0) { settings::threading::omp_threads = (int) std::thread::hardware_concurrency(); }
    omp_set_num_threads(settings::threading::omp_threads); // Should only need this. Both Eigen (non-Tensor) and MKL listen to this
                                                           //    omp_set_max_active_levels(1);
    tools::log->info("Using OpenMP with {} threads and {} active levels", omp_get_max_threads(), omp_get_max_active_levels());
#endif
#if defined(OPENBLAS_AVAILABLE)
    auto        openblas_parallel_mode = openblas_get_parallel();
    std::string openblas_parallel_str;
    if(openblas_parallel_mode == 0) openblas_parallel_str = "seq";
    if(openblas_parallel_mode == 1) openblas_parallel_str = "threads";
    if(openblas_parallel_mode == 2) openblas_parallel_str = "openmp";
    if(openblas_parallel_mode == 1) openblas_set_num_threads(settings::threading::omp_threads); // Use the omp_threads level for blas-related threading
    tools::log->info("{} compiled with parallel mode [{}] for target {} with config {} | multithread threshold {} | running with {} threads", OPENBLAS_VERSION,
                     openblas_parallel_str, openblas_get_corename(), openblas_get_config(), OPENBLAS_GEMM_MULTITHREAD_THRESHOLD, openblas_get_num_threads());
#endif
#if defined(MKL_AVAILABLE)
    tools::log->info("Using Intel MKL with {} threads", mkl_get_max_threads());
#endif

    // Initialize the algorithm class
    // This class stores simulation data automatically to a file specified in the config file
    AlgorithmLauncher launcher;

    // Run the algorithms
    launcher.run_algorithms();

    return 0;
}
<commit_msg>Fixed issue with implicit values for cli arguments<commit_after>/*! \file */

#include <algorithms/AlgorithmLauncher.h>
#include <config/settings.h>
#include <h5pp/h5pp.h>
#include <io/filesystem.h>
#include <math/rnd.h>
#include <math/tenx.h>
#include <tools/common/log.h>

#if defined(OPENBLAS_AVAILABLE)
    #include <openblas/cblas.h>
    #include <openblas/openblas_config.h>
#endif

#if defined(MKL_AVAILABLE)
    #define MKL_Complex8  std::complex<float>
    #define MKL_Complex16 std::complex<double>
    #include <mkl.h>
    #include <mkl_service.h>
#endif
#include <config/loader.h>
#include <cxxopts.hpp>
#include <debug/stacktrace.h>
#include <getopt.h>
#include <gitversion.h>
#include <thread>

#if __has_include(<unistd.h>)
    #include <unistd.h>
#endif

void print_usage() {
    std::printf(
        R"(
==========  DMRG++  ============
Usage                       : DMRG++ [-option <value>].
-h                          : Help. Shows this text.
-b <positive integer>       : Integer whose bitfield sets the initial product state. Negative is unused (default -1)
-c <.cfg or .h5 filename>   : Full or relative path to a config file or hdf5 file from a previous simulation (which has a config file) (default = input.cfg)
-n <stl threads>            : Number of C++11 threads (Used by Eigen::Tensor)
-o <output filename base>   : Full or relative path to the output file. The seed number will be appended to this filename unless -x is passed.
-i <.cfg or .h5 filename>   : Full or relative path to a config file or hdf5 file from a previous simulation (which has a config file) (default = input.cfg)
-s <seed>                   : Positive number that seeds the random number generator (default = 1)
-t <omp threads>            : Number of OpenMP threads
-v                          : Enables trace-level verbosity
-x                          : Do not append seed to the output filename.

)");
}

void clean_up() {
    if(not settings::storage::use_temp_dir) return;
    if(fs::exists(settings::storage::tmp::hdf5_temp_path)) {
        try {
            tools::log->info("Cleaning up temporary file: [{}]", settings::storage::tmp::hdf5_temp_path);
            h5pp::hdf5::moveFile(settings::storage::tmp::hdf5_temp_path, settings::storage::tmp::hdf5_final_path, h5pp::FilePermission::REPLACE);
        } catch(const std::exception &err) { tools::log->info("Cleaning not needed: {}", err.what()); }
    }
    H5garbage_collect();
    H5Eprint(H5E_DEFAULT, stderr);
}

std::string filename_append_number(const std::string &filename, const long number) {
    if(number < 0) return filename;
    // Append the seed_model to the output filename
    h5pp::fs::path oldFileName = filename;
    h5pp::fs::path newFileName = filename;
    if(oldFileName.stem().string().find(std::to_string(number)) != std::string::npos) return filename;
    newFileName.replace_filename(fmt::format("{}_{}{}", oldFileName.stem().string(), number, oldFileName.extension().string()));
    tools::log->info("Appended number [{}] to filename: [{}]", number, newFileName.string());
    return newFileName.string();
}

/*!
    \brief  Main function. Sets simulation parameters and excecutes the desired algorithms.
    \return an integer 0 upon exit success
*/

int main(int argc, char *argv[]) {
    // Register termination codes and what to do in those cases
    debug::register_callbacks();

    // Make sure to move the file back from temp location
    std::atexit(clean_up);
    std::at_quick_exit(clean_up);

    tools::log = tools::Logger::setLogger("DMRG++ main", 0, true);
    using namespace tools;

#if __has_include(<unistd.h>)
    char name[HOST_NAME_MAX];
    auto err = gethostname(name, HOST_NAME_MAX);
    if(err == 0) tools::log->info("Hostname        : {}", name);
#endif

    // print current Git status
    tools::log->info("Git branch      : {}", GIT::BRANCH);
    tools::log->info("    commit hash : {}", GIT::COMMIT_HASH);
    tools::log->info("    revision    : {}", GIT::REVISION);

    cxxopts::Options options("DMRG++", "An MPS-based algorithm to find 1D quantum-states");
    /* clang-format off */
    options.add_options()
    ("h,help",     "Show help")
    ("b,bitfield",    "Integer whose bitfield sets the initial product state. Negative is unused", cxxopts::value<long>())
    ("c,config",      "Path to a .cfg or .h5 file from a previous simulation",                     cxxopts::value<std::string>()->default_value("input/input.cfg"))
    ("n,stlthreads",  "Number of C++11 threads (Used by Eigen::Tensor)",                           cxxopts::value<int>())
    ("o,outfile",     "Path to the output file. The seed number gets appended by default (see -x)",cxxopts::value<std::string>()->default_value("output/output.h5"))
    ("s,seed",        "Positive number seeds the random number generator",                         cxxopts::value<long>())
    ("t,ompthreads",  "Number of OpenMP threads",                                                  cxxopts::value<int>())
    ("v,verbose",     "Sets verbosity level",                                                      cxxopts::value<long>())
    ("x,noseedname",  "Do not append seed to the output filename",                                 cxxopts::value<bool>());

    auto in = options.parse(argc, argv);
    if(in["help"].count() > 0) {
        fmt::print(options.help());
        exit(0);
    }
    if(in["config"].count() > 0)    settings::input::config_filename = in["config"].as<std::string>();

    //  Try loading the given config file.
    //  Note that there is a default "input/input.config" if none was given
    Loader dmrg_config(settings::input::config_filename);
    if(dmrg_config.file_exists) {
        dmrg_config.load();
        settings::load(dmrg_config); // B2
    } else
        throw std::runtime_error(fmt::format("Could not find config file: {}", settings::input::config_filename)); // Invalid file

    // Override the other settings
    bool        noseedname = false;
    if(in["bitfield"].count() > 0)      settings::input::bitfield           = in["bitfield"].as<long>();
    if(in["stlthreads"].count() > 0)    settings::threading::stl_threads    = in["stlthreads"].as<int>();
    if(in["outfile"].count() > 0)       settings::storage::output_filepath  = in["outfile"].as<std::string>();
    if(in["seed"].count() > 0)          settings::input::seed               = in["seed"].as<long>();
    if(in["ompthreads"].count() > 0)    settings::threading::omp_threads    = in["ompthreads"].as<int>();
    if(in["verbose"].count() > 0)       settings::console::verbosity        = in["verbose"].as<long>();
    if(in["noseedname"].count() > 0)    noseedname                          = in["noseedname"].as<bool>();

    tools::log = tools::Logger::setLogger("DMRG++ main", settings::console::verbosity, settings::console::timestamp);
    /* clang-format on */

    // C: Generate the correct output filename based on given seeds
    if(not noseedname) {
        settings::storage::output_filepath = filename_append_number(settings::storage::output_filepath, settings::input::seed);
        settings::storage::output_filepath = filename_append_number(settings::storage::output_filepath, settings::input::bitfield);
    }

    // Seed with random::device initially (This also takes care of srand used by Eigen)
    // This is to make reproducible simulations
    rnd::seed(settings::input::seed);

// Set the number of threads to be used
#if defined(EIGEN_USE_THREADS)
    if(settings::threading::stl_threads <= 0) { settings::threading::stl_threads = (int) std::thread::hardware_concurrency(); }
    tenx::omp::setNumThreads(settings::threading::stl_threads);
    tools::log->info("Using Eigen Tensor with {} C++11 threads", tenx::omp::num_threads);
#else
    if(settings::threading::stl_threads > 1)
        tools::log->warn("EIGEN_USE_THREADS is not defined: "
                         "Failed to enable threading in Eigen::Tensor with stl_threads = {}",
                         settings::threading::stl_threads);
#endif

#if defined(_OPENMP)
    if(settings::threading::omp_threads <= 0) { settings::threading::omp_threads = (int) std::thread::hardware_concurrency(); }
    omp_set_num_threads(settings::threading::omp_threads); // Should only need this. Both Eigen (non-Tensor) and MKL listen to this
                                                           //    omp_set_max_active_levels(1);
    tools::log->info("Using OpenMP with {} threads and {} active levels", omp_get_max_threads(), omp_get_max_active_levels());
#endif
#if defined(OPENBLAS_AVAILABLE)
    auto        openblas_parallel_mode = openblas_get_parallel();
    std::string openblas_parallel_str;
    if(openblas_parallel_mode == 0) openblas_parallel_str = "seq";
    if(openblas_parallel_mode == 1) openblas_parallel_str = "threads";
    if(openblas_parallel_mode == 2) openblas_parallel_str = "openmp";
    if(openblas_parallel_mode == 1) openblas_set_num_threads(settings::threading::omp_threads); // Use the omp_threads level for blas-related threading
    tools::log->info("{} compiled with parallel mode [{}] for target {} with config {} | multithread threshold {} | running with {} threads", OPENBLAS_VERSION,
                     openblas_parallel_str, openblas_get_corename(), openblas_get_config(), OPENBLAS_GEMM_MULTITHREAD_THRESHOLD, openblas_get_num_threads());
#endif
#if defined(MKL_AVAILABLE)
    tools::log->info("Using Intel MKL with {} threads", mkl_get_max_threads());
#endif

    // Initialize the algorithm class
    // This class stores simulation data automatically to a file specified in the config file
    AlgorithmLauncher launcher;

    // Run the algorithms
    launcher.run_algorithms();

    return 0;
}
<|endoftext|>


<commit_before>#ifndef "_MENU_HPP"
#define "_MENU_HPP"
#include <iostream>
#include <vector>
#include <string>

class Menu
{
public:
  Menu(const string &name, const string &prompt, const std::vector<std::pair<string, string>> &choices = std::vector<std::pair<string, string>>{});
  virtual ~Menu();
  const string& getChoice() const;
  bool operator++(const string &name) const;
private:
  static const string menuend;
  string _name, _prompt;
  std::vector<std::pair<string, string>> _choice;
};

#endif
<commit_msg>Update menu.hpp<commit_after>#ifndef "_MENU_HPP"
#define "_MENU_HPP"
#include <iostream>
#include <vector>
#include <string>

//Need to add point where data is pulled from a tokenizer

class Menu
{
public:
  Menu(const string &name, const string &prompt, const std::vector<std::pair<string, string>> &choices = std::vector<std::pair<string, string>>{});
  virtual ~Menu();
  const string& getChoice() const;
  bool operator++(const string &name) const;
private:
  static const string menuend;
  string _name, _prompt;
  std::vector<std::pair<string, string>> _choice;
};

#endif
<|endoftext|>


<commit_before>// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#define DEBUG
#define DEBUG2

#include <net/tcp.hpp>
#include <alloca.h>

using namespace std;
using namespace net;

TCP::TCP(IPStack& inet) :
  inet_(inet),
  listeners_(),
  connections_(),
  writeq(),
  used_ports(),
  MAX_SEG_LIFETIME(30s)
{
  inet.on_transmit_queue_available(transmit_avail_delg::from<TCP,&TCP::process_writeq>(this));
}

/*
  Note: There is different approaches to how to handle listeners & connections.
  Need to discuss and decide for the best one.

  Best solution(?):
  Preallocate a pool with listening connections.
  When threshold is reach, remove/add new ones, similar to TCP window.

  Current solution:
  Simple.
*/
TCP::Connection& TCP::bind(Port port) {
  // Already a listening socket.
  if(listeners_.find(port) != listeners_.end()) {
    throw TCPException{"Port is already taken."};
  }
  auto& connection = (listeners_.emplace(port, Connection{*this, port})).first->second;
  debug("<TCP::bind> Bound to port %i \n", port);
  connection.open(false);
  return connection;
}

/*
  Active open a new connection to the given remote.

  @WARNING: Callback is added when returned (TCP::connect(...).onSuccess(...)),
  and open() is called before callback is added.
*/
TCP::Connection_ptr TCP::connect(Socket remote) {
  auto port = next_free_port();
  std::shared_ptr<Connection> connection = add_connection(port, remote);
  connection->open(true);
  return connection;
}

/*
  Active open a new connection to the given remote.
*/
void TCP::connect(Socket remote, Connection::ConnectCallback callback) {
  auto port = next_free_port();
  auto connection = add_connection(port, remote);
  connection->onConnect(callback).open(true);
}

TCP::Seq TCP::generate_iss() {
  // Do something to get a iss.
  return rand();
}

/*
  TODO: Check if there is any ports free.
*/
TCP::Port TCP::next_free_port() {

  if(++current_ephemeral_ == 0) {
    current_ephemeral_ = 1025;
    // TODO: Can be taken
  }
  // Avoid giving a port that is bound to a service.
  while(listeners_.find(current_ephemeral_) != listeners_.end())
    current_ephemeral_++;

  return current_ephemeral_;
}

/*
  Expensive look up if port is in use.
*/
bool TCP::port_in_use(const TCP::Port port) const {
  if(listeners_.find(port) != listeners_.end())
    return true;

  for(auto conn : connections_) {
    if(conn.first.first == port)
      return true;
  }
  return false;
}

uint16_t TCP::checksum(TCP::Packet_ptr packet) {
  // TCP header
  TCP::Header* tcp_hdr = &(packet->header());
  // Pseudo header
  TCP::Pseudo_header pseudo_hdr;

  int tcp_length = packet->tcp_length();

  pseudo_hdr.saddr.whole = packet->src().whole;
  pseudo_hdr.daddr.whole = packet->dst().whole;
  pseudo_hdr.zero = 0;
  pseudo_hdr.proto = IP4::IP4_TCP;
  pseudo_hdr.tcp_length = htons(tcp_length);

  union Sum{
    uint32_t whole;
    uint16_t part[2];
  } sum;

  sum.whole = 0;

  // Compute sum of pseudo header
  for (uint16_t* it = (uint16_t*)&pseudo_hdr; it < (uint16_t*)&pseudo_hdr + sizeof(pseudo_hdr)/2; it++)
    sum.whole += *it;

  // Compute sum sum the actual header and data
  for (uint16_t* it = (uint16_t*)tcp_hdr; it < (uint16_t*)tcp_hdr + tcp_length/2; it++)
    sum.whole+= *it;

  // The odd-numbered case
  if (tcp_length & 1) {
    debug("<TCP::checksum> ODD number of bytes. 0-pading \n");
    union {
      uint16_t whole;
      uint8_t part[2];
    } last_chunk;
    last_chunk.part[0] = ((uint8_t*)tcp_hdr)[tcp_length - 1];
    last_chunk.part[1] = 0;
    sum.whole += last_chunk.whole;
  }

  debug2("<TCP::checksum: sum: 0x%x, half+half: 0x%x, TCP checksum: 0x%x, TCP checksum big-endian: 0x%x \n",
         sum.whole, sum.part[0] + sum.part[1], (uint16_t)~((uint16_t)(sum.part[0] + sum.part[1])), htons((uint16_t)~((uint16_t)(sum.part[0] + sum.part[1]))));

  Sum final_sum { (uint32_t)sum.part[0] + (uint32_t)sum.part[1] };

  if (final_sum.part[1])
    final_sum.part[0] += final_sum.part[1];

  return ~final_sum.whole;
}

void TCP::bottom(net::Packet_ptr packet_ptr) {
  // Translate into a TCP::Packet. This will be used inside the TCP-scope.
  auto packet = std::static_pointer_cast<TCP::Packet>(packet_ptr);
  debug("<TCP::bottom> TCP Packet received - Source: %s, Destination: %s \n",
        packet->source().to_string().c_str(), packet->destination().to_string().c_str());

  // Do checksum
  if(checksum(packet)) {
    debug("<TCP::bottom> TCP Packet Checksum != 0 \n");
  }

  Connection::Tuple tuple { packet->dst_port(), packet->source() };

  // Try to find the receiver
  auto conn_it = connections_.find(tuple);
  // Connection found
  if(conn_it != connections_.end()) {
    debug("<TCP::bottom> Connection found: %s \n", conn_it->second->to_string().c_str());
    conn_it->second->segment_arrived(packet);
  }
  // No connection found
  else {
    // Is there a listener?
    auto listen_conn_it = listeners_.find(packet->dst_port());
    debug("<TCP::bottom> No connection found - looking for listener..\n");
    // Listener found => Create listening Connection
    if(listen_conn_it != listeners_.end()) {
      auto& listen_conn = listen_conn_it->second;
      debug("<TCP::bottom> Listener found: %s ...\n", listen_conn.to_string().c_str());
      auto connection = (connections_.emplace(tuple, std::make_shared<Connection>(listen_conn)).first->second);
      // Set remote
      connection->set_remote(packet->source());
      debug("<TCP::bottom> ... Creating connection: %s \n", connection->to_string().c_str());

      connection->segment_arrived(packet);
    }
    // No listener found
    else {
      drop(packet);
    }
  }
}

void TCP::process_writeq(size_t packets) {
  debug2("<TCP::process_writeq> size=%u p=%u\n", writeq.size(), packets);
  // foreach connection who wants to write
  while(packets and !writeq.empty()) {
    auto conn = writeq.front();
    writeq.pop_back();
    conn->offer(packets);
    conn->set_queued(false);
  }
}

size_t TCP::send(Connection_ptr conn, const char* buffer, size_t n) {
  size_t written{0};
  auto packets = inet_.transmit_queue_available();

  debug2("<TCP::send> Send request for %u bytes\n", n);

  if(packets > 0) {
    written += conn->send(buffer, n, packets);
  }
  // if connection still can send (means there wasn't enough packets)
  // only requeue if not already queued
  if(conn->can_send() and !conn->is_queued()) {
    debug2("<TCP::send> Conn queued.\n");
    writeq.push_back(conn);
    conn->set_queued(true);
  }

  return written;
}

/*
  Show all connections for TCP as a string.

  Format:
  [Protocol][Recv][Send][Local][Remote][State]

  TODO: Make sure Recv, Send, In, Out is correct and add them to output. Also, alignment?
*/
string TCP::to_string() const {
  // Write all connections in a cute list.
  stringstream ss;
  ss << "LISTENING SOCKETS:\n";
  for(auto listen_it : listeners_) {
    ss << listen_it.second.to_string() << "\n";
  }
  ss << "\nCONNECTIONS:\n" <<  "Proto\tRecv\tSend\tIn\tOut\tLocal\t\t\tRemote\t\t\tState\n";
  for(auto con_it : connections_) {
    auto& c = *(con_it.second);
    ss << "tcp4\t"
       << " " << "\t" << " " << "\t"
       << " " << "\t" << " " << "\t"
       << c.local().to_string() << "\t\t" << c.remote().to_string() << "\t\t"
       << c.state().to_string() << "\n";
  }
  return ss.str();
}

TCP::Connection_ptr TCP::add_connection(Port local_port, TCP::Socket remote) {
  return        (connections_.emplace(
                                      Connection::Tuple{ local_port, remote },
                                      std::make_shared<Connection>(*this, local_port, remote))
                 ).first->second;
}

void TCP::close_connection(TCP::Connection& conn) {
  debug("<TCP::close_connection> Closing connection: %s \n", conn.to_string().c_str());
  connections_.erase(conn.tuple());
}

void TCP::drop(TCP::Packet_ptr) {
  //debug("<TCP::drop> Packet was dropped - no recipient: %s \n", packet->destination().to_string().c_str());
}

void TCP::transmit(TCP::Packet_ptr packet) {
  // Generate checksum.
  packet->set_checksum(TCP::checksum(packet));
  //if(packet->has_data())
  //  printf("<TCP::transmit> S: %u\n", packet->seq());
  _network_layer_out(packet);
}
<commit_msg>tcp: avoid reincarnating old connections<commit_after>// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#define DEBUG
#define DEBUG2

#include <net/tcp.hpp>
#include <alloca.h>

using namespace std;
using namespace net;

TCP::TCP(IPStack& inet) :
  inet_(inet),
  listeners_(),
  connections_(),
  writeq(),
  used_ports(),
  MAX_SEG_LIFETIME(30s)
{
  inet.on_transmit_queue_available(transmit_avail_delg::from<TCP,&TCP::process_writeq>(this));
}

/*
  Note: There is different approaches to how to handle listeners & connections.
  Need to discuss and decide for the best one.

  Best solution(?):
  Preallocate a pool with listening connections.
  When threshold is reach, remove/add new ones, similar to TCP window.

  Current solution:
  Simple.
*/
TCP::Connection& TCP::bind(Port port) {
  // Already a listening socket.
  if(listeners_.find(port) != listeners_.end()) {
    throw TCPException{"Port is already taken."};
  }
  auto& connection = (listeners_.emplace(port, Connection{*this, port})).first->second;
  debug("<TCP::bind> Bound to port %i \n", port);
  connection.open(false);
  return connection;
}

/*
  Active open a new connection to the given remote.

  @WARNING: Callback is added when returned (TCP::connect(...).onSuccess(...)),
  and open() is called before callback is added.
*/
TCP::Connection_ptr TCP::connect(Socket remote) {
  auto port = next_free_port();
  std::shared_ptr<Connection> connection = add_connection(port, remote);
  connection->open(true);
  return connection;
}

/*
  Active open a new connection to the given remote.
*/
void TCP::connect(Socket remote, Connection::ConnectCallback callback) {
  auto port = next_free_port();
  auto connection = add_connection(port, remote);
  connection->onConnect(callback).open(true);
}

TCP::Seq TCP::generate_iss() {
  // Do something to get a iss.
  return rand();
}

/*
  TODO: Check if there is any ports free.
*/
TCP::Port TCP::next_free_port() {

  if(++current_ephemeral_ == 0) {
    current_ephemeral_ = 1025;
    // TODO: Can be taken
  }
  // Avoid giving a port that is bound to a service.
  while(listeners_.find(current_ephemeral_) != listeners_.end())
    current_ephemeral_++;

  return current_ephemeral_;
}

/*
  Expensive look up if port is in use.
*/
bool TCP::port_in_use(const TCP::Port port) const {
  if(listeners_.find(port) != listeners_.end())
    return true;

  for(auto conn : connections_) {
    if(conn.first.first == port)
      return true;
  }
  return false;
}

uint16_t TCP::checksum(TCP::Packet_ptr packet) {
  // TCP header
  TCP::Header* tcp_hdr = &(packet->header());
  // Pseudo header
  TCP::Pseudo_header pseudo_hdr;

  int tcp_length = packet->tcp_length();

  pseudo_hdr.saddr.whole = packet->src().whole;
  pseudo_hdr.daddr.whole = packet->dst().whole;
  pseudo_hdr.zero = 0;
  pseudo_hdr.proto = IP4::IP4_TCP;
  pseudo_hdr.tcp_length = htons(tcp_length);

  union Sum{
    uint32_t whole;
    uint16_t part[2];
  } sum;

  sum.whole = 0;

  // Compute sum of pseudo header
  for (uint16_t* it = (uint16_t*)&pseudo_hdr; it < (uint16_t*)&pseudo_hdr + sizeof(pseudo_hdr)/2; it++)
    sum.whole += *it;

  // Compute sum sum the actual header and data
  for (uint16_t* it = (uint16_t*)tcp_hdr; it < (uint16_t*)tcp_hdr + tcp_length/2; it++)
    sum.whole+= *it;

  // The odd-numbered case
  if (tcp_length & 1) {
    debug("<TCP::checksum> ODD number of bytes. 0-pading \n");
    union {
      uint16_t whole;
      uint8_t part[2];
    } last_chunk;
    last_chunk.part[0] = ((uint8_t*)tcp_hdr)[tcp_length - 1];
    last_chunk.part[1] = 0;
    sum.whole += last_chunk.whole;
  }

  debug2("<TCP::checksum: sum: 0x%x, half+half: 0x%x, TCP checksum: 0x%x, TCP checksum big-endian: 0x%x \n",
         sum.whole, sum.part[0] + sum.part[1], (uint16_t)~((uint16_t)(sum.part[0] + sum.part[1])), htons((uint16_t)~((uint16_t)(sum.part[0] + sum.part[1]))));

  Sum final_sum { (uint32_t)sum.part[0] + (uint32_t)sum.part[1] };

  if (final_sum.part[1])
    final_sum.part[0] += final_sum.part[1];

  return ~final_sum.whole;
}

void TCP::bottom(net::Packet_ptr packet_ptr) {
  // Translate into a TCP::Packet. This will be used inside the TCP-scope.
  auto packet = std::static_pointer_cast<TCP::Packet>(packet_ptr);
  debug("<TCP::bottom> TCP Packet received - Source: %s, Destination: %s \n",
        packet->source().to_string().c_str(), packet->destination().to_string().c_str());

  // Do checksum
  if(checksum(packet)) {
    debug("<TCP::bottom> TCP Packet Checksum != 0 \n");
  }

  Connection::Tuple tuple { packet->dst_port(), packet->source() };

  // Try to find the receiver
  auto conn_it = connections_.find(tuple);
  // Connection found
  if(conn_it != connections_.end()) {
    debug("<TCP::bottom> Connection found: %s \n", conn_it->second->to_string().c_str());
    conn_it->second->segment_arrived(packet);
  }
  // No connection found
  else if(packet->isset(SYN)) {
    // Is there a listener?
    auto listen_conn_it = listeners_.find(packet->dst_port());
    debug("<TCP::bottom> No connection found - looking for listener..\n");
    // Listener found => Create listening Connection
    if(listen_conn_it != listeners_.end()) {
      auto& listen_conn = listen_conn_it->second;
      debug("<TCP::bottom> Listener found: %s ...\n", listen_conn.to_string().c_str());
      auto connection = (connections_.emplace(tuple, std::make_shared<Connection>(listen_conn)).first->second);
      // Set remote
      connection->set_remote(packet->source());
      debug("<TCP::bottom> ... Creating connection: %s \n", connection->to_string().c_str());

      connection->segment_arrived(packet);
    }
    // No listener found
    else {
      drop(packet);
    }
  }
  else {
    drop(packet);
  }
}

void TCP::process_writeq(size_t packets) {
  debug2("<TCP::process_writeq> size=%u p=%u\n", writeq.size(), packets);
  // foreach connection who wants to write
  while(packets and !writeq.empty()) {
    auto conn = writeq.front();
    writeq.pop_back();
    conn->offer(packets);
    conn->set_queued(false);
  }
}

size_t TCP::send(Connection_ptr conn, const char* buffer, size_t n) {
  size_t written{0};
  auto packets = inet_.transmit_queue_available();

  debug2("<TCP::send> Send request for %u bytes\n", n);

  if(packets > 0) {
    written += conn->send(buffer, n, packets);
  }
  // if connection still can send (means there wasn't enough packets)
  // only requeue if not already queued
  if(conn->can_send() and !conn->is_queued()) {
    debug2("<TCP::send> Conn queued.\n");
    writeq.push_back(conn);
    conn->set_queued(true);
  }

  return written;
}

/*
  Show all connections for TCP as a string.

  Format:
  [Protocol][Recv][Send][Local][Remote][State]

  TODO: Make sure Recv, Send, In, Out is correct and add them to output. Also, alignment?
*/
string TCP::to_string() const {
  // Write all connections in a cute list.
  stringstream ss;
  ss << "LISTENING SOCKETS:\n";
  for(auto listen_it : listeners_) {
    ss << listen_it.second.to_string() << "\n";
  }
  ss << "\nCONNECTIONS:\n" <<  "Proto\tRecv\tSend\tIn\tOut\tLocal\t\t\tRemote\t\t\tState\n";
  for(auto con_it : connections_) {
    auto& c = *(con_it.second);
    ss << "tcp4\t"
       << " " << "\t" << " " << "\t"
       << " " << "\t" << " " << "\t"
       << c.local().to_string() << "\t\t" << c.remote().to_string() << "\t\t"
       << c.state().to_string() << "\n";
  }
  return ss.str();
}

TCP::Connection_ptr TCP::add_connection(Port local_port, TCP::Socket remote) {
  return        (connections_.emplace(
                                      Connection::Tuple{ local_port, remote },
                                      std::make_shared<Connection>(*this, local_port, remote))
                 ).first->second;
}

void TCP::close_connection(TCP::Connection& conn) {
  debug("<TCP::close_connection> Closing connection: %s \n", conn.to_string().c_str());
  connections_.erase(conn.tuple());
}

void TCP::drop(TCP::Packet_ptr) {
  //debug("<TCP::drop> Packet was dropped - no recipient: %s \n", packet->destination().to_string().c_str());
}

void TCP::transmit(TCP::Packet_ptr packet) {
  // Generate checksum.
  packet->set_checksum(TCP::checksum(packet));
  //if(packet->has_data())
  //  printf("<TCP::transmit> S: %u\n", packet->seq());
  _network_layer_out(packet);
}
<|endoftext|>


<commit_before>#include "netlink.hpp"

#define mnl_attr_for_each_cpp(attr, nlh, offset) \
	for ((attr) = (struct nlattr*) mnl_nlmsg_get_payload_offset((nlh), (offset)); \
		mnl_attr_ok((attr), (char *)mnl_nlmsg_get_payload_tail(nlh) - (char *)(attr)); \
		(attr) = mnl_attr_next(attr))

using namespace std;

// Adapted from:
// https://git.netfilter.org/libmnl/plain/examples/rtnl/rtnl-link-dump3.c
static int data_cb_ip(const struct nlmsghdr *nlh, void *data)
{
	struct ifaddrmsg *ifm = (struct ifaddrmsg *) mnl_nlmsg_get_payload(nlh);
	struct nlattr *attr;
	vector<interface>* interfaces = static_cast<vector<interface>*>(data);
	uint32_t index = ifm->ifa_index;

	auto it = find_if(interfaces->begin(), interfaces->end(),
			[index](interface& i){
				return i == index;
			});

	interface& interface =
		(it == interfaces->end())
		? *interfaces->emplace(interfaces->end())
		: *it;
	interface.netlinkIndex = index;

	mnl_attr_for_each_cpp(attr, nlh, sizeof(*ifm)) {
		int type = mnl_attr_get_type(attr);

		/* skip unsupported attribute in user-space */
		if (mnl_attr_type_valid(attr, IFLA_MAX) < 0)
			continue;

		switch(type) {
		case IFA_ADDRESS:
			if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
				logErr("mnl_attr_validate");
				return MNL_CB_ERROR;
			}
			interface.IPs.push_back(htonl(mnl_attr_get_u32(attr)));
			break;
		case IFA_LABEL:
			if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
				logErr("mnl_attr_validate");
				return MNL_CB_ERROR;
			}
			interface.name = mnl_attr_get_str(attr);
			break;
		}
	}

	return MNL_CB_OK;
}

// Adapted from:
// https://git.netfilter.org/libmnl/plain/examples/rtnl/rtnl-link-dump3.c
static int data_cb_link(const struct nlmsghdr *nlh, void *data) {
	struct ifinfomsg *ifm = (struct ifinfomsg*) mnl_nlmsg_get_payload(nlh);
	struct nlattr *attr;

	vector<interface>* interfaces = static_cast<vector<interface>*>(data);
	uint32_t index = ifm->ifi_index;

	auto it = find_if(interfaces->begin(), interfaces->end(),
			[index](interface& i){
				return i == index;
			});

	interface& interface =
		(it == interfaces->end())
		? *interfaces->emplace(interfaces->end())
		: *it;
	interface.netlinkIndex = index;

	mnl_attr_for_each_cpp(attr, nlh, sizeof(*ifm)) {
		int type = mnl_attr_get_type(attr);

		/* skip unsupported attribute in user-space */
		if (mnl_attr_type_valid(attr, IFLA_MAX) < 0)
			continue;

		switch(type) {
		case IFLA_ADDRESS:
			memcpy(&interface.mac, RTA_DATA(attr), 6);
			break;
		}
	}

	return MNL_CB_OK;
}

// Adapted from:
// https://git.netfilter.org/libmnl/plain/examples/rtnl/rtnl-link-dump3.c
shared_ptr<vector<interface>> Netlink::getAllInterfaces() {
	struct mnl_socket *nl;
	char buf[MNL_SOCKET_BUFFER_SIZE];
	struct nlmsghdr *nlh;
	struct rtgenmsg *rt;
	int ret;
	unsigned int seq, portid;

	shared_ptr<vector<interface>> interfaces = make_shared<vector<interface>>();

	// Open NL socket
	nl = mnl_socket_open(NETLINK_ROUTE);
	if (nl == NULL) {
		fatal("mnl_socket_open() failed");
	}

	if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
		fatal("mnl_socket_bind() failed");
	}
	portid = mnl_socket_get_portid(nl);

	// do "ip a"
	nlh = mnl_nlmsg_put_header(buf);
	nlh->nlmsg_type	= RTM_GETADDR;
	nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
	nlh->nlmsg_seq = seq = time(NULL);
	rt = (struct rtgenmsg *) mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
	rt->rtgen_family = AF_PACKET;

	if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
		fatal("mnl_socket_sendto() failed");
	}

	ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
	while (ret > 0) {
		ret = mnl_cb_run(buf, ret, seq, portid, data_cb_ip, interfaces.get());
		if (ret <= MNL_CB_STOP)
			break;
		ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
	}
	if (ret == -1) {
		fatal("mnl_socket_recvfrom() failed");
	}

	// do "ip l"
	nlh = mnl_nlmsg_put_header(buf);
	nlh->nlmsg_type	= RTM_GETLINK;
	nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
	nlh->nlmsg_seq = seq = time(NULL);
	rt = (struct rtgenmsg *) mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
	rt->rtgen_family = AF_PACKET;

	if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
		fatal("mnl_socket_sendto");
	}

	ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
	while (ret > 0) {
		ret = mnl_cb_run(buf, ret, seq, portid, data_cb_link, interfaces.get());
		if (ret <= MNL_CB_STOP)
			break;
		ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
	}
	if (ret == -1) {
		fatal("mnl_socket_recvfrom() failed");
	}

	mnl_socket_close(nl);

	return interfaces;
}

<commit_msg>(hopefully) fix netlink error<commit_after>#include "netlink.hpp"

#define mnl_attr_for_each_cpp(attr, nlh, offset) \
	for ((attr) = (struct nlattr*) mnl_nlmsg_get_payload_offset((nlh), (offset)); \
		mnl_attr_ok((attr), (char *)mnl_nlmsg_get_payload_tail(nlh) - (char *)(attr)); \
		(attr) = mnl_attr_next(attr))

using namespace std;

// Adapted from:
// https://git.netfilter.org/libmnl/plain/examples/rtnl/rtnl-link-dump3.c
static int data_cb_ip(const struct nlmsghdr *nlh, void *data)
{
	struct ifaddrmsg *ifm = (struct ifaddrmsg *) mnl_nlmsg_get_payload(nlh);
	struct nlattr *attr;
	vector<interface>* interfaces = static_cast<vector<interface>*>(data);
	uint32_t index = ifm->ifa_index;

	auto it = find_if(interfaces->begin(), interfaces->end(),
			[index](interface& i){
				return i == index;
			});

	interface& interface =
		(it == interfaces->end())
		? *interfaces->emplace(interfaces->end())
		: *it;
	interface.netlinkIndex = index;

	mnl_attr_for_each_cpp(attr, nlh, sizeof(*ifm)) {
		int type = mnl_attr_get_type(attr);

		/* skip unsupported attribute in user-space */
		if (mnl_attr_type_valid(attr, IFLA_MAX) < 0)
			continue;

		switch(type) {
		case IFA_ADDRESS:
			if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) {
				logErr("mnl_attr_validate() failed");
				return MNL_CB_ERROR;
			}
			interface.IPs.push_back(htonl(mnl_attr_get_u32(attr)));
			break;
		case IFA_LABEL:
			if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
				logErr("mnl_attr_validate() failed");
				return MNL_CB_ERROR;
			}
			interface.name = mnl_attr_get_str(attr);
			break;
		}
	}

	return MNL_CB_OK;
}

// Adapted from:
// https://git.netfilter.org/libmnl/plain/examples/rtnl/rtnl-link-dump3.c
static int data_cb_link(const struct nlmsghdr *nlh, void *data) {
	struct ifinfomsg *ifm = (struct ifinfomsg*) mnl_nlmsg_get_payload(nlh);
	struct nlattr *attr;

	vector<interface>* interfaces = static_cast<vector<interface>*>(data);
	uint32_t index = ifm->ifi_index;

	auto it = find_if(interfaces->begin(), interfaces->end(),
			[index](interface& i){
				return i == index;
			});

	interface& interface =
		(it == interfaces->end())
		? *interfaces->emplace(interfaces->end())
		: *it;
	interface.netlinkIndex = index;

	mnl_attr_for_each_cpp(attr, nlh, sizeof(*ifm)) {
		int type = mnl_attr_get_type(attr);

		/* skip unsupported attribute in user-space */
		if (mnl_attr_type_valid(attr, IFLA_MAX) < 0)
			continue;

		switch(type) {
		case IFLA_ADDRESS:
			memcpy(&interface.mac, RTA_DATA(attr), 6);
			break;
		}
	}

	return MNL_CB_OK;
}

// Adapted from:
// https://git.netfilter.org/libmnl/plain/examples/rtnl/rtnl-link-dump3.c
shared_ptr<vector<interface>> Netlink::getAllInterfaces() {
	struct mnl_socket *nl;
	char buf[MNL_SOCKET_BUFFER_SIZE];
	struct nlmsghdr *nlh;
	struct rtgenmsg *rt;
	int ret;
	unsigned int seq, portid;

	shared_ptr<vector<interface>> interfaces = make_shared<vector<interface>>();

	// Open NL socket
	nl = mnl_socket_open(NETLINK_ROUTE);
	if (nl == NULL) {
		fatal("mnl_socket_open() failed");
	}

	if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
		fatal("mnl_socket_bind() failed");
	}
	portid = mnl_socket_get_portid(nl);

	// do "ip a"
	nlh = mnl_nlmsg_put_header(buf);
	nlh->nlmsg_type	= RTM_GETADDR;
	nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
	nlh->nlmsg_seq = seq = time(NULL);
	rt = (struct rtgenmsg *) mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
	rt->rtgen_family = AF_PACKET;

	if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
		fatal("mnl_socket_sendto() failed");
	}

	ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
	while (ret > 0) {
		ret = mnl_cb_run(buf, ret, seq, portid, data_cb_ip, interfaces.get());
		if (ret <= MNL_CB_STOP)
			break;
		ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
	}
	if (ret == -1) {
		fatal("mnl_socket_recvfrom() failed");
	}

	// do "ip l"
	nlh = mnl_nlmsg_put_header(buf);
	nlh->nlmsg_type	= RTM_GETLINK;
	nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
	nlh->nlmsg_seq = seq = time(NULL);
	rt = (struct rtgenmsg *) mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
	rt->rtgen_family = AF_PACKET;

	if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
		fatal("mnl_socket_sendto");
	}

	ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
	while (ret > 0) {
		ret = mnl_cb_run(buf, ret, seq, portid, data_cb_link, interfaces.get());
		if (ret <= MNL_CB_STOP)
			break;
		ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
	}
	if (ret == -1) {
		fatal("mnl_socket_recvfrom() failed");
	}

	mnl_socket_close(nl);

	return interfaces;
}

<|endoftext|>


<commit_before>/*

    Copyright Eli Dupree and Isaac Dupree, 2011, 2012, 2013

    This file is part of Lasercake.

    Lasercake is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    Lasercake 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with Lasercake.  If not, see <http://www.gnu.org/licenses/>.

*/

// When you add a new tests file, define a new name here and with
// DECLARE_TESTS_FILE near the top of test_header.hpp, and put at
// the bottom of your tests file:
// REGISTER_TESTS // This must come last in the file.
#define TESTS_FILE bignum_tests
#include "test_header.hpp"

#include "../data_structures/bignum.hpp"

/*
extern void g(bignum<6> const&);
extern unsigned int q,p,r,s,t,u,v,w;
void f() { g(long_multiply_signed<6>(bignum<4>{{q,p,t,u}},bignum<4>{{r,s,v,w}})); }

template class bigint<192>;
template class biguint<192>;
template class bigint<0>;
template class biguint<0>;
*/

static void bignum_compile_test() {
  bigint<128> aa = bigint<64>{};
  bigint<128> ab(bigint<192>{});
  biguint<128> ac = biguint<64>{};
  biguint<128> ad(biguint<192>{});
  +aa;+ac;
  -aa;-ac;
  aa+aa;ac+ac;
  aa-aa;ac-ac;
  aa*aa;ac*ac;
  multiply_to<64>(aa,aa);
  multiply_to<64>(ac,ac);
  lossless_multiply(aa,aa);
  lossless_multiply(ac,ac);
  aa&aa;ac∾
  aa|aa;ac|ac;
  aa^aa;ac^ac;
  ~aa;~ac;
  aa>>90;ac>>90;
  aa<<90;ac<<90;
  aa==aa;ac==ac;
  aa!=aa;ac!=ac;
  aa<aa;ac<ac;
  aa<=aa;ac<=ac;
  aa>aa;ac>ac;
  aa>=aa;ac>=ac;

  aa+=aa;ac+=ac;
  aa-=aa;ac-=ac;
  aa*=aa;ac*=ac;
  aa&=aa;ac&=ac;
  aa|=aa;ac|=ac;
  aa^=aa;ac^=ac;
  aa>>=90;ac>>=90;
  aa<<=90;ac<<=90;

  (++aa)++; (++ac)++;
  (--aa)--; (--ac)--;

  !aa; !ac;
  (bool)(aa); (bool)(ac);

  //aa = ac;
  // explicit sign conversion
  aa = decltype(aa)(ac);
  ac = decltype(ac)(aa);

  std::numeric_limits<decltype(aa)>::max();
  std::numeric_limits<decltype(ac)>::max();

  (float)(aa);
  (float)(ac);
  (double)(aa);
  (double)(ac);
  //(long double)(aa);
  //(long double)(ac);

  abs(aa);
  abs(ac);
  hash_value(aa);
  hash_value(ac);
  ilog2(aa);
  ilog2(ac);
  isqrt(aa);
  isqrt(ac);
  popcount(aa);
  popcount(ac);
  count_trailing_zeroes(aa);
  count_trailing_zeroes(ac);
}

BOOST_AUTO_TEST_CASE(bignum_runtests) {
  //bigint<128> aa = bigint<64>(1000000000LL);
  //aa = aa*aa*aa*(aa*(aa*aa));
  bigint<512> aa = bigint<64>(1000000000LL);
  aa = aa*aa*aa*(aa*(aa*aa));
  //LOG << aa << "\n";
  //LOG << decltype(aa)(2) << "\n";
  BOOST_CHECK(+aa);
  BOOST_CHECK(-aa);
  BOOST_CHECK_EQUAL(-(-aa), aa);
  BOOST_CHECK_NE(-aa, aa);
  BOOST_CHECK_EQUAL(aa, aa);
  BOOST_CHECK_EQUAL(aa-aa, decltype(aa)(0));
  BOOST_CHECK_NE(aa+aa, aa);
  BOOST_CHECK_NE(aa+aa, decltype(aa)(0));
  BOOST_CHECK_LT(decltype(aa)(-1), decltype(aa)(0));
  BOOST_CHECK(!(aa-aa));
  BOOST_CHECK(!-(aa-aa));
  BOOST_CHECK(~(aa-aa));
  BOOST_CHECK(!(aa+(-aa)));
  BOOST_CHECK(!((-aa)+aa));
  BOOST_CHECK_EQUAL(-1*aa, -aa);
  BOOST_CHECK_EQUAL(aa*(aa*aa), (aa*aa)*aa);
  BOOST_CHECK_EQUAL(aa*(-aa*aa), (-aa*-aa)*-aa);
  BOOST_CHECK_EQUAL(aa*(aa+aa), aa*aa+aa*aa);
  BOOST_CHECK_EQUAL(aa*(aa-aa), aa*aa-aa*aa);
  BOOST_CHECK_EQUAL(aa*(aa+(-aa)), aa*aa-aa*aa);
  BOOST_CHECK_EQUAL(aa*(aa+(-aa)), aa*aa+(-aa*aa));
  BOOST_CHECK_EQUAL(aa+aa, 2*aa);
  BOOST_CHECK_EQUAL(aa+aa+aa, 3*aa);

  BOOST_CHECK_EQUAL(lossless_multiply(aa, lossless_multiply(aa,aa)), lossless_multiply(lossless_multiply(aa, aa), aa));
  BOOST_CHECK_EQUAL(lossless_multiply(aa, lossless_multiply(-aa,aa)), lossless_multiply(lossless_multiply(-aa, -aa), -aa));
  BOOST_CHECK_EQUAL(lossless_multiply(aa, aa+aa), lossless_multiply(aa, aa) + lossless_multiply(aa, aa));
  BOOST_CHECK_EQUAL(lossless_multiply(aa, aa-aa), lossless_multiply(aa, aa) - lossless_multiply(aa, aa));
/*
  aa&aa;
  aa|aa;
  aa^aa;
  ~aa;~ac;
  aa>>90;ac>>90;
  aa<<90;ac<<90;
  aa==aa;ac==ac;
  aa!=aa;ac!=ac;
  aa<aa;ac<ac;
  aa<=aa;ac<=ac;
  aa>aa;ac>ac;
  aa>=aa;ac>=ac;

  aa+=aa;ac+=ac;
  aa-=aa;ac-=ac;
  aa*=aa;ac*=ac;
  aa&=aa;ac&=ac;
  aa|=aa;ac|=ac;
  aa^=aa;ac^=ac;
  aa>>=90;ac>>=90;
  aa<<=90;ac<<=90;*/
}

REGISTER_TESTS // This must come last in the file.
<commit_msg>test that bigint works in physical_quantity.<commit_after>/*

    Copyright Eli Dupree and Isaac Dupree, 2011, 2012, 2013

    This file is part of Lasercake.

    Lasercake is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    Lasercake 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with Lasercake.  If not, see <http://www.gnu.org/licenses/>.

*/

// When you add a new tests file, define a new name here and with
// DECLARE_TESTS_FILE near the top of test_header.hpp, and put at
// the bottom of your tests file:
// REGISTER_TESTS // This must come last in the file.
#define TESTS_FILE bignum_tests
#include "test_header.hpp"

#include "../data_structures/bignum.hpp"

#include "../data_structures/bounds_checked_int.hpp"
#include "../units.hpp"

/*
extern void g(bignum<6> const&);
extern unsigned int q,p,r,s,t,u,v,w;
void f() { g(long_multiply_signed<6>(bignum<4>{{q,p,t,u}},bignum<4>{{r,s,v,w}})); }

template class bigint<192>;
template class biguint<192>;
template class bigint<0>;
template class biguint<0>;
*/

static void bignum_compile_test() {
  bigint<128> aa = bigint<64>{};
  bigint<128> ab(bigint<192>{});
  biguint<128> ac = biguint<64>{};
  biguint<128> ad(biguint<192>{});
  +aa;+ac;
  -aa;-ac;
  aa+aa;ac+ac;
  aa-aa;ac-ac;
  aa*aa;ac*ac;
  multiply_to<64>(aa,aa);
  multiply_to<64>(ac,ac);
  lossless_multiply(aa,aa);
  lossless_multiply(ac,ac);
  aa&aa;ac∾
  aa|aa;ac|ac;
  aa^aa;ac^ac;
  ~aa;~ac;
  aa>>90;ac>>90;
  aa<<90;ac<<90;
  aa==aa;ac==ac;
  aa!=aa;ac!=ac;
  aa<aa;ac<ac;
  aa<=aa;ac<=ac;
  aa>aa;ac>ac;
  aa>=aa;ac>=ac;

  aa+=aa;ac+=ac;
  aa-=aa;ac-=ac;
  aa*=aa;ac*=ac;
  aa&=aa;ac&=ac;
  aa|=aa;ac|=ac;
  aa^=aa;ac^=ac;
  aa>>=90;ac>>=90;
  aa<<=90;ac<<=90;

  (++aa)++; (++ac)++;
  (--aa)--; (--ac)--;

  !aa; !ac;
  (bool)(aa); (bool)(ac);

  //aa = ac;
  // explicit sign conversion
  aa = decltype(aa)(ac);
  ac = decltype(ac)(aa);

  std::numeric_limits<decltype(aa)>::max();
  std::numeric_limits<decltype(ac)>::max();

  (float)(aa);
  (float)(ac);
  (double)(aa);
  (double)(ac);
  //(long double)(aa);
  //(long double)(ac);

  abs(aa);
  abs(ac);
  hash_value(aa);
  hash_value(ac);
  ilog2(aa);
  ilog2(ac);
  isqrt(aa);
  isqrt(ac);
  popcount(aa);
  popcount(ac);
  count_trailing_zeroes(aa);
  count_trailing_zeroes(ac);

  //bounds_checked_int<bigint<256> > fsddfs;
  physical_quantity<bigint<256>, units<dim::meter<1>> > dist;
  dist+dist;
  dist*dist*dist;
}

BOOST_AUTO_TEST_CASE(bignum_runtests) {
  //bigint<128> aa = bigint<64>(1000000000LL);
  //aa = aa*aa*aa*(aa*(aa*aa));
  bigint<512> aa = bigint<64>(1000000000LL);
  aa = aa*aa*aa*(aa*(aa*aa));
  //LOG << aa << "\n";
  //LOG << decltype(aa)(2) << "\n";
  BOOST_CHECK(+aa);
  BOOST_CHECK(-aa);
  BOOST_CHECK_EQUAL(-(-aa), aa);
  BOOST_CHECK_NE(-aa, aa);
  BOOST_CHECK_EQUAL(aa, aa);
  BOOST_CHECK_EQUAL(aa-aa, decltype(aa)(0));
  BOOST_CHECK_NE(aa+aa, aa);
  BOOST_CHECK_NE(aa+aa, decltype(aa)(0));
  BOOST_CHECK_LT(decltype(aa)(-1), decltype(aa)(0));
  BOOST_CHECK(!(aa-aa));
  BOOST_CHECK(!-(aa-aa));
  BOOST_CHECK(~(aa-aa));
  BOOST_CHECK(!(aa+(-aa)));
  BOOST_CHECK(!((-aa)+aa));
  BOOST_CHECK_EQUAL(-1*aa, -aa);
  BOOST_CHECK_EQUAL(aa*(aa*aa), (aa*aa)*aa);
  BOOST_CHECK_EQUAL(aa*(-aa*aa), (-aa*-aa)*-aa);
  BOOST_CHECK_EQUAL(aa*(aa+aa), aa*aa+aa*aa);
  BOOST_CHECK_EQUAL(aa*(aa-aa), aa*aa-aa*aa);
  BOOST_CHECK_EQUAL(aa*(aa+(-aa)), aa*aa-aa*aa);
  BOOST_CHECK_EQUAL(aa*(aa+(-aa)), aa*aa+(-aa*aa));
  BOOST_CHECK_EQUAL(aa+aa, 2*aa);
  BOOST_CHECK_EQUAL(aa+aa+aa, 3*aa);

  BOOST_CHECK_EQUAL(lossless_multiply(aa, lossless_multiply(aa,aa)), lossless_multiply(lossless_multiply(aa, aa), aa));
  BOOST_CHECK_EQUAL(lossless_multiply(aa, lossless_multiply(-aa,aa)), lossless_multiply(lossless_multiply(-aa, -aa), -aa));
  BOOST_CHECK_EQUAL(lossless_multiply(aa, aa+aa), lossless_multiply(aa, aa) + lossless_multiply(aa, aa));
  BOOST_CHECK_EQUAL(lossless_multiply(aa, aa-aa), lossless_multiply(aa, aa) - lossless_multiply(aa, aa));
/*
  aa&aa;
  aa|aa;
  aa^aa;
  ~aa;~ac;
  aa>>90;ac>>90;
  aa<<90;ac<<90;
  aa==aa;ac==ac;
  aa!=aa;ac!=ac;
  aa<aa;ac<ac;
  aa<=aa;ac<=ac;
  aa>aa;ac>ac;
  aa>=aa;ac>=ac;

  aa+=aa;ac+=ac;
  aa-=aa;ac-=ac;
  aa*=aa;ac*=ac;
  aa&=aa;ac&=ac;
  aa|=aa;ac|=ac;
  aa^=aa;ac^=ac;
  aa>>=90;ac>>=90;
  aa<<=90;ac<<=90;*/
}

REGISTER_TESTS // This must come last in the file.
<|endoftext|>