add config options: db_startup_connection_max_attempts - default 0 (infinite) db_startup_connection_attempt_delay - delay in seconds between attempts (default 5) BREAKING CHANGE: WINIX_PLUGIN_INIT plugin message requires to set result status, you have to set the result status to true (env.res) if your plugin was initialized correctly, otherwise winix will not start
385 lines
8.9 KiB
C++
385 lines
8.9 KiB
C++
/*
|
|
* This file is a part of Winix
|
|
* and is distributed under the 2-Clause BSD licence.
|
|
* Author: Tomasz Sowa <t.sowa@ttmath.org>
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2008-2022, Tomasz Sowa
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*/
|
|
|
|
#include <cstdlib>
|
|
#include <ctime>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <sys/param.h>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include "core/app.h"
|
|
#include "core/version.h"
|
|
#include "utf8/utf8.h"
|
|
#include "mainoptions/mainoptionsparser.h"
|
|
|
|
|
|
|
|
|
|
namespace Winix
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
application object
|
|
*/
|
|
App app;
|
|
|
|
|
|
|
|
void print_syntax()
|
|
{
|
|
std::cout << "Winix version " << WINIX_VER_MAJOR << '.' << WINIX_VER_MINOR << '.' << WINIX_VER_REVISION << std::endl;
|
|
std::cout << "Copyright (c) 2008-2022, Tomasz Sowa" << std::endl;
|
|
|
|
std::cout << "Usage: winix [options]" << std::endl;
|
|
std::cout << " -c : a path to the config file" << std::endl;
|
|
std::cout << " --config : a path to the config file" << std::endl;
|
|
std::cout << " --use-env : allow to use environment variables" << std::endl;
|
|
std::cout << " --dump-config : dump all read config options to stdout and exit" << std::endl;
|
|
std::cout << " -h : print usage information" << std::endl;
|
|
std::cout << " --help : print usage information" << std::endl;
|
|
std::cout << " -? : print usage information" << std::endl;
|
|
std::cout << std::endl;
|
|
std::cout << "At least one -c or --config parameter is required, use -c or --config option" << std::endl;
|
|
std::cout << "multiple times to load more than one config." << std::endl;
|
|
std::cout << std::endl;
|
|
std::cout << "Environment variables must be prefixed with winix_ to be loaded by winix." << std::endl;
|
|
std::cout << "The winix_ prefix is then removed and the key value converted to lowercase." << std::endl;
|
|
std::cout << "Sample:" << std::endl;
|
|
std::cout << "evn WINIX_MY_OPTION=TEST123test winix -c config_file" << std::endl;
|
|
std::cout << "This will add my_option to the config with value TEST123test." << std::endl;
|
|
std::cout << "Environment variables are read last so they will overwrite the values" << std::endl;
|
|
std::cout << "from the configuration files." << std::endl;
|
|
}
|
|
|
|
|
|
|
|
void CreateNewDescriptor(int des_dst, int flags)
|
|
{
|
|
int descriptor;
|
|
|
|
descriptor = open("/dev/null", flags | O_NOCTTY);
|
|
|
|
if( descriptor != -1 )
|
|
{
|
|
dup2(descriptor, des_dst);
|
|
|
|
if( descriptor != des_dst )
|
|
close(descriptor);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void CloseDescriptors()
|
|
{
|
|
close(0);
|
|
close(1);
|
|
close(2);
|
|
app.stdout_is_closed = true;
|
|
|
|
CreateNewDescriptor(0, O_RDONLY);
|
|
CreateNewDescriptor(1, O_WRONLY);
|
|
CreateNewDescriptor(2, O_WRONLY);
|
|
}
|
|
|
|
|
|
void LogInfo(Log & log, LogManipulators log_level, const char * msg, bool put_version, const char * msg2)
|
|
{
|
|
log << log_level;
|
|
log.PrintDate(pt::Date(std::time(0)));
|
|
log << ' ' << msg;
|
|
|
|
if( put_version )
|
|
{
|
|
log << ' ' << WINIX_VER_MAJOR
|
|
<< '.' << WINIX_VER_MINOR
|
|
<< '.' << WINIX_VER_REVISION;
|
|
}
|
|
|
|
log << ' ' << msg2 << logend;
|
|
}
|
|
|
|
|
|
void SavePidFile(Log & log)
|
|
{
|
|
if( !app.config.pid_file.empty() )
|
|
{
|
|
std::string file_name;
|
|
pt::wide_to_utf8(app.config.pid_file, file_name);
|
|
std::ofstream file(file_name);
|
|
|
|
if( !file )
|
|
{
|
|
log << log1 << "I cannot save the pid to a file: " << app.config.pid_file << logend;
|
|
}
|
|
else
|
|
{
|
|
file << getpid() << "\n";
|
|
file.close();
|
|
log << log3 << "Process pid saved to: " << app.config.pid_file << logend;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void RemovePidFile()
|
|
{
|
|
if( !app.config.pid_file.empty() )
|
|
{
|
|
std::string file_name;
|
|
pt::wide_to_utf8(app.config.pid_file, file_name);
|
|
unlink(file_name.c_str());
|
|
}
|
|
}
|
|
|
|
|
|
bool ReadConfigs(const pt::Space::TableType & table, size_t & config_read)
|
|
{
|
|
bool status = true;
|
|
|
|
for(const pt::Space * config : table)
|
|
{
|
|
if( config->is_table() && config->table_size() == 1 )
|
|
{
|
|
std::wstring config_file = config->value.value_table[0]->to_wstr();
|
|
config_read += 1;
|
|
|
|
if( !app.config.ReadConfig(config_file) )
|
|
{
|
|
status = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
void ReadEnvOption(const char * option)
|
|
{
|
|
const char winix_prefix[] = "winix_";
|
|
size_t prefix_len = sizeof(winix_prefix) - 1; // null terminating table
|
|
std::wstring opt, val;
|
|
|
|
if( pt::is_substr_nc(winix_prefix, option) )
|
|
{
|
|
pt::utf8_to_wide(option + prefix_len, opt);
|
|
|
|
std::wstring::size_type sep = opt.find('=');
|
|
|
|
if( sep != std::wstring::npos )
|
|
{
|
|
val = opt.substr(sep + 1);
|
|
opt.erase(sep);
|
|
}
|
|
|
|
TemplatesFunctions::locale.ToSmall(opt);
|
|
app.config.space.add(opt, val);
|
|
}
|
|
}
|
|
|
|
|
|
void ReadConfigFromEnv(const char ** env)
|
|
{
|
|
for(size_t i = 0 ; env[i] ; ++i)
|
|
{
|
|
ReadEnvOption(env[i]);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ReadConfigs(const pt::Space & options, const char ** env)
|
|
{
|
|
size_t config_read = 0;
|
|
bool status = true;
|
|
const pt::Space::TableType * table = options.get_table(L"c");
|
|
app.config.space.clear();
|
|
|
|
if( table )
|
|
{
|
|
status = status && ReadConfigs(*table, config_read);
|
|
}
|
|
|
|
table = options.get_table(L"config");
|
|
|
|
if( table )
|
|
{
|
|
status = status && ReadConfigs(*table, config_read);
|
|
}
|
|
|
|
if( config_read == 0 )
|
|
{
|
|
std::cout << "You have to provide a config file with c parameter" << std::endl;
|
|
Winix::print_syntax();
|
|
return false;
|
|
}
|
|
|
|
if( options.has_key(L"use-env") )
|
|
{
|
|
ReadConfigFromEnv(env);
|
|
}
|
|
|
|
app.config.AssignValuesFromSpace();
|
|
|
|
return status;
|
|
}
|
|
|
|
} // namespace Winix
|
|
|
|
|
|
|
|
int main(int argc, const char ** argv, const char ** env)
|
|
{
|
|
using Winix::app;
|
|
|
|
std::srand(std::time(0));
|
|
app.system.system_start = time(0);
|
|
|
|
pt::Space options;
|
|
pt::Space arguments;
|
|
pt::MainOptionsParser options_parser;
|
|
|
|
arguments.add(L"c", 1);
|
|
arguments.add(L"config", 1);
|
|
|
|
pt::MainOptionsParser::Status status = options_parser.parse(argc, argv, options, arguments);
|
|
|
|
if( status != pt::MainOptionsParser::status_ok )
|
|
{
|
|
Winix::print_syntax();
|
|
return 6;
|
|
}
|
|
|
|
if( options.has_key(L"h") || options.has_key(L"help") || options.has_key(L"?") )
|
|
{
|
|
Winix::print_syntax();
|
|
return 0;
|
|
}
|
|
|
|
Winix::Log & log = app.GetMainLog();
|
|
pt::WTextStream * log_buffer = log.get_log_buffer();
|
|
Winix::LogInfo(log, Winix::log3, "UTC booting Winix", true, ""); // date will be printed as UTC because the time zones are not loaded yet
|
|
|
|
if( !Winix::ReadConfigs(options, env) )
|
|
{
|
|
// we need to print the buffer by hand because the logger
|
|
// is not fully initialized yet
|
|
if( log_buffer && !log_buffer->empty() )
|
|
{
|
|
pt::wide_stream_to_utf8(*log_buffer, std::cout);
|
|
}
|
|
|
|
return 2;
|
|
}
|
|
|
|
if( options.has_key(L"dump-config") )
|
|
{
|
|
if( log_buffer )
|
|
{
|
|
log << "all read config options (some of the values could have been modified by winix"
|
|
<< " but the modified values are not printed here):" << Winix::logend;
|
|
app.config.space.serialize_to_space_stream(*log_buffer, true);
|
|
log << Winix::logend;
|
|
pt::wide_stream_to_utf8(*log_buffer, std::cout);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
app.InitLoggers();
|
|
|
|
if( app.stdout_is_closed || app.config.demonize )
|
|
app.config.log_stdout = false;
|
|
|
|
if( !app.config.log_stdout )
|
|
Winix::CloseDescriptors();
|
|
|
|
if( app.config.demonize && !app.Demonize() )
|
|
{
|
|
log << Winix::logsave;
|
|
return 4;
|
|
}
|
|
|
|
if( !app.InitFCGI() )
|
|
{
|
|
log << Winix::logsave;
|
|
return 5;
|
|
}
|
|
|
|
if( !app.DropPrivileges() )
|
|
{
|
|
log << Winix::logsave;
|
|
return 3;
|
|
}
|
|
|
|
app.LogUserGroups();
|
|
Winix::SavePidFile(log);
|
|
|
|
// app.Init() starts other threads as well (they will be waiting on the lock)
|
|
if( !app.Init() )
|
|
{
|
|
Winix::RemovePidFile();
|
|
log << Winix::logsave;
|
|
return 1;
|
|
}
|
|
|
|
app.StartThreads();
|
|
// now we have more threads, we should use Lock() and Unlock()
|
|
|
|
// saving all starting logs
|
|
app.Lock();
|
|
Winix::LogInfo(log, Winix::log1, "Winix", true, "started");
|
|
log << Winix::logsave;
|
|
app.Unlock();
|
|
|
|
// main loop
|
|
app.Start();
|
|
|
|
app.Close();
|
|
|
|
Winix::LogInfo(log, Winix::log1, "Winix", true, "stopped");
|
|
Winix::RemovePidFile();
|
|
log << Winix::logsave;
|
|
|
|
return 0;
|
|
}
|
|
|