/run/user/$UID not mounted when daemon starts
Clash Royale CLAN TAG#URR8PPP
up vote
1
down vote
favorite
When I reboot my Raspberry Pi (Stretch) a daemon fails to start because /run/user/1000
does not exist. This is my unit file:
[Unit]
Description=SBFspot Upload Daemon
[Service]
User=pi
Type=forking
TimeoutStopSec=60
ExecStart=/usr/local/bin/sbfspot.3/SBFspotUploadDaemon -p /run/user/1000/sbfspotupload.pid 2>&1> /dev/null
PIDFile=/run/user/1000/sbfspotupload.pid
Restart=no
RestartSec=15
SuccessExitStatus=SIGKILL
[Install]
WantedBy=default.target
When I configure to Restart=on-failure
all goes well after a few retries, but that's not really what I want.
I want the daemon to wait for /run/user/1000
gets mounted. I tried with After=run-user-1000.mount
but it still fails.
Is this possible or do I have to stick with the Restart=on-failure
?
systemd systemd-unit
add a comment |Â
up vote
1
down vote
favorite
When I reboot my Raspberry Pi (Stretch) a daemon fails to start because /run/user/1000
does not exist. This is my unit file:
[Unit]
Description=SBFspot Upload Daemon
[Service]
User=pi
Type=forking
TimeoutStopSec=60
ExecStart=/usr/local/bin/sbfspot.3/SBFspotUploadDaemon -p /run/user/1000/sbfspotupload.pid 2>&1> /dev/null
PIDFile=/run/user/1000/sbfspotupload.pid
Restart=no
RestartSec=15
SuccessExitStatus=SIGKILL
[Install]
WantedBy=default.target
When I configure to Restart=on-failure
all goes well after a few retries, but that's not really what I want.
I want the daemon to wait for /run/user/1000
gets mounted. I tried with After=run-user-1000.mount
but it still fails.
Is this possible or do I have to stick with the Restart=on-failure
?
systemd systemd-unit
User=pi
makes it run as user pi, no? $UID of pi is 1000
â SBF
Jun 26 at 10:25
add a comment |Â
up vote
1
down vote
favorite
up vote
1
down vote
favorite
When I reboot my Raspberry Pi (Stretch) a daemon fails to start because /run/user/1000
does not exist. This is my unit file:
[Unit]
Description=SBFspot Upload Daemon
[Service]
User=pi
Type=forking
TimeoutStopSec=60
ExecStart=/usr/local/bin/sbfspot.3/SBFspotUploadDaemon -p /run/user/1000/sbfspotupload.pid 2>&1> /dev/null
PIDFile=/run/user/1000/sbfspotupload.pid
Restart=no
RestartSec=15
SuccessExitStatus=SIGKILL
[Install]
WantedBy=default.target
When I configure to Restart=on-failure
all goes well after a few retries, but that's not really what I want.
I want the daemon to wait for /run/user/1000
gets mounted. I tried with After=run-user-1000.mount
but it still fails.
Is this possible or do I have to stick with the Restart=on-failure
?
systemd systemd-unit
When I reboot my Raspberry Pi (Stretch) a daemon fails to start because /run/user/1000
does not exist. This is my unit file:
[Unit]
Description=SBFspot Upload Daemon
[Service]
User=pi
Type=forking
TimeoutStopSec=60
ExecStart=/usr/local/bin/sbfspot.3/SBFspotUploadDaemon -p /run/user/1000/sbfspotupload.pid 2>&1> /dev/null
PIDFile=/run/user/1000/sbfspotupload.pid
Restart=no
RestartSec=15
SuccessExitStatus=SIGKILL
[Install]
WantedBy=default.target
When I configure to Restart=on-failure
all goes well after a few retries, but that's not really what I want.
I want the daemon to wait for /run/user/1000
gets mounted. I tried with After=run-user-1000.mount
but it still fails.
Is this possible or do I have to stick with the Restart=on-failure
?
systemd systemd-unit
asked Jun 25 at 21:14
SBF
1182
1182
User=pi
makes it run as user pi, no? $UID of pi is 1000
â SBF
Jun 26 at 10:25
add a comment |Â
User=pi
makes it run as user pi, no? $UID of pi is 1000
â SBF
Jun 26 at 10:25
User=pi
makes it run as user pi, no? $UID of pi is 1000â SBF
Jun 26 at 10:25
User=pi
makes it run as user pi, no? $UID of pi is 1000â SBF
Jun 26 at 10:25
add a comment |Â
1 Answer
1
active
oldest
votes
up vote
1
down vote
accepted
/run/user/1000
, which of course does not exist until user #1000 logs in or explicitly starts up xyr per-user service management, is a red herring. The entire mechanism that uses it should not be there.
Bug #215 for this program runs a lot deeper than you think. This service unit file is very wrong, as is the operation of the program itself. There is a lot of cargo cult programming, based upon not actually understanding the fundamentals of systemd service units.
Service units are not shell script. The systemd manual does explain this. TheExecStart
setting here causes the service program to be run with some extra arguments,2>&1>
and/dev/null
.
The service manager already ensures that only one service runs. All of this code added here is unnecessary junk.
The rickety and dangerous PID file mechanism should not be used. It has no place in proper service management.
The service manager also handles invoking the service in a dæmon context. A lot of the other code inmain()
is also unnecessary junk, based upon the dæmonization fallacy.- The program should not be
fork()
ing at all, and the service readiness mechanism should not be specified asType=forking
. Like so many programs in the real world, this program is not speaking the forking readiness protocol in the first place. - The program is already running as the superuser.
User=root
is unnecessary, and indeed the service should be redesigned to not need running with superuser privileges but rather run under the aegis of a dedicated unprivileged service account. - The service manager is already logging the standard output and error, and doing a better job of it than this program is. This home-grown logging system just grows a log file until it fills up an entire filesystem, consuming all of the emergency space reserved for the superuser.
- Your log is simply the standard error, handily accessible from C++ as
std::clog
. - In fact, all of the code from the
fork()
to the redirection of standard error should not be used. Service management handles all of this, from session leadership through working directory and umask to standard I/O, and does it properly. This program does not, and it should not be attempting to do any of this for itself when used under a service manager.Everything that you took from Boost was wrong.
- The program should not be
Three service units is unnecessary maintenance overhead. They only differ in theirAfter
settings, and those can just be merged into one.
Graceless termination is not success. Given that there was already one problem with cleaning up files upon termination,SuccessExitStatus=SIGKILL
is wrongheaded. Normal termination should be graceful, viaSIGTERM
, andSIGKILL
should be considered abnormal. (Of course, the wholeoutput
file mechanism is a badly implemented home-grown logging mechanism that should not be used under service management, as already explained.) This is the systemd default.
Destructors of the database objects and other stuff should run. Do not leavemain()
withexit()
.
A dæmon program implemented properly for running under a service manager, be it from daemontools, runit, s6, nosh, systemd, or something else, is a lot shorter:
⦠// 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;
And the service unit is shorter, too:
[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
The log output is viewable with systemctl status
and journalctl
(with the -u
option and the service name, if desired).
Further reading
- Jonathan de Boyne Pollard (2016). Don't use
logrotate
ornewsyslog
in this century.. Frequently Given Answers. - Jonathan de Boyne Pollard (2001). Mistakes to avoid when designing Unix dæmon programs. Frequently Given Answers.
- Jonathan de Boyne Pollard (2015). Readiness protocol problems with Unix dæmons. Frequently Given Answers.
- https://unix.stackexchange.com/a/283739/5132
- https://unix.stackexchange.com/a/321716/5132
Well, I'm impressed by your answer and will take your guidelines into account. The use of systemd is added very recently. Before V3.4.1 the daemon was started with/etc/rc.local
About the logging: I suppose all logging is lost after reboot? In that case it's difficult troubleshooting, no? I don't get the use ofRestart=on-success
instead ofon-failure
- Can you explain this? Thanks.
â SBF
Jun 29 at 21:16
I have to addUser=pi
or daemon will run as root
â SBF
Jun 29 at 22:17
add a comment |Â
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
1
down vote
accepted
/run/user/1000
, which of course does not exist until user #1000 logs in or explicitly starts up xyr per-user service management, is a red herring. The entire mechanism that uses it should not be there.
Bug #215 for this program runs a lot deeper than you think. This service unit file is very wrong, as is the operation of the program itself. There is a lot of cargo cult programming, based upon not actually understanding the fundamentals of systemd service units.
Service units are not shell script. The systemd manual does explain this. TheExecStart
setting here causes the service program to be run with some extra arguments,2>&1>
and/dev/null
.
The service manager already ensures that only one service runs. All of this code added here is unnecessary junk.
The rickety and dangerous PID file mechanism should not be used. It has no place in proper service management.
The service manager also handles invoking the service in a dæmon context. A lot of the other code inmain()
is also unnecessary junk, based upon the dæmonization fallacy.- The program should not be
fork()
ing at all, and the service readiness mechanism should not be specified asType=forking
. Like so many programs in the real world, this program is not speaking the forking readiness protocol in the first place. - The program is already running as the superuser.
User=root
is unnecessary, and indeed the service should be redesigned to not need running with superuser privileges but rather run under the aegis of a dedicated unprivileged service account. - The service manager is already logging the standard output and error, and doing a better job of it than this program is. This home-grown logging system just grows a log file until it fills up an entire filesystem, consuming all of the emergency space reserved for the superuser.
- Your log is simply the standard error, handily accessible from C++ as
std::clog
. - In fact, all of the code from the
fork()
to the redirection of standard error should not be used. Service management handles all of this, from session leadership through working directory and umask to standard I/O, and does it properly. This program does not, and it should not be attempting to do any of this for itself when used under a service manager.Everything that you took from Boost was wrong.
- The program should not be
Three service units is unnecessary maintenance overhead. They only differ in theirAfter
settings, and those can just be merged into one.
Graceless termination is not success. Given that there was already one problem with cleaning up files upon termination,SuccessExitStatus=SIGKILL
is wrongheaded. Normal termination should be graceful, viaSIGTERM
, andSIGKILL
should be considered abnormal. (Of course, the wholeoutput
file mechanism is a badly implemented home-grown logging mechanism that should not be used under service management, as already explained.) This is the systemd default.
Destructors of the database objects and other stuff should run. Do not leavemain()
withexit()
.
A dæmon program implemented properly for running under a service manager, be it from daemontools, runit, s6, nosh, systemd, or something else, is a lot shorter:
⦠// 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;
And the service unit is shorter, too:
[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
The log output is viewable with systemctl status
and journalctl
(with the -u
option and the service name, if desired).
Further reading
- Jonathan de Boyne Pollard (2016). Don't use
logrotate
ornewsyslog
in this century.. Frequently Given Answers. - Jonathan de Boyne Pollard (2001). Mistakes to avoid when designing Unix dæmon programs. Frequently Given Answers.
- Jonathan de Boyne Pollard (2015). Readiness protocol problems with Unix dæmons. Frequently Given Answers.
- https://unix.stackexchange.com/a/283739/5132
- https://unix.stackexchange.com/a/321716/5132
Well, I'm impressed by your answer and will take your guidelines into account. The use of systemd is added very recently. Before V3.4.1 the daemon was started with/etc/rc.local
About the logging: I suppose all logging is lost after reboot? In that case it's difficult troubleshooting, no? I don't get the use ofRestart=on-success
instead ofon-failure
- Can you explain this? Thanks.
â SBF
Jun 29 at 21:16
I have to addUser=pi
or daemon will run as root
â SBF
Jun 29 at 22:17
add a comment |Â
up vote
1
down vote
accepted
/run/user/1000
, which of course does not exist until user #1000 logs in or explicitly starts up xyr per-user service management, is a red herring. The entire mechanism that uses it should not be there.
Bug #215 for this program runs a lot deeper than you think. This service unit file is very wrong, as is the operation of the program itself. There is a lot of cargo cult programming, based upon not actually understanding the fundamentals of systemd service units.
Service units are not shell script. The systemd manual does explain this. TheExecStart
setting here causes the service program to be run with some extra arguments,2>&1>
and/dev/null
.
The service manager already ensures that only one service runs. All of this code added here is unnecessary junk.
The rickety and dangerous PID file mechanism should not be used. It has no place in proper service management.
The service manager also handles invoking the service in a dæmon context. A lot of the other code inmain()
is also unnecessary junk, based upon the dæmonization fallacy.- The program should not be
fork()
ing at all, and the service readiness mechanism should not be specified asType=forking
. Like so many programs in the real world, this program is not speaking the forking readiness protocol in the first place. - The program is already running as the superuser.
User=root
is unnecessary, and indeed the service should be redesigned to not need running with superuser privileges but rather run under the aegis of a dedicated unprivileged service account. - The service manager is already logging the standard output and error, and doing a better job of it than this program is. This home-grown logging system just grows a log file until it fills up an entire filesystem, consuming all of the emergency space reserved for the superuser.
- Your log is simply the standard error, handily accessible from C++ as
std::clog
. - In fact, all of the code from the
fork()
to the redirection of standard error should not be used. Service management handles all of this, from session leadership through working directory and umask to standard I/O, and does it properly. This program does not, and it should not be attempting to do any of this for itself when used under a service manager.Everything that you took from Boost was wrong.
- The program should not be
Three service units is unnecessary maintenance overhead. They only differ in theirAfter
settings, and those can just be merged into one.
Graceless termination is not success. Given that there was already one problem with cleaning up files upon termination,SuccessExitStatus=SIGKILL
is wrongheaded. Normal termination should be graceful, viaSIGTERM
, andSIGKILL
should be considered abnormal. (Of course, the wholeoutput
file mechanism is a badly implemented home-grown logging mechanism that should not be used under service management, as already explained.) This is the systemd default.
Destructors of the database objects and other stuff should run. Do not leavemain()
withexit()
.
A dæmon program implemented properly for running under a service manager, be it from daemontools, runit, s6, nosh, systemd, or something else, is a lot shorter:
⦠// 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;
And the service unit is shorter, too:
[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
The log output is viewable with systemctl status
and journalctl
(with the -u
option and the service name, if desired).
Further reading
- Jonathan de Boyne Pollard (2016). Don't use
logrotate
ornewsyslog
in this century.. Frequently Given Answers. - Jonathan de Boyne Pollard (2001). Mistakes to avoid when designing Unix dæmon programs. Frequently Given Answers.
- Jonathan de Boyne Pollard (2015). Readiness protocol problems with Unix dæmons. Frequently Given Answers.
- https://unix.stackexchange.com/a/283739/5132
- https://unix.stackexchange.com/a/321716/5132
Well, I'm impressed by your answer and will take your guidelines into account. The use of systemd is added very recently. Before V3.4.1 the daemon was started with/etc/rc.local
About the logging: I suppose all logging is lost after reboot? In that case it's difficult troubleshooting, no? I don't get the use ofRestart=on-success
instead ofon-failure
- Can you explain this? Thanks.
â SBF
Jun 29 at 21:16
I have to addUser=pi
or daemon will run as root
â SBF
Jun 29 at 22:17
add a comment |Â
up vote
1
down vote
accepted
up vote
1
down vote
accepted
/run/user/1000
, which of course does not exist until user #1000 logs in or explicitly starts up xyr per-user service management, is a red herring. The entire mechanism that uses it should not be there.
Bug #215 for this program runs a lot deeper than you think. This service unit file is very wrong, as is the operation of the program itself. There is a lot of cargo cult programming, based upon not actually understanding the fundamentals of systemd service units.
Service units are not shell script. The systemd manual does explain this. TheExecStart
setting here causes the service program to be run with some extra arguments,2>&1>
and/dev/null
.
The service manager already ensures that only one service runs. All of this code added here is unnecessary junk.
The rickety and dangerous PID file mechanism should not be used. It has no place in proper service management.
The service manager also handles invoking the service in a dæmon context. A lot of the other code inmain()
is also unnecessary junk, based upon the dæmonization fallacy.- The program should not be
fork()
ing at all, and the service readiness mechanism should not be specified asType=forking
. Like so many programs in the real world, this program is not speaking the forking readiness protocol in the first place. - The program is already running as the superuser.
User=root
is unnecessary, and indeed the service should be redesigned to not need running with superuser privileges but rather run under the aegis of a dedicated unprivileged service account. - The service manager is already logging the standard output and error, and doing a better job of it than this program is. This home-grown logging system just grows a log file until it fills up an entire filesystem, consuming all of the emergency space reserved for the superuser.
- Your log is simply the standard error, handily accessible from C++ as
std::clog
. - In fact, all of the code from the
fork()
to the redirection of standard error should not be used. Service management handles all of this, from session leadership through working directory and umask to standard I/O, and does it properly. This program does not, and it should not be attempting to do any of this for itself when used under a service manager.Everything that you took from Boost was wrong.
- The program should not be
Three service units is unnecessary maintenance overhead. They only differ in theirAfter
settings, and those can just be merged into one.
Graceless termination is not success. Given that there was already one problem with cleaning up files upon termination,SuccessExitStatus=SIGKILL
is wrongheaded. Normal termination should be graceful, viaSIGTERM
, andSIGKILL
should be considered abnormal. (Of course, the wholeoutput
file mechanism is a badly implemented home-grown logging mechanism that should not be used under service management, as already explained.) This is the systemd default.
Destructors of the database objects and other stuff should run. Do not leavemain()
withexit()
.
A dæmon program implemented properly for running under a service manager, be it from daemontools, runit, s6, nosh, systemd, or something else, is a lot shorter:
⦠// 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;
And the service unit is shorter, too:
[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
The log output is viewable with systemctl status
and journalctl
(with the -u
option and the service name, if desired).
Further reading
- Jonathan de Boyne Pollard (2016). Don't use
logrotate
ornewsyslog
in this century.. Frequently Given Answers. - Jonathan de Boyne Pollard (2001). Mistakes to avoid when designing Unix dæmon programs. Frequently Given Answers.
- Jonathan de Boyne Pollard (2015). Readiness protocol problems with Unix dæmons. Frequently Given Answers.
- https://unix.stackexchange.com/a/283739/5132
- https://unix.stackexchange.com/a/321716/5132
/run/user/1000
, which of course does not exist until user #1000 logs in or explicitly starts up xyr per-user service management, is a red herring. The entire mechanism that uses it should not be there.
Bug #215 for this program runs a lot deeper than you think. This service unit file is very wrong, as is the operation of the program itself. There is a lot of cargo cult programming, based upon not actually understanding the fundamentals of systemd service units.
Service units are not shell script. The systemd manual does explain this. TheExecStart
setting here causes the service program to be run with some extra arguments,2>&1>
and/dev/null
.
The service manager already ensures that only one service runs. All of this code added here is unnecessary junk.
The rickety and dangerous PID file mechanism should not be used. It has no place in proper service management.
The service manager also handles invoking the service in a dæmon context. A lot of the other code inmain()
is also unnecessary junk, based upon the dæmonization fallacy.- The program should not be
fork()
ing at all, and the service readiness mechanism should not be specified asType=forking
. Like so many programs in the real world, this program is not speaking the forking readiness protocol in the first place. - The program is already running as the superuser.
User=root
is unnecessary, and indeed the service should be redesigned to not need running with superuser privileges but rather run under the aegis of a dedicated unprivileged service account. - The service manager is already logging the standard output and error, and doing a better job of it than this program is. This home-grown logging system just grows a log file until it fills up an entire filesystem, consuming all of the emergency space reserved for the superuser.
- Your log is simply the standard error, handily accessible from C++ as
std::clog
. - In fact, all of the code from the
fork()
to the redirection of standard error should not be used. Service management handles all of this, from session leadership through working directory and umask to standard I/O, and does it properly. This program does not, and it should not be attempting to do any of this for itself when used under a service manager.Everything that you took from Boost was wrong.
- The program should not be
Three service units is unnecessary maintenance overhead. They only differ in theirAfter
settings, and those can just be merged into one.
Graceless termination is not success. Given that there was already one problem with cleaning up files upon termination,SuccessExitStatus=SIGKILL
is wrongheaded. Normal termination should be graceful, viaSIGTERM
, andSIGKILL
should be considered abnormal. (Of course, the wholeoutput
file mechanism is a badly implemented home-grown logging mechanism that should not be used under service management, as already explained.) This is the systemd default.
Destructors of the database objects and other stuff should run. Do not leavemain()
withexit()
.
A dæmon program implemented properly for running under a service manager, be it from daemontools, runit, s6, nosh, systemd, or something else, is a lot shorter:
⦠// 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;
And the service unit is shorter, too:
[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
The log output is viewable with systemctl status
and journalctl
(with the -u
option and the service name, if desired).
Further reading
- Jonathan de Boyne Pollard (2016). Don't use
logrotate
ornewsyslog
in this century.. Frequently Given Answers. - Jonathan de Boyne Pollard (2001). Mistakes to avoid when designing Unix dæmon programs. Frequently Given Answers.
- Jonathan de Boyne Pollard (2015). Readiness protocol problems with Unix dæmons. Frequently Given Answers.
- https://unix.stackexchange.com/a/283739/5132
- https://unix.stackexchange.com/a/321716/5132
answered Jun 26 at 7:15
JdeBP
27.9k459133
27.9k459133
Well, I'm impressed by your answer and will take your guidelines into account. The use of systemd is added very recently. Before V3.4.1 the daemon was started with/etc/rc.local
About the logging: I suppose all logging is lost after reboot? In that case it's difficult troubleshooting, no? I don't get the use ofRestart=on-success
instead ofon-failure
- Can you explain this? Thanks.
â SBF
Jun 29 at 21:16
I have to addUser=pi
or daemon will run as root
â SBF
Jun 29 at 22:17
add a comment |Â
Well, I'm impressed by your answer and will take your guidelines into account. The use of systemd is added very recently. Before V3.4.1 the daemon was started with/etc/rc.local
About the logging: I suppose all logging is lost after reboot? In that case it's difficult troubleshooting, no? I don't get the use ofRestart=on-success
instead ofon-failure
- Can you explain this? Thanks.
â SBF
Jun 29 at 21:16
I have to addUser=pi
or daemon will run as root
â SBF
Jun 29 at 22:17
Well, I'm impressed by your answer and will take your guidelines into account. The use of systemd is added very recently. Before V3.4.1 the daemon was started with
/etc/rc.local
About the logging: I suppose all logging is lost after reboot? In that case it's difficult troubleshooting, no? I don't get the use of Restart=on-success
instead of on-failure
- Can you explain this? Thanks.â SBF
Jun 29 at 21:16
Well, I'm impressed by your answer and will take your guidelines into account. The use of systemd is added very recently. Before V3.4.1 the daemon was started with
/etc/rc.local
About the logging: I suppose all logging is lost after reboot? In that case it's difficult troubleshooting, no? I don't get the use of Restart=on-success
instead of on-failure
- Can you explain this? Thanks.â SBF
Jun 29 at 21:16
I have to add
User=pi
or daemon will run as rootâ SBF
Jun 29 at 22:17
I have to add
User=pi
or daemon will run as rootâ SBF
Jun 29 at 22:17
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f451855%2frun-user-uid-not-mounted-when-daemon-starts%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
User=pi
makes it run as user pi, no? $UID of pi is 1000â SBF
Jun 26 at 10:25