/run/user/1000 , que obviamente não existe até que o usuário # 1000 efetue login ou inicie explicitamente o xyr por gerenciamento de serviço por usuário, é um arenque vermelho. Todo o mecanismo que o usa não deveria estar lá.
O bug # 215 deste programa é executado muito mais profundamente do que você imagina. Este arquivo de unidade de serviço está muito errado, como é a operação do próprio programa . Há muita programação de cultos de carga, baseada na não compreensão dos fundamentos das unidades de serviço do systemd.
-
Unidades de serviço não são shell script. O manual do systemd faz explicar isso. A configuração
ExecStartfaz com que o programa de serviço seja executado com alguns argumentos extras,2>&1>e/dev/null. - O gerente de serviços já garante que apenas um serviço seja executado. Todo esse código adicionado aqui é lixo desnecessário.
- O mecanismo de arquivos PID frágil e perigoso deve não ser usado. Não tem lugar no gerenciamento de serviço adequado.
-
O gerenciador de serviço também lida com a chamada do serviço em um contexto de daemon. Muitos dos outros códigos em
main()são também lixo desnecessário, baseado na falácia da demonização.- O programa não deve ser
fork()ing, e o mecanismo de prontidão do serviço não deve ser especificado comoType=forking. Como muitos programas no mundo real, este programa não está falando o protocolo de prontidão de bifurcação em primeiro lugar. - O programa já está em execução como superusuário.
User=rooté desnecessário e, de fato, o serviço deve ser reprojetado para que não precise ser executado com privilégios de superusuário, mas executado sob a égide de uma conta de serviço dedicada sem privilégios. - O gerenciador de serviços já está registrando a saída e o erro padrão, e fazendo um trabalho melhor do que este programa. Este sistema de registro caseiro apenas desenvolve um arquivo de log até que ele preencha todo um sistema de arquivos, consumindo todo o espaço de emergência reservado para o superusuário.
- Seu log é simplesmente o erro padrão, acessível a partir do C ++ como
std::clog. - Na verdade, todo o código do
fork()para o redirecionamento do padrão erro não deve ser usado. O gerenciamento de serviços lida com todos , desde a liderança da sessão até o diretório de trabalho e umask até a E / S padrão, e faz isso corretamente. Este programa não, e não deve estar tentando fazer qualquer deste para si quando usado sob um gerenciador de serviços.Tudo o que você tirou do Boost estava errado.
- O programa não deve ser
-
Três unidades de serviço são despesas desnecessárias de manutenção. Elas diferem apenas nas configurações de
After, e elas podem ser mescladas em uma só. -
Terminação sem graça não é sucesso. Dado que já havia um problema com a limpeza de arquivos após o término,
SuccessExitStatus=SIGKILLestá equivocado. A finalização normal deve ser graciosa, viaSIGTERM, eSIGKILLdeve ser considerado anormal. (Obviamente, todo o mecanismo de arquivosoutputé um mecanismo de registro caseiro desenvolvido de forma incorreta que não deve ser usado no gerenciamento de serviços, conforme já explicado.) Esse é o padrão systemd. -
Destrutores dos objetos de banco de dados e outras coisas devem ser executados. Não deixe
main()comexit().
Um programa do daemon implementado adequadamente para rodar sob um gerenciador de serviço, seja daemontools, runit, s6, nosh, systemd ou qualquer outra coisa, é muito mais curto:
… // the same until this point
void pvo_upload(void)
{
std::clog << "Starting Daemon..." << std::endl;
CommonServiceCode();
std::clog << "Stopping Daemon..." << std::endl;
}
int main(int argc, char *argv[])
{
int c;
const char *config_file = "";
/* parse commandline */
while(1)
{
static struct option long_options[] =
{
{ "config-file", required_argument, 0, 'c' },
{ 0, 0, 0, 0 }
};
int option_index = 0;
c = getopt_long (argc, argv, "c:", long_options, &option_index);
if (c == -1) break;
switch (c)
{
case 'c':
config_file = optarg;
break;
default:
return EXIT_FAILURE;
break;
}
}
if (cfg.readSettings(argv[0], config_file) != Configuration::CFG_OK)
return EXIT_FAILURE;
std::clog << "Starting SBFspotUploadDaemon Version " << VERSION << std::endl;
// Check if DB is accessible
db_SQL_Base db = db_SQL_Base();
db.open(cfg.getSqlHostname(), cfg.getSqlUsername(), cfg.getSqlPassword(), cfg.getSqlDatabase());
if (!db.isopen())
{
std::clog << "Unable to open database. Check configuration." << std::endl;
return EXIT_FAILURE;
}
// Check DB Version
int schema_version = 0;
db.get_config(SQL_SCHEMAVERSION, schema_version);
db.close();
if (schema_version < SQL_MINIMUM_SCHEMA_VERSION)
{
std::clog << "Upgrade your database to version " << SQL_MINIMUM_SCHEMA_VERSION << std::endl;
return EXIT_FAILURE;
}
// Install our signal handler.
// This responds to the service manager signalling the service to stop.
signal(SIGTERM, handler);
// Start daemon loop
pvo_upload();
return EXIT_SUCCESS;
}
E a unidade de serviço também é mais curta:
[Unit] Description=SBFspot upload daemon After=mysql.service mariadb.service network.target [Service] Type=simple TimeoutStopSec=10 ExecStart=/usr/local/bin/sbfspot.3/SBFspotUploadDaemon Restart=on-success [Install] WantedBy=multi-user.target
A saída do log é visível com systemctl status e journalctl (com a opção -u e o nome do serviço, se desejado).
Leitura adicional
- Jonathan de Boyne Pollard (2016). Não use
logrotateounewsyslogneste século. . Respostas frequentemente dadas. - Jonathan de Boyne Pollard (2001). Erros a evitar ao projetar programas do programa Unix . Respostas frequentemente dadas.
- Jonathan de Boyne Pollard (2015). Problemas de protocolo de preparação com o Unix dæmons . Respostas frequentemente dadas.
- link
- link