/run/user/$UID not mounted when daemon starts

The name of the pictureThe name of the pictureThe name of the pictureClash 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?







share|improve this question



















  • User=pi makes it run as user pi, no? $UID of pi is 1000
    – SBF
    Jun 26 at 10:25














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?







share|improve this question



















  • User=pi makes it run as user pi, no? $UID of pi is 1000
    – SBF
    Jun 26 at 10:25












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?







share|improve this question











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?









share|improve this question










share|improve this question




share|improve this question









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
















  • 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










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. The ExecStart 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 in main() 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 as Type=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.





  • Three service units is unnecessary maintenance overhead. They only differ in their After 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, via SIGTERM, and SIGKILL should be considered abnormal. (Of course, the whole output 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 leave main() with exit().

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 or newsyslog 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





share|improve this answer





















  • 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










Your Answer







StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "106"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);








 

draft saved


draft discarded


















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






























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. The ExecStart 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 in main() 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 as Type=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.





  • Three service units is unnecessary maintenance overhead. They only differ in their After 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, via SIGTERM, and SIGKILL should be considered abnormal. (Of course, the whole output 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 leave main() with exit().

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 or newsyslog 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





share|improve this answer





















  • 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














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. The ExecStart 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 in main() 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 as Type=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.





  • Three service units is unnecessary maintenance overhead. They only differ in their After 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, via SIGTERM, and SIGKILL should be considered abnormal. (Of course, the whole output 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 leave main() with exit().

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 or newsyslog 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





share|improve this answer





















  • 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












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. The ExecStart 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 in main() 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 as Type=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.





  • Three service units is unnecessary maintenance overhead. They only differ in their After 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, via SIGTERM, and SIGKILL should be considered abnormal. (Of course, the whole output 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 leave main() with exit().

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 or newsyslog 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





share|improve this answer













/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. The ExecStart 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 in main() 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 as Type=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.





  • Three service units is unnecessary maintenance overhead. They only differ in their After 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, via SIGTERM, and SIGKILL should be considered abnormal. (Of course, the whole output 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 leave main() with exit().

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 or newsyslog 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






share|improve this answer













share|improve this answer



share|improve this answer











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 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
















  • 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















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












 

draft saved


draft discarded


























 


draft saved


draft discarded














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













































































Popular posts from this blog

How to check contact read email or not when send email to Individual?

Displaying single band from multi-band raster using QGIS

How many registers does an x86_64 CPU actually have?