Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f75e4d236c | |||
| e35ccf3baf | |||
| f320851fb5 | |||
| 12bbc7371d | |||
| 709b7fd7ca | |||
| cc12b4765c | |||
| 7e8c45e88e | |||
| c98f239f2c | |||
| 4468ed5065 | |||
| 276fa5d738 | |||
| 28a5ef1850 | |||
| 91ed5050a8 |
16 changed files with 1331 additions and 25687 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -1,4 +1,8 @@
|
||||||
build/*
|
build/*
|
||||||
*.sh
|
*.sh
|
||||||
*.json
|
*.json
|
||||||
|
*.qt/*
|
||||||
|
Build/*
|
||||||
|
.qtcreator/*
|
||||||
|
*swp
|
||||||
|
*.cmake
|
||||||
|
|
|
||||||
BIN
.ninja_deps
BIN
.ninja_deps
Binary file not shown.
|
|
@ -1,6 +0,0 @@
|
||||||
# ninja log v6
|
|
||||||
1 62 1754932580772976334 helloworld_autogen/timestamp 7853c8a1c93b2124
|
|
||||||
1 62 1754932580772976334 helloworld_autogen/mocs_compilation.cpp 7853c8a1c93b2124
|
|
||||||
1 62 1754932580772976334 /home/alfred/custom_start_up_build/helloworld_autogen/timestamp 7853c8a1c93b2124
|
|
||||||
1 62 1754932580772976334 /home/alfred/custom_start_up_build/helloworld_autogen/mocs_compilation.cpp 7853c8a1c93b2124
|
|
||||||
62 80 1754932580784976334 CMakeFiles/helloworld.dir/helloworld_autogen/mocs_compilation.cpp.o 6c2b962c6a90f545
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
cmake_minimum_required(VERSION 3.16...3.21)
|
|
||||||
|
|
||||||
# These are part of the public API. Projects should use them to provide a
|
|
||||||
# consistent set of prefix-relative destinations.
|
|
||||||
if(NOT QT_DEPLOY_BIN_DIR)
|
|
||||||
set(QT_DEPLOY_BIN_DIR "bin")
|
|
||||||
endif()
|
|
||||||
if(NOT QT_DEPLOY_LIB_DIR)
|
|
||||||
set(QT_DEPLOY_LIB_DIR "lib")
|
|
||||||
endif()
|
|
||||||
if(NOT QT_DEPLOY_PLUGINS_DIR)
|
|
||||||
set(QT_DEPLOY_PLUGINS_DIR "plugins")
|
|
||||||
endif()
|
|
||||||
if(NOT QT_DEPLOY_QML_DIR)
|
|
||||||
set(QT_DEPLOY_QML_DIR "qml")
|
|
||||||
endif()
|
|
||||||
if(NOT QT_DEPLOY_PREFIX)
|
|
||||||
set(QT_DEPLOY_PREFIX "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}")
|
|
||||||
endif()
|
|
||||||
if(QT_DEPLOY_PREFIX STREQUAL "")
|
|
||||||
set(QT_DEPLOY_PREFIX .)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# These are internal implementation details. They may be removed at any time.
|
|
||||||
set(__QT_DEPLOY_SYSTEM_NAME "Linux")
|
|
||||||
set(__QT_DEPLOY_IS_SHARED_LIBS_BUILD "ON")
|
|
||||||
set(__QT_DEPLOY_TOOL "")
|
|
||||||
set(__QT_DEPLOY_IMPL_DIR "/home/alfred/custom_start_up_build/.qt")
|
|
||||||
set(__QT_DEPLOY_VERBOSE "")
|
|
||||||
set(__QT_CMAKE_EXPORT_NAMESPACE "Qt6")
|
|
||||||
set(__QT_DEPLOY_GENERATOR_IS_MULTI_CONFIG "0")
|
|
||||||
set(__QT_DEPLOY_ACTIVE_CONFIG "")
|
|
||||||
set(__QT_NO_CREATE_VERSIONLESS_FUNCTIONS "")
|
|
||||||
set(__QT_DEFAULT_MAJOR_VERSION "6")
|
|
||||||
|
|
||||||
# Define the CMake commands to be made available during deployment.
|
|
||||||
set(__qt_deploy_support_files
|
|
||||||
"/usr/lib/aarch64-linux-gnu/cmake/Qt6Core/Qt6CoreDeploySupport.cmake"
|
|
||||||
)
|
|
||||||
foreach(__qt_deploy_support_file IN LISTS __qt_deploy_support_files)
|
|
||||||
include("${__qt_deploy_support_file}")
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
unset(__qt_deploy_support_file)
|
|
||||||
unset(__qt_deploy_support_files)
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
project(helloworld VERSION 1.0.0 LANGUAGES CXX)
|
project(StartUpRunner VERSION 1.0.0 LANGUAGES CXX)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 23)
|
set(CMAKE_CXX_STANDARD 26)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
find_package(Boost CONFIG)
|
find_package(Boost CONFIG)
|
||||||
find_package(Qt6 REQUIRED COMPONENTS Core)
|
find_package(nlohmann_json CONFIG)
|
||||||
|
|
||||||
qt_add_executable(StartUpRunner main.cpp)
|
add_executable(StartUpRunner main.cpp
|
||||||
|
appinfo.h appinfo.cpp
|
||||||
|
executionmanager.h executionmanager.cpp
|
||||||
|
log.h log.cpp)
|
||||||
|
|
||||||
target_link_libraries(StartUpRunner PRIVATE Qt6::Core)
|
target_link_libraries(StartUpRunner PRIVATE nlohmann_json::nlohmann_json)
|
||||||
|
|
|
||||||
322
appinfo.cpp
Normal file
322
appinfo.cpp
Normal file
|
|
@ -0,0 +1,322 @@
|
||||||
|
#include "appinfo.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
AppInfo::~AppInfo() {
|
||||||
|
std::cout << "Module: " << getModuleName() << " finished ops." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppInfo::setProgramName(std::string name)
|
||||||
|
{
|
||||||
|
this->program_name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppInfo::setModuleName(std::string name)
|
||||||
|
{
|
||||||
|
this->module_name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppInfo::setArgs(std::deque<std::string> args)
|
||||||
|
{
|
||||||
|
this->args = args;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppInfo::setWorkingDir(std::string working_dir)
|
||||||
|
{
|
||||||
|
this->working_dir = working_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppInfo::setEnv(string_map env)
|
||||||
|
{
|
||||||
|
this->env = env;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppInfo::setAfter(std::unordered_map<std::string, std::string> after)
|
||||||
|
{
|
||||||
|
this->after = after;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppInfo::setCreateLog(bool create_log)
|
||||||
|
{
|
||||||
|
this->create_log = create_log;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppInfo::setRunMode(std::string mode)
|
||||||
|
{
|
||||||
|
if(mode == "script") {
|
||||||
|
this->mode = RunMode::script;
|
||||||
|
}
|
||||||
|
else if(mode == "daemon") {
|
||||||
|
this->mode = RunMode::daemon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppInfo::setFinished()
|
||||||
|
{
|
||||||
|
this->state = State::exited;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppInfo::setRunning()
|
||||||
|
{
|
||||||
|
this->state = State::running;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppInfo::insertParent(std::shared_ptr<AppInfo> parent, State state)
|
||||||
|
{
|
||||||
|
this->parents[parent] = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AppInfo::isParentDependencyCleared(std::osyncstream &out) const
|
||||||
|
{
|
||||||
|
for(auto &[key, value]: parents) {
|
||||||
|
if(key->getState() != value) {
|
||||||
|
out << key->getModuleName() << " not at state: " << stateToString(value) <<
|
||||||
|
" but is at:" << stateToString(key->getState()) << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AppInfo::getModuleName() const
|
||||||
|
{
|
||||||
|
return module_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AppInfo::getProgramName() const
|
||||||
|
{
|
||||||
|
return program_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AppInfo::getWorkingDir() const
|
||||||
|
{
|
||||||
|
return working_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppInfo::string_map AppInfo::getEnv() const
|
||||||
|
{
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AppInfo::getAfterString() const
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
for(auto &[key,value]: after) {
|
||||||
|
ss << key << "[" << value << "], ";
|
||||||
|
}
|
||||||
|
std::string after = ss.str();
|
||||||
|
after = after.substr(0,after.size() - 2);
|
||||||
|
return after;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AppInfo::getAfterKeys() const
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
for(auto &[key,value]: after) {
|
||||||
|
ss << key << ", ";
|
||||||
|
}
|
||||||
|
std::string after = ss.str();
|
||||||
|
after = after.substr(0,after.size() - 2);
|
||||||
|
return after;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppInfo::RunMode AppInfo::getRunMode() const
|
||||||
|
{
|
||||||
|
return this->mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppInfo::State AppInfo::getState() const
|
||||||
|
{
|
||||||
|
return this->state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppInfo::copyFromData(AppInfo::AppData &other)
|
||||||
|
{
|
||||||
|
this->module_name = other.module_name;
|
||||||
|
this->program_name = other.program_name;
|
||||||
|
this->create_log = other.create_log;
|
||||||
|
this->working_dir = other.working_dir;
|
||||||
|
for(auto &i: other.args) {
|
||||||
|
this->args.push_back(i);
|
||||||
|
}
|
||||||
|
this->after = other.after;
|
||||||
|
this->env = other.env;
|
||||||
|
setRunMode(other.run_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_map<std::shared_ptr<AppInfo>, AppInfo::State> AppInfo::getParents() const
|
||||||
|
{
|
||||||
|
return this->parents;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_map<std::string, std::string> AppInfo::getAfter() const
|
||||||
|
{
|
||||||
|
return after;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AppInfo::getCreateLog()
|
||||||
|
{
|
||||||
|
return create_log;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::deque<std::string> AppInfo::getArgs() const
|
||||||
|
{
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppInfo::AppInfo() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AppInfo::operator==(const AppInfo &other)
|
||||||
|
{
|
||||||
|
if(this->module_name == other.module_name && this->program_name == other.program_name
|
||||||
|
&& this->args == other.args
|
||||||
|
&& this->working_dir == other.working_dir
|
||||||
|
&& this->after == other.after
|
||||||
|
&& this->create_log == other.create_log
|
||||||
|
)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppInfo::printInfo(std::ostream &out)
|
||||||
|
{
|
||||||
|
out << "************************************App************************************" << std::endl;
|
||||||
|
out << "Module: " << module_name << std::endl;
|
||||||
|
out << "App Name: " << program_name << std::endl;
|
||||||
|
out << "Arg List: " << std::endl;
|
||||||
|
int index = 0;
|
||||||
|
for(auto &i: args) {
|
||||||
|
out << "Arg[" << index++ << "]: " << i << std::endl;
|
||||||
|
}
|
||||||
|
out << "Run Mode: " << runmodeToString() << std::endl;
|
||||||
|
out << "Working Directory: " << working_dir << std::endl;
|
||||||
|
std::stringstream ss;
|
||||||
|
for(auto &[key,value]: after) {
|
||||||
|
ss << key << "[" << value << "], ";
|
||||||
|
}
|
||||||
|
std::string after = ss.str();
|
||||||
|
if(!after.empty() && after.size() >= 2)
|
||||||
|
after = after.substr(0,after.size() - 2);
|
||||||
|
out << "After: " << after << std::endl;
|
||||||
|
std::stringstream ss2, ss3;
|
||||||
|
for(auto &[key, value]: parents) {
|
||||||
|
if(!key) {
|
||||||
|
ss2 << "Invalid Parent" << std::endl;
|
||||||
|
}
|
||||||
|
else if(value == State::running) {
|
||||||
|
ss2 << key->getModuleName() << " At: " << key << " with state: running." << std::endl;
|
||||||
|
}
|
||||||
|
else if(value == State::exited) {
|
||||||
|
ss2 << key->getModuleName() << " At: " << key << " with state: exited." << std::endl;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ss2 << key->getModuleName() << " At: " << key << " with state: unknown." << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(parents.size() != 0) {
|
||||||
|
out << "Parents: " << parents.size() << " modules." << std::endl;
|
||||||
|
out << ss2.str();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
out << "Parents: None." << std::endl;
|
||||||
|
}
|
||||||
|
for(auto &[key,value]: env) {
|
||||||
|
ss3 << key << "=" << value << ", ";
|
||||||
|
}
|
||||||
|
std::string env = ss3.str();
|
||||||
|
if(!env.empty() && env.size() >= 2)
|
||||||
|
env = env.substr(0,env.size() - 2);
|
||||||
|
out << "Environment: " << env << std::endl;
|
||||||
|
out << "Create Log: " << create_log << std::endl;
|
||||||
|
out << "************************************End************************************" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AppInfo::runmodeToString() const
|
||||||
|
{
|
||||||
|
switch(this->mode) {
|
||||||
|
case RunMode::script:
|
||||||
|
return "script";
|
||||||
|
case RunMode::daemon:
|
||||||
|
return "daemon";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AppInfo::stateToString() const
|
||||||
|
{
|
||||||
|
return stateToString(this->state);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AppInfo::stateToString(State s) const
|
||||||
|
{
|
||||||
|
switch(s) {
|
||||||
|
case State::initial:
|
||||||
|
return "initial";
|
||||||
|
case State::running:
|
||||||
|
return "running";
|
||||||
|
case State::exited:
|
||||||
|
return "exited";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppInfo::AppData::printInfo(std::ostream &out)
|
||||||
|
{
|
||||||
|
out << "************************************App************************************" << std::endl;
|
||||||
|
out << "Module: " << AppData::module_name << std::endl;
|
||||||
|
out << "App Name: " << AppData::program_name << std::endl;
|
||||||
|
out << "Arg List: " << std::endl;
|
||||||
|
int index = 0;
|
||||||
|
for(auto &i: AppData::args) {
|
||||||
|
out << "Arg[" << index++ << "]: " << i << std::endl;
|
||||||
|
}
|
||||||
|
out << "Run Mode: " << run_mode << std::endl;
|
||||||
|
out << "Working Directory: " << AppData::working_dir << std::endl;
|
||||||
|
std::stringstream ss, ss2;
|
||||||
|
for(auto &[key,value]: AppData::after) {
|
||||||
|
ss << key << "[" << value << "], ";
|
||||||
|
}
|
||||||
|
std::string after = ss.str();
|
||||||
|
if(!after.empty() && after.size() >= 2)
|
||||||
|
after = after.substr(0,after.size() - 2);
|
||||||
|
out << "After: " << after << std::endl;
|
||||||
|
ss.clear();
|
||||||
|
ss.flush();
|
||||||
|
for(auto &[key,value]: AppData::env) {
|
||||||
|
ss2 << key << "=" << value << ", ";
|
||||||
|
}
|
||||||
|
std::string env = ss2.str();
|
||||||
|
if(!env.empty() && env.size() >= 2)
|
||||||
|
env = env.substr(0,env.size() - 2);
|
||||||
|
out << "Environment: " << env << std::endl;
|
||||||
|
out << "Create Log: " << AppData::create_log << std::endl;
|
||||||
|
out << "************************************End************************************" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AppInfo::AppData::getAfterString() const
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
for(auto &[key,value]: after) {
|
||||||
|
ss << key << "[" << value << "], ";
|
||||||
|
}
|
||||||
|
std::string after = ss.str();
|
||||||
|
after = after.substr(0,after.size() - 2);
|
||||||
|
return after;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AppInfo::AppData::getAfterKeys() const
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
for(auto &[key,value]: after) {
|
||||||
|
ss << key << ", ";
|
||||||
|
}
|
||||||
|
std::string after = ss.str();
|
||||||
|
after = after.substr(0,after.size() - 2);
|
||||||
|
return after;
|
||||||
|
}
|
||||||
89
appinfo.h
Normal file
89
appinfo.h
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
#ifndef APPINFO_H
|
||||||
|
#define APPINFO_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <deque>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <syncstream>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
class AppInfo
|
||||||
|
{
|
||||||
|
typedef std::unordered_map<std::string, std::string> string_map;
|
||||||
|
public:
|
||||||
|
enum class State {
|
||||||
|
initial,
|
||||||
|
running,
|
||||||
|
exited
|
||||||
|
};
|
||||||
|
enum class RunMode {
|
||||||
|
script,
|
||||||
|
daemon
|
||||||
|
};
|
||||||
|
struct AppData {
|
||||||
|
std::string program_name;
|
||||||
|
std::string module_name;
|
||||||
|
std::deque<std::string> args;
|
||||||
|
std::string working_dir;
|
||||||
|
string_map after;
|
||||||
|
string_map env;
|
||||||
|
std::string run_mode;
|
||||||
|
bool create_log = false;
|
||||||
|
void printInfo(std::ostream &out);
|
||||||
|
std::string getAfterString() const;
|
||||||
|
std::string getAfterKeys() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string program_name;
|
||||||
|
std::string module_name;
|
||||||
|
std::deque<std::string> args;
|
||||||
|
std::string working_dir;
|
||||||
|
string_map after;
|
||||||
|
string_map env;
|
||||||
|
bool create_log = false;
|
||||||
|
std::unordered_map<std::shared_ptr<AppInfo>, State> parents;
|
||||||
|
std::atomic<State> state{State::initial};
|
||||||
|
RunMode mode = RunMode::script;
|
||||||
|
public:
|
||||||
|
void setProgramName(std::string name);
|
||||||
|
void setModuleName(std::string name);
|
||||||
|
void setArgs(std::deque<std::string> args);
|
||||||
|
void setWorkingDir(std::string working_dir);
|
||||||
|
void setAfter(string_map after);
|
||||||
|
void setEnv(string_map env);
|
||||||
|
void setCreateLog(bool create_log);
|
||||||
|
void setRunMode(std::string mode);
|
||||||
|
void setFinished();
|
||||||
|
void setRunning();
|
||||||
|
void insertParent(std::shared_ptr<AppInfo> parent, State state);
|
||||||
|
bool isParentDependencyCleared(std::osyncstream &out) const;
|
||||||
|
std::string getModuleName() const;
|
||||||
|
std::string getProgramName() const;
|
||||||
|
std::deque<std::string> getArgs() const;
|
||||||
|
std::string getWorkingDir() const;
|
||||||
|
string_map getEnv() const;
|
||||||
|
string_map getAfter() const;
|
||||||
|
std::string getAfterString() const;
|
||||||
|
std::string getAfterKeys() const;
|
||||||
|
RunMode getRunMode() const;
|
||||||
|
State getState() const;
|
||||||
|
std::unordered_map<std::shared_ptr<AppInfo>, State> getParents() const;
|
||||||
|
void copyFromData(AppInfo::AppData &other);
|
||||||
|
bool getCreateLog();
|
||||||
|
AppInfo();
|
||||||
|
~AppInfo();
|
||||||
|
AppInfo(const AppInfo &other) = delete;
|
||||||
|
AppInfo(const AppInfo &&other) = delete;
|
||||||
|
AppInfo &operator=(const AppInfo &other) = delete;
|
||||||
|
bool operator==(const AppInfo &other);
|
||||||
|
void printInfo(std::ostream& out);
|
||||||
|
private:
|
||||||
|
std::string runmodeToString() const;
|
||||||
|
std::string stateToString() const;
|
||||||
|
std::string stateToString(State s) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // APPINFO_H
|
||||||
215
executionmanager.cpp
Normal file
215
executionmanager.cpp
Normal file
|
|
@ -0,0 +1,215 @@
|
||||||
|
#include "executionmanager.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <format>
|
||||||
|
#include <syncstream>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
ExecutionManager::ExecutionManager(int threads) :
|
||||||
|
log_file{nullptr}
|
||||||
|
{
|
||||||
|
this->threads = threads;
|
||||||
|
if(this->threads <= 1) {
|
||||||
|
this->threads = 2; // Does not work with threads = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExecutionManager::setLogFile(const Log &log_file)
|
||||||
|
{
|
||||||
|
this->log_file.copyFromOther(log_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExecutionManager::setLogFileMaxSize(long long size)
|
||||||
|
{
|
||||||
|
log_file_max_size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExecutionManager::setLogDirectoy(std::string log_dir)
|
||||||
|
{
|
||||||
|
this->log_dir = log_dir;
|
||||||
|
if(!std::filesystem::exists(log_dir)) {
|
||||||
|
std::filesystem::create_directories(log_dir);
|
||||||
|
log_file << "Created log dir: " << log_dir << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExecutionManager::setJobs(std::vector<std::shared_ptr<AppInfo> > &job_list)
|
||||||
|
{
|
||||||
|
this->job_list = std::move(job_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExecutionManager::run()
|
||||||
|
{
|
||||||
|
finished = false;
|
||||||
|
initial_list_size = job_list.size();
|
||||||
|
while(true) {
|
||||||
|
for(int i=active_thread_count.load(); i < threads ;i++) {
|
||||||
|
int val = 0;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock_get(this->job_list_mutex);
|
||||||
|
val = index_count++;
|
||||||
|
if(job_list.empty()) {
|
||||||
|
log_file << "Main Thread: Job list is empty, waiting for finish." << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(val > (initial_list_size - 1)) {
|
||||||
|
log_file << "Main Thread: All jobs queued." << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
active_thread_count.fetch_add(1);
|
||||||
|
std::shared_ptr<std::thread> thread = std::make_shared<std::thread>([this, val](){
|
||||||
|
std::shared_ptr<AppInfo> ptr;
|
||||||
|
std::osyncstream os(log_file.rdbuf());
|
||||||
|
size_t list_size = 0;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock_get(this->job_list_mutex);
|
||||||
|
if(job_list.size() == 0) {
|
||||||
|
os << std::vformat("Thread[{}] called while the job list was empty: ", std::make_format_args(val))
|
||||||
|
<< std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ptr = this->job_list[0];
|
||||||
|
job_list.erase(job_list.begin());
|
||||||
|
list_size = job_list.size();
|
||||||
|
}
|
||||||
|
std::string module_name = ptr->getModuleName();
|
||||||
|
os << std::vformat("Thread[{}] running: ", std::make_format_args(val))
|
||||||
|
<< module_name << " current list size: " << list_size << std::endl;
|
||||||
|
int old = state_change.load();
|
||||||
|
while(!ptr->isParentDependencyCleared(os))
|
||||||
|
{
|
||||||
|
state_change.wait(old);
|
||||||
|
old = state_change.load();
|
||||||
|
os << std::vformat("Thread[{}] waiting for dependency: ", std::make_format_args(val))
|
||||||
|
<< module_name << " current state count: " << old << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create variables required for forking
|
||||||
|
std::string dir = ptr->getWorkingDir();
|
||||||
|
std::string program_name = ptr->getProgramName();
|
||||||
|
std::string program_log_name_temp = module_name + ".log";
|
||||||
|
std::string program_log_name = "";
|
||||||
|
for(auto &ch: program_log_name_temp) {
|
||||||
|
if(ch != ' ') {
|
||||||
|
program_log_name += ch;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
program_log_name += '_';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::string program_log_file = std::filesystem::path(this->log_dir) / program_log_name;
|
||||||
|
bool create_log = ptr->getCreateLog();
|
||||||
|
std::deque<std::string> pargs = ptr->getArgs();
|
||||||
|
int total_args = pargs.size() + 2;
|
||||||
|
std::vector<char*> args(total_args);
|
||||||
|
args[0] = program_name.data();
|
||||||
|
for(int i=1,index=0; index < pargs.size(); i++,index++) {
|
||||||
|
args[i] = pargs[index].data();
|
||||||
|
}
|
||||||
|
args[total_args - 1] = (char *)NULL;
|
||||||
|
bool log_file_exists = std::filesystem::exists(program_log_file);
|
||||||
|
long long size = 0;
|
||||||
|
if(log_file_exists)
|
||||||
|
size = std::filesystem::file_size(program_log_file);
|
||||||
|
// Do fork and run
|
||||||
|
auto pid = fork();
|
||||||
|
FILE *popen_file = nullptr;
|
||||||
|
std::unordered_map<std::string,std::string> env = ptr->getEnv();
|
||||||
|
switch(pid) {
|
||||||
|
case 0: {
|
||||||
|
if(!dir.empty()) {
|
||||||
|
int err = chdir(dir.c_str());
|
||||||
|
}
|
||||||
|
for(auto &[key,value]: env) {
|
||||||
|
setenv(key.data(), value.data(), 1);
|
||||||
|
}
|
||||||
|
if(create_log) {
|
||||||
|
int output_fd = -99;
|
||||||
|
if(!log_file_exists)
|
||||||
|
output_fd = open(program_log_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
|
||||||
|
else {
|
||||||
|
if(size < log_file_max_size)
|
||||||
|
{
|
||||||
|
output_fd = open(program_log_file.c_str(), O_WRONLY | O_APPEND, S_IRUSR | S_IWUSR);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
output_fd = open(program_log_file.c_str(), O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (output_fd < 0) {
|
||||||
|
_exit(3);
|
||||||
|
}
|
||||||
|
if (dup2(output_fd, STDOUT_FILENO) < 0) {
|
||||||
|
_exit(3);
|
||||||
|
}
|
||||||
|
if (dup2(output_fd, STDERR_FILENO) < 0) {
|
||||||
|
_exit(3);
|
||||||
|
}
|
||||||
|
close(output_fd);
|
||||||
|
}
|
||||||
|
execv(program_name.c_str(), args.data());
|
||||||
|
_exit(1);
|
||||||
|
}
|
||||||
|
case -1: {
|
||||||
|
os << "Fork failed for module: " << module_name << std::endl;
|
||||||
|
ptr->setFinished(); // Or a dedicated failed state
|
||||||
|
state_change.fetch_add(1);
|
||||||
|
state_change.notify_all();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
// After fork in parent do
|
||||||
|
ptr->setRunning();
|
||||||
|
state_change.fetch_add(1);
|
||||||
|
state_change.notify_all();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If script daemon wait here
|
||||||
|
AppInfo::RunMode mode = ptr->getRunMode();
|
||||||
|
int status;
|
||||||
|
if(mode == AppInfo::RunMode::script) {
|
||||||
|
os << "Run mode is script so will wait for child to finish: " << module_name << std::endl;
|
||||||
|
int wait = waitpid(pid, &status, WUNTRACED);
|
||||||
|
ptr->setFinished();
|
||||||
|
os << "Parent wait finished: " << wait << " child status: " << status << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set finished
|
||||||
|
state_change.fetch_add(1);
|
||||||
|
state_change.notify_all();
|
||||||
|
os << std::vformat("Thread[{}] finished: ", std::make_format_args(val))
|
||||||
|
<< module_name<< std::endl;
|
||||||
|
active_thread_count.fetch_sub(1);
|
||||||
|
});
|
||||||
|
thread_list.push_back(thread);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock_get(this->job_list_mutex);
|
||||||
|
if(job_list.empty()) {
|
||||||
|
log_file << "Main Thread: Job list is empty, waiting for finish." << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if((index_count) > (initial_list_size - 1)) {
|
||||||
|
log_file << "Main Thread: All jobs queued." << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (active_thread_count.load() >= threads) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(auto i: thread_list) {
|
||||||
|
if(i->joinable())
|
||||||
|
i->join();
|
||||||
|
}
|
||||||
|
finished = true;
|
||||||
|
}
|
||||||
34
executionmanager.h
Normal file
34
executionmanager.h
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef EXECUTIONMANAGER_H
|
||||||
|
#define EXECUTIONMANAGER_H
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <memory>
|
||||||
|
#include <atomic>
|
||||||
|
#include <thread>
|
||||||
|
#include "appinfo.h"
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
class ExecutionManager
|
||||||
|
{
|
||||||
|
bool finished = false;
|
||||||
|
int threads;
|
||||||
|
int index_count = 0;
|
||||||
|
int initial_list_size = 0;
|
||||||
|
long long int log_file_max_size = 0;
|
||||||
|
std::string log_dir;
|
||||||
|
std::mutex job_list_mutex;
|
||||||
|
std::atomic<int> state_change = 0;
|
||||||
|
std::atomic<int> active_thread_count = 0;
|
||||||
|
std::vector<std::shared_ptr<std::thread>> thread_list;
|
||||||
|
std::vector<std::shared_ptr<AppInfo>> job_list;
|
||||||
|
Log log_file;
|
||||||
|
public:
|
||||||
|
ExecutionManager(int threads=16);
|
||||||
|
void setLogFile(const Log &other);
|
||||||
|
void setLogFileMaxSize(long long int size);
|
||||||
|
void setLogDirectoy(std::string log_dir);
|
||||||
|
void setJobs(std::vector<std::shared_ptr<AppInfo>>& job_list);
|
||||||
|
void run();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // EXECUTIONMANAGER_H
|
||||||
79
log.cpp
Normal file
79
log.cpp
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
Log::Log(std::string file_name, int max_size) :
|
||||||
|
log_file{nullptr}
|
||||||
|
{
|
||||||
|
bool out_mode = false;
|
||||||
|
if(std::filesystem::exists(file_name))
|
||||||
|
if(std::filesystem::file_size(file_name) < max_size)
|
||||||
|
log_file_s.open(file_name, std::fstream::app);
|
||||||
|
else {
|
||||||
|
out_mode = true;
|
||||||
|
std::cout << "Log file size greater than: " << max_size << std::endl;
|
||||||
|
std::cout << "Truncating..." << std::endl;
|
||||||
|
log_file_s.open(file_name, std::fstream::out);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log_file_s.open(file_name, std::fstream::out);
|
||||||
|
out_mode = true;
|
||||||
|
}
|
||||||
|
if(log_file_s.is_open())
|
||||||
|
{
|
||||||
|
std::cout << "Logs openned in " << (( out_mode ) ? "truncate" : "append") << " mode" << std::endl;
|
||||||
|
log_file.rdbuf(log_file_s.rdbuf());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::cout << "Unable to open log file" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::Log(int *other) :
|
||||||
|
log_file{nullptr}
|
||||||
|
{
|
||||||
|
if(other == nullptr)
|
||||||
|
return;
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::~Log()
|
||||||
|
{
|
||||||
|
if(log_file_s.is_open()) {
|
||||||
|
log_file_s.flush();
|
||||||
|
log_file_s.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::Log(const Log &other):
|
||||||
|
log_file{nullptr}
|
||||||
|
{
|
||||||
|
log_file.rdbuf(other.log_file.rdbuf());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Log::copyFromOther(const Log &other)
|
||||||
|
{
|
||||||
|
log_file.rdbuf(other.log_file.rdbuf());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Log::isOpen()
|
||||||
|
{
|
||||||
|
return log_file_s.is_open();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream &Log::rdbuf()
|
||||||
|
{
|
||||||
|
return log_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log &Log::operator<<(std::ostream &(*f)(std::ostream &)) {
|
||||||
|
if(f == (std::basic_ostream<char>& (*)(std::basic_ostream<char>&)) &std::endl) {
|
||||||
|
log_file << f;
|
||||||
|
std::cout << f;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log &Log::operator<<(std::filesystem::path p) {
|
||||||
|
log_file << p.string();
|
||||||
|
std::cout << p.string();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
41
log.h
Normal file
41
log.h
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
#ifndef LOG_H
|
||||||
|
#define LOG_H
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
class Log {
|
||||||
|
std::ostream log_file;
|
||||||
|
std::fstream log_file_s;
|
||||||
|
public:
|
||||||
|
explicit Log(std::string file_name, int max_size);
|
||||||
|
explicit Log(int *other);
|
||||||
|
~Log();
|
||||||
|
Log(const Log &other);
|
||||||
|
void copyFromOther(const Log &other);
|
||||||
|
bool isOpen();
|
||||||
|
std::ostream& rdbuf();
|
||||||
|
Log& operator<<(std::filesystem::path p);
|
||||||
|
Log& operator<<(std::ostream&(*f)(std::ostream&));
|
||||||
|
template<typename T>
|
||||||
|
Log &operator<<(T t) {
|
||||||
|
log_file << t;
|
||||||
|
std::cout << t;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
template<typename T>
|
||||||
|
Log &operator<<(const T *t) {
|
||||||
|
log_file << t;
|
||||||
|
std::cout << t;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
template<typename T>
|
||||||
|
Log &operator<<(T *t) {
|
||||||
|
log_file << t;
|
||||||
|
std::cout << t;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // LOG_H
|
||||||
615
main.cpp
615
main.cpp
|
|
@ -2,108 +2,537 @@
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <format>
|
#include <format>
|
||||||
#include <QObject>
|
#include <nlohmann/json.hpp>
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QJsonArray>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <QString>
|
#include <deque>
|
||||||
#include <QCoreApplication>
|
#include <vector>
|
||||||
#include <QProcess>
|
#include <cctype>
|
||||||
|
#include "appinfo.h"
|
||||||
|
#include "executionmanager.h"
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <pwd.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
}
|
||||||
|
|
||||||
char *get_current_time() {
|
char *get_current_time() {
|
||||||
const auto now = std::chrono::system_clock::now();
|
const auto now = std::chrono::system_clock::now();
|
||||||
const std::time_t t_c = std::chrono::system_clock::to_time_t(now);
|
const std::time_t t_c = std::chrono::system_clock::to_time_t(now);
|
||||||
return std::ctime(&t_c);
|
return std::ctime(&t_c);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Options {
|
||||||
|
std::string json_file_path = "";
|
||||||
|
std::string log_directory = "";
|
||||||
|
long long int log_file_max_size = 10 * 1024 * 1024;
|
||||||
|
int threads = 16;
|
||||||
|
}options;
|
||||||
|
|
||||||
|
void createConfig() {
|
||||||
|
using json = nlohmann::json;
|
||||||
|
json config;
|
||||||
|
config["log_file_max_size"] = 10 * 1024 * 1024;
|
||||||
|
std::string str = config.dump();
|
||||||
|
std::fstream file_out("config.json", std::fstream::out);
|
||||||
|
file_out << str;
|
||||||
|
file_out.flush();
|
||||||
|
file_out.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseConfig() {
|
||||||
|
using json = nlohmann::json;
|
||||||
|
if(!std::filesystem::exists("config.json")) {
|
||||||
|
std::cout << "No config file creating one." << std::endl;
|
||||||
|
createConfig();
|
||||||
|
}
|
||||||
|
std::fstream file_in("config.json", std::fstream::in);
|
||||||
|
std::stringstream ss;
|
||||||
|
char data;
|
||||||
|
while(!file_in.eof()) {
|
||||||
|
file_in.get(data);
|
||||||
|
if(file_in.eof())
|
||||||
|
break;
|
||||||
|
ss << data;
|
||||||
|
}
|
||||||
|
json config = json::parse(ss.str());
|
||||||
|
if(config.find("log_file_max_size") != config.end() && config["log_file_max_size"].is_number_integer()) {
|
||||||
|
options.log_file_max_size = config["log_file_max_size"].get<long long int>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string strip(std::string& t) {
|
||||||
|
std::string g;
|
||||||
|
for(auto i: t) {
|
||||||
|
if(i == ' ' || i == '\t' || i == '\n' || i == '\v' || i == '\r')
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
g += i;
|
||||||
|
}
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
void printHelp() {
|
||||||
|
std::cout << "Usage: StartUpRunner [OPTIONS]" << std::endl;
|
||||||
|
std::cout << "OPTIONS:" << std::endl;
|
||||||
|
std::cout << "\t--help|-h : Prints this screen." << std::endl;
|
||||||
|
std::cout << "\t--version|-v : Prints version info." << std::endl;
|
||||||
|
std::cout << "\t--threads|-t [T] : Sets number of threads to [T]." << std::endl;
|
||||||
|
std::cout << "\t--log_dir|-ld [Dir] : Sets log directory to [Dir]." << std::endl;
|
||||||
|
std::cout << "\t--defaults|-d : Prints the default values used if no arguments are provided." << std::endl;
|
||||||
|
std::cout << "\t--json|-j [FileName]: Set Json Startup List to FileName." << std::endl;
|
||||||
|
std::cout << "For a detailed info about the json startup file use --help json." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void printVersion() {
|
||||||
|
std::cout << "StartUpRunner: Version-0.0.2 Pre-Alpha Pre-BigBang Era Edition." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void printDefaults() {
|
||||||
|
std::cout << "StartUpRunner Default Values: " << std::endl;
|
||||||
|
std::cout << "Json file : " << options.json_file_path << std::endl;
|
||||||
|
std::cout << "Logs directory : " << options.log_directory << std::endl;
|
||||||
|
std::cout << "Log file max size: " << options.threads << std::endl;
|
||||||
|
std::cout << "Threads : " << options.threads << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void printJsonHelp() {
|
||||||
|
std::cout << "Json StartUp List file format: " << std::endl;
|
||||||
|
std::cout << "Top level is an Array of Apps." << std::endl;
|
||||||
|
std::cout << "Each App Entry contains: " << std::endl;
|
||||||
|
std::cout << "\t[REQUIRED] name[string] : Name of the module." << std::endl;
|
||||||
|
std::cout << "\t[REQUIRED] app_name[string] : Path to the program with its name." << std::endl;
|
||||||
|
std::cout << "\t[OPTIONAL] app_args[list[string]] : List of arguments to the program." << std::endl;
|
||||||
|
std::cout << "\t[REQUIRED] run_mode[string] : Mode can be daemon or script." << std::endl;
|
||||||
|
std::cout << "\t\tMode daemon : The app forks the program and exits without waiting." << std::endl;
|
||||||
|
std::cout << "\t\tMode script : The app forks the program and waits for it to finish execution." << std::endl;
|
||||||
|
std::cout << "\t\tMode script : If no run_mode is given it is presumed to be script." << std::endl;
|
||||||
|
std::cout << "\t[OPTIONAL] after[{name,state}] : List containing names and state of modules." << std::endl;
|
||||||
|
std::cout << "\t\tname[string] : Name of the other module." << std::endl;
|
||||||
|
std::cout << "\t\tstate[string]: State of the other module." << std::endl;
|
||||||
|
std::cout << "\t\t\t\tState exited : Specified module must have finished execution." << std::endl;
|
||||||
|
std::cout << "\t\t\t\tState running : Specified module must be running." << std::endl;
|
||||||
|
std::cout << "\t\tThis module will be launched after the module found\n\t\t"
|
||||||
|
"in this list using name is at the state specified as state." << std::endl;
|
||||||
|
std::cout << "\t[OPTIONAL] env{} : Additional enviroment variables." << std::endl;
|
||||||
|
std::cout << "\t\tEvery key in this object will be used as environment name,\n\t\tand every corresponding value as its value." << std::endl;
|
||||||
|
std::cout << "\t[OPTIONAL] working_dir[string] : Working directory of the program." << std::endl;
|
||||||
|
std::cout << "\t[OPTIONAL] create_log[boolean] : Create a log file at logs path." << std::endl;
|
||||||
|
std::cout << std::endl << "Example startup_apps.json: ";
|
||||||
|
std::cout << R"(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "LS 0",
|
||||||
|
"app_name": "/bin/ls",
|
||||||
|
"app_args": ["-l"],
|
||||||
|
"run_mode": "script",
|
||||||
|
"create_log": true,
|
||||||
|
"working_dir": "/home/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LS 1",
|
||||||
|
"app_name": "/bin/ls",
|
||||||
|
"app_args": ["-l"],
|
||||||
|
"run_mode": "script",
|
||||||
|
"create_log": true,
|
||||||
|
"after": [{"module": "LS 0", "state": "exited"}],
|
||||||
|
"env": {"PATH": "/home/:/usr/bin/", "NAME": "ONE"}
|
||||||
|
"working_dir": "/home/etc"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int contains(std::vector<std::string> &list, std::string l, std::string v) {
|
||||||
|
for(int i = 0; i < list.size(); i++) {
|
||||||
|
if(list[i] == l || list[i] == v)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int toint(std::string s) {
|
||||||
|
std::stringstream ss;
|
||||||
|
int ret;
|
||||||
|
ss << s;
|
||||||
|
ss >> ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseArgs(int c, char *v[]) {
|
||||||
|
if(c == 1)
|
||||||
|
return;
|
||||||
|
if(c == 2) {
|
||||||
|
std::string arg1(v[1]);
|
||||||
|
if(arg1 == "--help" || arg1 == "-h") {
|
||||||
|
printHelp();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
if(arg1 == "--version" || arg1 == "-v") {
|
||||||
|
printVersion();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
if(arg1 == "--defaults" || arg1 == "-d") {
|
||||||
|
printDefaults();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(c >= 3) {
|
||||||
|
std::vector<std::string> arg_list;
|
||||||
|
for(int i=1; i<c; i++) {
|
||||||
|
arg_list.push_back(v[i]);
|
||||||
|
}
|
||||||
|
bool contains_any_known_arg = false;
|
||||||
|
int i = contains(arg_list, "--json", "-j");
|
||||||
|
if(i >= 0) {
|
||||||
|
int j = i + 1;
|
||||||
|
if(j > arg_list.size()) {
|
||||||
|
printHelp();
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
options.json_file_path = arg_list[j];
|
||||||
|
std::cout << "Json file path: " << options.json_file_path << std::endl;
|
||||||
|
contains_any_known_arg = true;
|
||||||
|
}
|
||||||
|
i = contains(arg_list, "--log_dir", "-ld");
|
||||||
|
if(i >= 0) {
|
||||||
|
int j = i + 1;
|
||||||
|
if(j > arg_list.size()) {
|
||||||
|
printHelp();
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
options.log_directory = arg_list[j];
|
||||||
|
std::cout << "Log directory: " << options.log_directory << std::endl;
|
||||||
|
contains_any_known_arg = true;
|
||||||
|
}
|
||||||
|
i = contains(arg_list, "--threads", "-t");
|
||||||
|
if(i >= 0) {
|
||||||
|
int j = i + 1;
|
||||||
|
if(j > arg_list.size()) {
|
||||||
|
printHelp();
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
options.threads = toint(arg_list[j]);
|
||||||
|
std::cout << "Threads: " << options.threads << std::endl;
|
||||||
|
contains_any_known_arg = true;
|
||||||
|
}
|
||||||
|
i = contains(arg_list, "--help", "-h");
|
||||||
|
if(i >= 0) {
|
||||||
|
int j = i + 1;
|
||||||
|
if(j > arg_list.size()) {
|
||||||
|
printHelp();
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
if(arg_list[j] == "json") {
|
||||||
|
printJsonHelp();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printHelp();
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
contains_any_known_arg = true;
|
||||||
|
}
|
||||||
|
if(!contains_any_known_arg) {
|
||||||
|
printHelp();
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string string_strip(std::string str) {
|
||||||
|
std::string ch;
|
||||||
|
bool front_found = false;
|
||||||
|
for(int i=0; i<str.size(); i++) {
|
||||||
|
if(!front_found) {
|
||||||
|
if(str[i] == ' ' || str[i] == '\t' || str[i] == '\n' || str[i] == '\v' || str[i] == '\r')
|
||||||
|
continue;
|
||||||
|
else {
|
||||||
|
front_found = true;
|
||||||
|
ch += str[i];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ch += str[i];
|
||||||
|
}
|
||||||
|
auto pos = ch.find_last_not_of(" \t\n\v\r");
|
||||||
|
if(pos == std::string::npos)
|
||||||
|
return ch;
|
||||||
|
ch.erase(pos+1);
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> string_split(std::string s, const std::string& delimiter) {
|
||||||
|
std::vector<std::string> tokens;
|
||||||
|
size_t pos = 0;
|
||||||
|
std::string token;
|
||||||
|
while ((pos = s.find(delimiter)) != std::string::npos) {
|
||||||
|
token = s.substr(0, pos);
|
||||||
|
tokens.push_back(token);
|
||||||
|
s.erase(0, pos + delimiter.length());
|
||||||
|
}
|
||||||
|
tokens.push_back(string_strip(s));
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string tolower(std::string str) {
|
||||||
|
std::string s;
|
||||||
|
std::transform(str.begin(), str.end(), std::back_inserter(s), [](char c){
|
||||||
|
return std::tolower(c);
|
||||||
|
});
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void printListModules(const std::vector<std::shared_ptr<AppInfo>>& item_list, std::ostream &out) {
|
||||||
|
std::stringstream ss;
|
||||||
|
int s = item_list.size();
|
||||||
|
ss << "List Contains --> ";
|
||||||
|
for(auto &i: item_list) {
|
||||||
|
s--;
|
||||||
|
if(s > 0)
|
||||||
|
ss << i->getModuleName() << ", ";
|
||||||
|
else
|
||||||
|
ss << i->getModuleName();
|
||||||
|
}
|
||||||
|
out << ss.str() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool listContainsAfter(const std::vector<std::shared_ptr<AppInfo>> &list, const AppInfo::AppData &item) {
|
||||||
|
std::map<std::string, bool> contains_list;
|
||||||
|
std::string it = item.getAfterKeys();
|
||||||
|
auto string_list = string_split(it, ",");
|
||||||
|
for(auto &i: string_list) {
|
||||||
|
bool found = false;
|
||||||
|
for(auto &j: list) {
|
||||||
|
if(tolower(string_strip(j->getModuleName())) == tolower(string_strip(i))) {
|
||||||
|
contains_list[i] = true;
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!found)
|
||||||
|
contains_list[i] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool value = std::all_of(contains_list.begin(), contains_list.end(),
|
||||||
|
[](const auto &it) {
|
||||||
|
return it.second;
|
||||||
|
});
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<int> checkAppList(std::deque<AppInfo::AppData> ©_list, std::vector<std::shared_ptr<AppInfo>> &after_list, std::ostream &out) {
|
||||||
|
out << "Checking app list with: " << copy_list.size() << " apps for errors..." << std::endl;
|
||||||
|
int reshuffle_count = 0;
|
||||||
|
while(copy_list.size() != 0) {
|
||||||
|
size_t copy_list_size = copy_list.size();
|
||||||
|
for(auto item = copy_list.begin(); item != copy_list.end();) {
|
||||||
|
if(item->after.size() == 0) {
|
||||||
|
std::shared_ptr<AppInfo> app_info = std::make_shared<AppInfo>();
|
||||||
|
app_info->copyFromData(*item);
|
||||||
|
after_list.push_back(std::move(app_info));
|
||||||
|
out << "Module has no dependency: " << item->module_name << std::endl;
|
||||||
|
reshuffle_count = 0;
|
||||||
|
item = copy_list.erase(item);
|
||||||
|
}
|
||||||
|
else if(listContainsAfter(after_list, *item)) {
|
||||||
|
out << "Module: " << item->module_name <<
|
||||||
|
" has dependency: " << item->getAfterString() << " and it was found." << std::endl;
|
||||||
|
|
||||||
|
std::shared_ptr<AppInfo> app_info = std::make_shared<AppInfo>();
|
||||||
|
app_info->copyFromData(*item);
|
||||||
|
|
||||||
|
for(auto &[key, value]: item->after) {
|
||||||
|
bool found = false;
|
||||||
|
for(int i=0;i<after_list.size();i++) {
|
||||||
|
if(tolower(string_strip(after_list[i]->getModuleName())) == tolower(string_strip(key))) {
|
||||||
|
if(value == "running") {
|
||||||
|
app_info->insertParent(after_list[i], AppInfo::State::running);
|
||||||
|
}
|
||||||
|
else if(value == "exited") {
|
||||||
|
app_info->insertParent(after_list[i], AppInfo::State::exited);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
out << "Unknown state for: " << item->module_name <<
|
||||||
|
"'s dependency of: " << after_list[i]->getModuleName() << std::endl;
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!found) {
|
||||||
|
out << "Depenedency not found" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
after_list.push_back(std::move(app_info));
|
||||||
|
reshuffle_count = 0;
|
||||||
|
item = copy_list.erase(item);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
item++;
|
||||||
|
}
|
||||||
|
if(copy_list.size() == 0 || item == copy_list.end())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(copy_list.size() == copy_list_size) {
|
||||||
|
reshuffle_count++;
|
||||||
|
if(reshuffle_count >= 3) {
|
||||||
|
out << "List contains cyclic dependency or unresolvable dependency..." << std::endl;
|
||||||
|
out << "Affected apps in list: " << std::endl;
|
||||||
|
for(auto &i: copy_list) {
|
||||||
|
i.printInfo(out);
|
||||||
|
}
|
||||||
|
std::cout << "Resolved list order: " << std::endl;
|
||||||
|
printListModules(after_list, out);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(auto &i: after_list) {
|
||||||
|
for(auto &[j, state]: i->getParents()) {
|
||||||
|
if(state == AppInfo::State::exited && j->getRunMode() == AppInfo::RunMode::daemon) {
|
||||||
|
out << "[WARN]: " << i->getModuleName() <<
|
||||||
|
" is waiting for a daemon: " << j->getModuleName() << " to start exeuction." <<
|
||||||
|
std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
QCoreApplication app(argc, argv);
|
parseConfig();
|
||||||
|
parseArgs(argc, argv);
|
||||||
if(!std::filesystem::exists("/home/ud/logs/")) {
|
if(!std::filesystem::exists(options.log_directory)) {
|
||||||
std::filesystem::create_directory("/home/ud/logs");
|
std::filesystem::create_directory(options.log_directory);
|
||||||
}
|
|
||||||
std::fstream log_file("/home/ud/logs/StartUpRunner.log", std::fstream::app);
|
|
||||||
if(!log_file.is_open()) {
|
|
||||||
log_file.open("/home/ud/logs/StartUpRunner.log", std::fstream::out);
|
|
||||||
}
|
|
||||||
log_file << "-------- Starting app: " << get_current_time();
|
|
||||||
log_file << "Current path: " << std::filesystem::current_path() << "\n";
|
|
||||||
if(!std::filesystem::exists("startup_apps.json")) {
|
|
||||||
log_file << "startup_apps.json not found exiting.\n";
|
|
||||||
log_file.flush();
|
|
||||||
log_file.close();
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
std::string data = "";
|
|
||||||
std::string t_data = "";
|
|
||||||
std::fstream json_file;
|
|
||||||
json_file.open("startup_apps.json", std::fstream::in);
|
|
||||||
if(!json_file.is_open()) {
|
|
||||||
std::cout << "Why is the startup_apps.json file not open?" << std::endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
std::stringstream test;
|
|
||||||
test << json_file.rdbuf();
|
|
||||||
json_file.close();
|
|
||||||
QByteArray arr_data = QByteArray::fromStdString(test.str());
|
|
||||||
QJsonArray top = QJsonDocument::fromJson(arr_data).array();
|
|
||||||
std::vector<QProcess *> active_process_list;
|
|
||||||
for(auto i: top) {
|
|
||||||
auto object = i.toObject();
|
|
||||||
std::string name = object["app_name"].toString().toStdString();
|
|
||||||
std::string args = "";
|
|
||||||
QStringList sargs;
|
|
||||||
for(auto k: object["app_args"].toArray()) {
|
|
||||||
args += k.toString().toStdString();
|
|
||||||
sargs << k.toString();
|
|
||||||
}
|
}
|
||||||
QString mode = object["mode"].toString("wait");
|
std::filesystem::path json_path = options.json_file_path;
|
||||||
QString working_dir = object["working_dir"].toString("/home/ud/StartUpApps/");
|
std::string log_file_path = std::filesystem::path(options.log_directory) / "StartUpRunner.log";
|
||||||
std::string file_name = std::filesystem::path(name).stem().string();
|
Log log_file(log_file_path, options.log_file_max_size);
|
||||||
std::string log_file_name = std::format("/home/ud/logs/{}.log", file_name);
|
std::cout << "Starter Init: Log file path: " << log_file_path << std::endl;
|
||||||
bool create_log_file = object["create_log"].toBool();
|
std::cout << "Log file open: " << log_file.isOpen() << std::endl;
|
||||||
std::string command;
|
log_file << "-------- Starting app --------" << std::endl;
|
||||||
if(create_log_file)
|
log_file << "Current Time: " << get_current_time();
|
||||||
command = std::format("{} {} > {}", name, args, log_file_name);
|
log_file << "Log file max size: " << options.log_file_max_size << std::endl;
|
||||||
else
|
log_file << "Current path: " << std::filesystem::current_path() << std::endl;
|
||||||
command = std::format("{} {}", name, args);
|
std::string json_file_path = json_path;
|
||||||
log_file << "Running: " << command << " with working dir: " << working_dir.toStdString() << "\n";
|
if(std::filesystem::is_directory(json_path)) {
|
||||||
QProcess *proc = new QProcess();
|
json_file_path = json_path / "startup_apps.json";
|
||||||
QObject::connect(proc, &QProcess::started, [&, command, proc, working_dir] () {
|
|
||||||
log_file << "Started process: " << command << " with pid: " << QString::number(proc->processId()).toStdString() << " with working directory as: " << working_dir.toStdString() << std::endl;
|
|
||||||
});
|
|
||||||
if (mode == "wait") {
|
|
||||||
|
|
||||||
QObject::connect(proc, &QProcess::finished, [&, command, proc] (int exitcode, QProcess::ExitStatus status) {
|
|
||||||
auto process = proc;
|
|
||||||
if(status == QProcess::NormalExit)
|
|
||||||
log_file << "Process: " << command << " with pid: " << QString::number(proc->processId()).toStdString() << std::endl << " exited with exit code: " << exitcode << std::endl;
|
|
||||||
else
|
|
||||||
log_file << "Process: " << command << " with pid: " << QString::number(proc->processId()).toStdString() << std::endl << " CRASHED with exit code: " << exitcode << std::endl;
|
|
||||||
auto it = std::find(active_process_list.begin(), active_process_list.end(), process);
|
|
||||||
active_process_list.erase(it);
|
|
||||||
if(active_process_list.size() == 0) {
|
|
||||||
log_file << "-------- All processes finished." << std::endl;
|
|
||||||
log_file.flush();
|
|
||||||
log_file.close();
|
|
||||||
app.exit(0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
active_process_list.push_back(proc);
|
|
||||||
}
|
}
|
||||||
if(!working_dir.isEmpty())
|
else if(json_path == "") {
|
||||||
proc->setWorkingDirectory(working_dir);
|
json_file_path = "startup_apps.json";
|
||||||
proc->setStandardOutputFile(QString::fromStdString(log_file_name));
|
}
|
||||||
proc->setProgram(QString::fromStdString(name));
|
if(!std::filesystem::exists(json_file_path)) {
|
||||||
proc->setArguments(sargs);
|
log_file << std::vformat("startup_apps.json not found exiting at: {}.\n", std::make_format_args(json_file_path));
|
||||||
if(mode == "wait")
|
exit(-1);
|
||||||
proc->start(QString::fromStdString(name), sargs);
|
}
|
||||||
else
|
else {
|
||||||
proc->startDetached(QString::fromStdString(name), sargs);
|
log_file << "startup_apps.json loaded from: " << json_file_path << std::endl;
|
||||||
}
|
}
|
||||||
return app.exec();
|
std::string data = "";
|
||||||
|
std::string t_data = "";
|
||||||
|
std::fstream json_file;
|
||||||
|
json_file.open(json_file_path, std::fstream::in);
|
||||||
|
if(!json_file.is_open()) {
|
||||||
|
log_file << "Why is the startup_apps.json file not open?" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
std::stringstream test;
|
||||||
|
test << json_file.rdbuf();
|
||||||
|
json_file.close();
|
||||||
|
using json = nlohmann::json;
|
||||||
|
bool valid = json::accept(test.str());
|
||||||
|
if(!valid) {
|
||||||
|
log_file << "Json Document Parsing error...." << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
json file = json::parse(test.str());
|
||||||
|
std::deque<AppInfo::AppData> initial_app_list;
|
||||||
|
for(auto i = file.begin(); i != file.end(); i++) {
|
||||||
|
AppInfo::AppData app_info;
|
||||||
|
auto object = i->get<json>();
|
||||||
|
if(object.find("app_name") == object.end()) {
|
||||||
|
log_file << "Error in json." << std::endl;
|
||||||
|
log_file << "Field app name is a required field." << std::endl;
|
||||||
|
exit(-2);
|
||||||
|
}
|
||||||
|
if(object.find("name") == object.end()) {
|
||||||
|
log_file << "Error in json." << std::endl;
|
||||||
|
log_file << "Field name is a required field." << std::endl;
|
||||||
|
exit(-2);
|
||||||
|
}
|
||||||
|
if(object.find("run_mode") == object.end()) {
|
||||||
|
log_file << "Error in json." << std::endl;
|
||||||
|
log_file << "Field run_mode is a required field." << std::endl;
|
||||||
|
exit(-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
app_info.program_name = object["app_name"].get<std::string>();
|
||||||
|
std::string args = "";
|
||||||
|
std::deque<const char*> c_args;
|
||||||
|
std::deque<std::string> arg_list;
|
||||||
|
if(object.find("app_args") != object.end()) {
|
||||||
|
for(const auto &k: object["app_args"].get<json>()) {
|
||||||
|
std::string str = k.get<std::string>();
|
||||||
|
args += str + " ";
|
||||||
|
arg_list.push_back(str);
|
||||||
|
c_args.push_back(str.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::unordered_map<std::string, std::string> after_list;
|
||||||
|
std::unordered_map<std::string, std::string> env_list;
|
||||||
|
if(object.find("after") != object.end()) {
|
||||||
|
for(const auto &k: object["after"].get<json>()) {
|
||||||
|
auto l = k.get<json>();
|
||||||
|
auto key = l["module"].get<std::string>();
|
||||||
|
auto value = l["state"].get<std::string>();
|
||||||
|
after_list[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(object.find("env") != object.end()) {
|
||||||
|
json k = object["env"].get<json>();
|
||||||
|
for (auto &it: k.items()) {
|
||||||
|
env_list[it.key()] = it.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
app_info.args = arg_list;
|
||||||
|
if(object.find("working_dir") != object.end())
|
||||||
|
app_info.working_dir = object["working_dir"].get<std::string>();
|
||||||
|
if(object.find("create_log") != object.end())
|
||||||
|
app_info.create_log = object["create_log"].get<bool>();
|
||||||
|
app_info.env = env_list;
|
||||||
|
app_info.after = after_list;
|
||||||
|
app_info.module_name = object["name"].get<std::string>();
|
||||||
|
app_info.run_mode = object["run_mode"].get<std::string>();
|
||||||
|
app_info.printInfo(log_file.rdbuf());
|
||||||
|
initial_app_list.push_back(app_info);
|
||||||
|
}
|
||||||
|
std::vector<std::shared_ptr<AppInfo>> list;
|
||||||
|
auto list_check = checkAppList(initial_app_list, list, log_file.rdbuf());
|
||||||
|
if(list_check.has_value()) {
|
||||||
|
log_file << "No errors found in app list." << std::endl;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log_file << "Errors present in app list; Please fix them" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
for(int i=0;i<list.size();i++) {
|
||||||
|
list[i]->printInfo(log_file.rdbuf());
|
||||||
|
}
|
||||||
|
log_file << "Order of Execution -> " << std::endl;
|
||||||
|
printListModules(list, log_file.rdbuf());
|
||||||
|
ExecutionManager manager{options.threads};
|
||||||
|
manager.setLogFile(log_file);
|
||||||
|
manager.setLogFileMaxSize(options.log_file_max_size);
|
||||||
|
manager.setLogDirectoy(options.log_directory);
|
||||||
|
manager.setJobs(list);
|
||||||
|
manager.run();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
[
|
|
||||||
{
|
|
||||||
"app_name": "/home/ud/StartUpApps/piFinder",
|
|
||||||
"app_args": [],
|
|
||||||
"mode": "no-wait"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"app_name": "/home/ud/git_external/acer-predator-turbo-and-rgb-keyboard-linux-module/install.sh",
|
|
||||||
"working_dir": "/home/ud/git_external/acer-predator-turbo-and-rgb-keyboard-linux-module/"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
4
startup_service.dinit
Normal file
4
startup_service.dinit
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
type = scripted
|
||||||
|
command = /path_to_executable/StartUpRunner --log_dir /var/logs/StartUpRunner/ --json /path_to_startup_json/startup_apps.json
|
||||||
|
logfile = /var/log/dinit/StartUpRunner.log
|
||||||
|
after = network.target
|
||||||
12
startup_service.systemd
Normal file
12
startup_service.systemd
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
[Unit]
|
||||||
|
Description=A Simple Startup Runner Service
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=forking
|
||||||
|
ExecStart=/path_to_executable/StartUpRunner
|
||||||
|
KillMode=process
|
||||||
|
WorkingDirectory=/path_to_executable/StartUpApps/
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
Loading…
Reference in a new issue