/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
ExecStart
faz 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=SIGKILL
está equivocado. A finalização normal deve ser graciosa, viaSIGTERM
, eSIGKILL
deve 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
logrotate
ounewsyslog
neste 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