#include "snigdhaosblackbox.h" // Includes the header file for the SnigdhaOSBlackbox class to use its declarations and functionality. #include "./ui_snigdhaosblackbox.h" // Includes the auto-generated header file for the UI created using Qt Designer. #include // Used to manage checkbox UI components. #include // Provides tools for debugging, logging information, and printing messages to the console. #include // Allows access to file metadata, such as checking file modification times. #include // Used to manage and interact with external processes (such as running commands in the terminal). #include // Provides a scrollable area in the UI to allow navigation through large widgets. #include // Creates temporary files that are automatically deleted after use. #include // Provides functionality for scheduling tasks with delays or intervals. #include // Handles responses from network requests (used to check internet connectivity). #include // Provides POSIX functions, used here for process management (e.g., restarting the application). const char* INTERNET_CHECK_URL = "https://snigdha-os.github.io/"; // URL used to verify internet connectivity by sending a network request. SnigdhaOSBlackbox::SnigdhaOSBlackbox(QWidget *parent, QString state) : QMainWindow(parent) // Calls the constructor of the QMainWindow base class to initialize the main window with the parent widget. , ui(new Ui::SnigdhaOSBlackbox) // Initializes the user interface (UI) for the SnigdhaOSBlackbox window, using the UI class auto-generated by Qt Designer. { // Sets the window icon to the specified file path, ensuring that the application window will display the given icon (SVG format). this->setWindowIcon(QIcon("/usr/share/pixmaps/snigdhaos-blackbox.svg")); // Initializes the user interface, setting up the UI components (buttons, labels, etc.) in the SnigdhaOSBlackbox window. ui->setupUi(this); // Modifies the window flags to disable the close button on the window (i.e., the application cannot be closed directly via the window). this->setWindowFlags(this->windowFlags() & -Qt::WindowCloseButtonHint); // Gets the last modified timestamp of the executable file (the current running application), which is useful for checking when the application was last updated. executable_modify_date = QFileInfo(QCoreApplication::applicationFilePath()).lastModified(); // Updates the application state based on the provided `state` parameter. // This can be a state like "WELCOME", "INTERNET", etc., depending on the condition provided by the caller. updateState(state); } SnigdhaOSBlackbox::~SnigdhaOSBlackbox() { // Frees the memory allocated for the user interface (UI) object. // The 'ui' pointer was allocated in the constructor, and it's responsible for managing the UI components of the SnigdhaOSBlackbox window. delete ui; } void SnigdhaOSBlackbox::doInternetUpRequest() { // Create a new QNetworkAccessManager instance for managing the network request. // The 'this' pointer ensures the manager is associated with the current object (SnigdhaOSBlackbox). QNetworkAccessManager* network_manager = new QNetworkAccessManager(this); // Send a HEAD request to the specified URL to check if the internet connection is up. // The HEAD request is used to check the server status without downloading the full content. QNetworkReply* network_reply = network_manager->head(QNetworkRequest(QString(INTERNET_CHECK_URL))); // Create a new QTimer instance that will be used to implement a timeout for the network request. QTimer* timer = new QTimer(this); // Set the timer to single-shot mode, so it will only trigger once after the specified interval (5 seconds). timer->setSingleShot(true); // Start the timer with a 5-second interval (5000 milliseconds). timer->start(5000); // 5 sec // Connect the timer's timeout signal to a lambda function. // This function will execute if the timer expires (i.e., after 5 seconds), aborting the network request. connect(timer, &QTimer::timeout, this, [this, timer, network_reply, network_manager]() { // Clean up the resources associated with the timer, network reply, and network manager. // Deleting them to prevent memory leaks. timer->deleteLater(); network_reply->abort(); // Abort the network request as the timeout occurred. network_reply->deleteLater(); // Delete the network reply object to free resources. network_manager->deleteLater(); // Delete the network manager object to free resources. // Retry the internet check by calling this function recursively. doInternetUpRequest(); }); // Connect the network reply's finished signal to a lambda function that handles the network reply. connect(network_reply, &QNetworkReply::finished, this, [this, timer, network_reply, network_manager]() { // Stop the timer once the network reply is finished, as we no longer need to wait for the timeout. timer->stop(); // Clean up the resources associated with the timer, network reply, and network manager after the request finishes. timer->deleteLater(); network_reply->deleteLater(); network_manager->deleteLater(); // Check if there was no error with the network reply (i.e., the internet is up). if (network_reply->error() == network_reply->NoError) { // If there was no error, update the state to indicate that the update process can begin. updateState(State::UPDATE); } else { // If there was an error, retry the internet check by calling this function recursively. doInternetUpRequest(); } }); } void SnigdhaOSBlackbox::doUpdate() { // Check if the environment variable "SNIGDHAOS_BLACKBOX_SELFUPDATE" is set. // This is typically used to determine if the application is running in an update process. if (qEnvironmentVariableIsSet("SNIGDHAOS_BLACKBOX_SELFUPDATE")) { // If the environment variable is set, update the state to "SELECT" (presumably to indicate a state where the user can select an option). updateState(State::SELECT); return; // Exit the function if the self-update process is active. } // Create a new QProcess object. This will be used to run external processes (such as the terminal command to update the system). auto process = new QProcess(this); // Create a temporary file that will be used during the update process. QTemporaryFile* file = new QTemporaryFile(this); file->open(); // Open the temporary file for writing. file->setAutoRemove(true); // Set the temporary file to be automatically removed when it is closed. // Start a new process to launch the terminal and execute the system update command. // The command runs "sudo pacman -Syyu" to update the system and then deletes the temporary file after the update is done. // It also asks the user to press Enter before closing the terminal. process->start("/usr/lib/snigdhaos/launch-terminal", QStringList() << QString("sudo pacman -Syyu 2>&1 && rm \"" + file->fileName() + "\"; read -p 'Press Enter↵ to Exit'")); // Connect the finished signal of the QProcess to a lambda function, which will be executed when the process finishes. connect(process, QOverload::of(&QProcess::finished), this, [this, process, file](int exitcode, QProcess::ExitStatus status) { // Delete the QProcess and temporary file objects after the process finishes. process->deleteLater(); file->deleteLater(); // Check the exit code of the process: // If the exit code is 0 (successful), and the temporary file no longer exists, // it indicates that the update process was successful, so relaunch the app with the state "POST_UPDATE". if (exitcode == 0 && !file->exists()) { relaunchSelf("POST_UPDATE"); } else { // If the update failed (either the exit code is non-zero or the temporary file still exists), // relaunch the app with the state "UPDATE_RETRY", indicating that the update should be retried. relaunchSelf("UPDATE_RETRY"); } }); } void SnigdhaOSBlackbox::doApply() { QStringList packages; QStringList setup_commands; QStringList prepare_commands; auto checkBoxList = ui->selectWidget_tabs->findChildren(); for (auto checkbox : checkBoxList) { if (checkbox->isChecked()) { packages += checkbox->property("packages").toStringList(); setup_commands += checkbox->property("setup_commands").toStringList(); prepare_commands += checkbox->property("prepare_commands").toStringList(); } } if (packages.isEmpty()) { updateState(State::SUCCESS); return; } if (packages.contains("podman")) { setup_commands += "systemctl enable --now podman.socket"; } if (packages.contains("docker")) { setup_commands += "systemctl enable --now docker.socket"; } packages.removeDuplicates(); QTemporaryFile* prepareFile = new QTemporaryFile(this); prepareFile->setAutoRemove(true); prepareFile->open(); QTextStream prepareStream(prepareFile); prepareStream << prepare_commands.join('\n'); prepareFile->close(); QTemporaryFile* packagesFile = new QTemporaryFile(this); packagesFile->setAutoRemove(true); packagesFile->open(); QTextStream packagesStream(packagesFile); packagesStream << packages.join(' '); packagesFile->close(); QTemporaryFile* setupFile = new QTemporaryFile(this); setupFile->setAutoRemove(true); setupFile->open(); QTextStream setupStream(setupFile); setupStream << setup_commands.join('\n'); setupFile->close(); auto process = new QProcess(this); process->start("/usr/lib/snigdhaos/launch-terminal", QStringList() << QString("/usr/lib/snigdhaos-blackbox/apply.sh \"") + prepareFile->fileName() + "\" \"" + packagesFile->fileName() + "\" \"" + setupFile->fileName() + "\""); connect(process, QOverload::of(&QProcess::finished), this, [this, process, prepareFile, packagesFile, setupFile](int exitcode, QProcess::ExitStatus status) { process->deleteLater(); prepareFile->deleteLater(); packagesFile->deleteLater(); setupFile->deleteLater(); if (exitcode == 0 && !packagesFile->exists()) { updateState(State::SELECT); } else { updateState(State::APPLY_RETRY); } }); } void SnigdhaOSBlackbox::populateSelectWidget() { if (ui->selectWidget_tabs->count() > 1) { return; } auto desktop = qEnvironmentVariable("XDG_DESKTOP_SESSION"); ui->checkBox_GNOME->setVisible(desktop == "gnome"); bool isDesktop = false; auto chassis = QFile("/sys/class/dmi/id/chassis_type"); if (chassis.open(QFile::ReadOnly)) { QStringList list = { "3", "4", "6", "7", "23", "24" }; QTextStream in(&chassis); isDesktop = list.contains(in.readLine()); } ui->checkBox_Performance->setVisible(isDesktop); populateSelectWidget("/usr/lib/snigdhaos-blackbox/webapp.txt", "WEBAPP"); } void SnigdhaOSBlackbox::populateSelectWidget(QString filename, QString label) { QFile file(filename); if (file.open(QIODevice::ReadOnly)) { QScrollArea* scroll = new QScrollArea(ui->selectWidget_tabs); QWidget* tab = new QWidget(scroll); QVBoxLayout* layout = new QVBoxLayout(tab); QTextStream in(&file); while (!in.atEnd()) { QString def = in.readLine(); QString packages = in.readLine(); QString display = in.readLine(); auto checkbox = new QCheckBox(tab); checkbox->setChecked(def == "true"); checkbox->setText(display); checkbox->setProperty("packages", packages.split(" ")); layout->addWidget(checkbox); } scroll->setWidget(tab); ui->selectWidget_tabs->addTab(scroll, label); file.close(); } } void SnigdhaOSBlackbox::updateState(State state) { if (currentState != state) { currentState = state; this->show(); this->activateWindow(); this->raise(); switch (state) { case State::WELCOME: ui->mainStackedWidget->setCurrentWidget(ui->textWidget); ui->textStackedWidget->setCurrentWidget(ui->textWidget_welcome); ui->textWidget_buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); break; case State::INTERNET: ui->mainStackedWidget->setCurrentWidget(ui->mainStackedWidget); ui->waitingWidget_text->setText("Waiting For Internet Connection..."); doInternetUpRequest(); break; case State::UPDATE: ui->mainStackedWidget->setCurrentWidget(ui->waitingWidget); ui->waitingWidget_text->setText("Please Wait! Till We Finish The Update..."); doUpdate(); break; case State::UPDATE_RETRY: ui->mainStackedWidget->setCurrentWidget(ui->textWidget); ui->textStackedWidget->setCurrentWidget(ui->textWidget_updateRetry); ui->textWidget_buttonBox->setStandardButtons(QDialogButtonBox::Yes | QDialogButtonBox::No); break; case State::QUIT: ui->mainStackedWidget->setCurrentWidget(ui->textWidget); ui->textStackedWidget->setCurrentWidget(ui->textWidget_quit); ui->textWidget_buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Reset); break; case State::SELECT: ui->mainStackedWidget->setCurrentWidget(ui->waitingWidget); populateSelectWidget(); break; case State::APPLY: ui->mainStackedWidget->setCurrentWidget(ui->waitingWidget); ui->waitingWidget_text->setText("We are applying the changes..."); doApply(); break; case State::APPLY_RETRY: ui->mainStackedWidget->setCurrentWidget(ui->textWidget); ui->textStackedWidget->setCurrentWidget(ui->textWidget_applyRetry); ui->textWidget_buttonBox->setStandardButtons(QDialogButtonBox::Yes | QDialogButtonBox::No | QDialogButtonBox::Reset); break; case State::SUCCESS: ui->mainStackedWidget->setCurrentWidget(ui->textWidget); ui->textStackedWidget->setCurrentWidget(ui->textWidget_success); ui->textWidget_buttonBox->setStandardButtons(QDialogButtonBox::Ok); break; } } } void SnigdhaOSBlackbox::updateState(QString state) { if (state == "POST_UPDATE"){ updateState(State::SELECT); } else if (state == "UPDATE_RETRY") { updateState(State::UPDATE_RETRY); } else { updateState(State::WELCOME); } } void SnigdhaOSBlackbox::relaunchSelf(QString param) { auto binary = QFileInfo(QCoreApplication::applicationFilePath()); if (executable_modify_date != binary.lastModified()) { execlp(binary.absoluteFilePath().toUtf8().constData(), binary.fileName().toUtf8().constData(), param.toUtf8().constData(), NULL); exit(0); } else { updateState(param); } } void SnigdhaOSBlackbox::on_textWidget_buttonBox_clicked(QAbstractButton* button) { switch(currentState) { case State::WELCOME: if (ui->textWidget_buttonBox->standardButton(button) == QDialogButtonBox::Ok) { updateState(State::INTERNET); } break; case State::UPDATE_RETRY: if (ui->textWidget_buttonBox->standardButton(button) == QDialogButtonBox::Yes) { updateState(State::INTERNET); } break; case State::APPLY_RETRY: if (ui->textWidget_buttonBox->standardButton(button) == QDialogButtonBox::Yes) { updateState(State::APPLY); } else if (ui->textWidget_buttonBox->standardButton(button) == QDialogButtonBox::Reset) { updateState(State::SELECT); } break; case State::SUCCESS: if (ui->textWidget_buttonBox->standardButton(button) == QDialogButtonBox::Ok) { QApplication::quit(); } break; case State::QUIT: if (ui->textWidget_buttonBox->standardButton(button) == QDialogButtonBox::No || ui->textWidget_buttonBox->standardButton(button) == QDialogButtonBox::Ok) { QApplication::quit(); } else { updateState(State::WELCOME); } break; default:; } if (ui->textWidget_buttonBox->standardButton(button) == QDialogButtonBox::No || ui->textWidget_buttonBox->standardButton(button) == QDialogButtonBox::Cancel) { updateState(State::QUIT); } } void SnigdhaOSBlackbox::on_selectWidget_buttonBox_Clicked(QAbstractButton* button) { if (ui->selectWidget_buttonBox->standardButton(button) == QDialogButtonBox::Ok) { updateState(State::APPLY); } else { updateState(State::QUIT); } }