www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.announce - Daemonize v0.1 - simple way to create cross-platform daemons

reply "NCrashed" <NCrashed gmail.com> writes:
Finally I've finished library for wrapping applications into 
daemons or services (Windows). The library hides 
platform-specific boilerplate behind compile-time API:
```
// First you need to describe your daemon via template
alias daemon = Daemon!(
     "DaemonizeExample1", // unique name

     // Setting associative map signal -> callbacks
     KeyValueList!(
         // You can bind same delegate for several signals by 
Composition template
         // delegate can take additional argument to know which 
signal is caught
         Composition!(Signal.Terminate, Signal.Quit, 
Signal.Shutdown, Signal.Stop), (logger, signal)
         {
             logger.logInfo("Exiting...");
             return false; // returning false will terminate daemon
         },
         Signal.HangUp, (logger)
         {
             logger.logInfo("Hello World!");
             return true; // continue execution
         }
     ),

     // Main function where your code is
     (logger, shouldExit) {
         // will stop the daemon in 5 minutes
         auto time = Clock.currSystemTick + 
cast(TickDuration)5.dur!"minutes";
         while(!shouldExit() && time > Clock.currSystemTick) {  }

         return 0;
     }
);

int main()
{
     return buildDaemon!daemon.run(new shared 
StrictLogger("logfile.log"));
}
```

At the moment daemonize has following features:
* Daemons for GNU/Linux, services for Windows
* Custom signals
* Signal composition
* Client for sending signals to defined daemons
* Auto installing and uninstalling for Windows services
* Usage of .pid and .lock files (GNU/Linux)
* Privileges lowing (GNU/Linux)

Daemonize operates well with vibe.d (example - 
https://github.com/NCrashed/daemonize/tree/master/examples/03.Vibed)

P.S. At the moment library doesn't support Mac and other Posix 
systems, the support is going to be added at next releases.
Aug 31 2014
next sibling parent ketmar via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> writes:
On Sun, 31 Aug 2014 11:27:41 +0000
NCrashed via Digitalmars-d-announce
<digitalmars-d-announce puremagic.com> wrote:

looks very interesting, thank you.
Aug 31 2014
prev sibling next sibling parent reply Philippe Sigaud via Digitalmars-d-announce writes:
Nice!

I have a few questions/remarks, mainly to simplify the API somewhat.
Please bear with me :-)

 // First you need to describe your daemon via template
 alias daemon = Daemon!(
     "DaemonizeExample1", // unique name
Does the user sees/uses this name in any way afterwards? Because I think you could also produce a unique string at compile-time (by using __FILE__ and __LINE__, unless someone has a better idea), if the user does not provide one. Maybe he just wants an anonymous daemon, or doesn't care, whatever.
     // Setting associative map signal -> callbacks
     KeyValueList!(
If I understand correctly, the Daemon template waits for a list of (at least one) Signal, then a delegate, then some more Signals, another delegate, and so on? If that's so I think you could ditch KeyValueList (or build it invisibly to the user) and let the user write only the signals and delegates: alias daemon = Daemon!( Signal.Terminate, Signal.Quit, Signal.Shutdown, Signal.Stop, (logger, signal) { ...}, Signal.Hangup, (logger) { ...} ... ); Iterate the argument list, collecting Signals. When you hit a delegate, create a Composition!( ... ) with the previous signals, jump above the delegate and so on. Is the idea that, if the delegate has two arguments, then the second is the signal that will be passed to it, and if it has only one argument, only the logger will be passed? What if the user does not want a logger? Is a daemon always associated to a log file in OSes?
     // Main function where your code is
     (logger, shouldExit) {
         // will stop the daemon in 5 minutes
         auto time = Clock.currSystemTick +
 cast(TickDuration)5.dur!"minutes";
         while(!shouldExit() && time > Clock.currSystemTick) {  }

         return 0;
     }
Is the main function always the last delegate? Concerning the DaemonClient template, could you not ask for Daemon to generate it on demand? Or is DaemonClient always used in another module? Because, given a Daemon, extracting the simplified DaemonClient can be done, I think.
 * Custom signals
enum Signal : string { ... } nogc Signal customSignal(string name) safe pure nothrow { return cast(Signal)name; } I didn't know you could have an enum and extend it with a cast like this. Wow.
 * Signal composition
What happens when an unhandled signal is passed to a daemon?
 P.S. At the moment library doesn't support Mac and other Posix systems, the
 support is going to be added at next releases.
Do you foresee any difficulty in adapting this to Mac?
Aug 31 2014
next sibling parent reply "NCrashed" <NCrashed gmail.com> writes:
Thanks a lot for the respond!

 Does the user sees/uses this name in any way afterwards? 
 Because I
 think you could also produce a unique string at compile-time 
 (by using
 __FILE__ and __LINE__, unless someone has a better idea), if 
 the user
 does not provide one. Maybe he just wants an anonymous daemon, 
 or
 doesn't care, whatever.
Yes, the name is used in windows service manager (you can start/stop the daemon by control panel) and for default locations of .pid and .lock files. Auto generated name will prevent sending signals and could be ugly displayed in service manager. The feature is useful for simple daemons, I will play around with that idea to find out if it worth.
 If I understand correctly, the Daemon template waits for a list 
 of (at
 least one) Signal, then a delegate, then some more Signals, 
 another
 delegate, and so on?

 If that's so I think you could ditch KeyValueList (or build it
 invisibly to the user) and let the user write only the signals 
 and
 delegates:

 alias daemon = Daemon!(
     Signal.Terminate, Signal.Quit, Signal.Shutdown, Signal.Stop,
     (logger, signal) {
     ...},
     Signal.Hangup,
     (logger) {
     ...}
 ...
 );

 Iterate the argument list, collecting Signals. When you hit a
 delegate, create a Composition!( ... ) with the previous 
 signals, jump
 above the delegate and so on.
I will add the approach in next release (it requires some more additional templates to wrap all the mess) thanks to arguments "grammar" has no ambiguities.
 Is the idea that, if the delegate has two arguments, then the 
 second
 is the signal that will be passed to it, and if it has only one
 argument, only the logger will be passed?
Yes
 What if the user does not want a logger? Is a daemon always 
 associated
 to a log file in OSes?
It is a general rule as the stderr and stdout files are closed. At next version I want to use duck typing for logger (or sink approach same as toString uses) and add a feature to reopen stderr/stdout to another file.
 Is the main function always the last delegate?
Yes
 Concerning the DaemonClient template, could you not ask for 
 Daemon to
 generate it on demand? Or is DaemonClient always used in another
 module?
DaemonClient is designed to be used in another module, you can send signals with full Daemon template.
 What happens when an unhandled signal is passed to a daemon?
The event is logged down and ignored.
 Do you foresee any difficulty in adapting this to Mac?
All logic should be the same as for linux, I just don't have any machine to test all out. I hope virtual machine will help. On Sunday, 31 August 2014 at 16:01:10 UTC, Philippe Sigaud via Digitalmars-d-announce wrote:
 Nice!

 I have a few questions/remarks, mainly to simplify the API 
 somewhat.
 Please bear with me :-)

 // First you need to describe your daemon via template
 alias daemon = Daemon!(
     "DaemonizeExample1", // unique name
Does the user sees/uses this name in any way afterwards? Because I think you could also produce a unique string at compile-time (by using __FILE__ and __LINE__, unless someone has a better idea), if the user does not provide one. Maybe he just wants an anonymous daemon, or doesn't care, whatever.
     // Setting associative map signal -> callbacks
     KeyValueList!(
If I understand correctly, the Daemon template waits for a list of (at least one) Signal, then a delegate, then some more Signals, another delegate, and so on? If that's so I think you could ditch KeyValueList (or build it invisibly to the user) and let the user write only the signals and delegates: alias daemon = Daemon!( Signal.Terminate, Signal.Quit, Signal.Shutdown, Signal.Stop, (logger, signal) { ...}, Signal.Hangup, (logger) { ...} ... ); Iterate the argument list, collecting Signals. When you hit a delegate, create a Composition!( ... ) with the previous signals, jump above the delegate and so on. Is the idea that, if the delegate has two arguments, then the second is the signal that will be passed to it, and if it has only one argument, only the logger will be passed? What if the user does not want a logger? Is a daemon always associated to a log file in OSes?
     // Main function where your code is
     (logger, shouldExit) {
         // will stop the daemon in 5 minutes
         auto time = Clock.currSystemTick +
 cast(TickDuration)5.dur!"minutes";
         while(!shouldExit() && time > Clock.currSystemTick) {  
 }

         return 0;
     }
Is the main function always the last delegate? Concerning the DaemonClient template, could you not ask for Daemon to generate it on demand? Or is DaemonClient always used in another module? Because, given a Daemon, extracting the simplified DaemonClient can be done, I think.
 * Custom signals
enum Signal : string { ... } nogc Signal customSignal(string name) safe pure nothrow { return cast(Signal)name; } I didn't know you could have an enum and extend it with a cast like this. Wow.
 * Signal composition
What happens when an unhandled signal is passed to a daemon?
 P.S. At the moment library doesn't support Mac and other Posix 
 systems, the
 support is going to be added at next releases.
Do you foresee any difficulty in adapting this to Mac?
Aug 31 2014
parent reply Philippe Sigaud via Digitalmars-d-announce writes:
 Does the user sees/uses this name in any way afterwards? Because I
 think you could also produce a unique string at compile-time (by using
 __FILE__ and __LINE__, unless someone has a better idea), if the user
 does not provide one. Maybe he just wants an anonymous daemon, or
 doesn't care, whatever.
Yes, the name is used in windows service manager (you can start/stop the daemon by control panel) and for default locations of .pid and .lock files.
OK.
 Auto generated name will prevent sending signals and could be ugly displayed
 in service manager. The feature is useful for simple daemons, I will play
 around with that idea to find out if it worth.
Great.
 I will add the approach in next release (it requires some more additional
 templates to wrap all the mess) thanks to arguments "grammar" has no
 ambiguities.
Yes, the grammar is simple, use it to simplify the life of your users.
 Is the idea that, if the delegate has two arguments, then the second
 is the signal that will be passed to it, and if it has only one
 argument, only the logger will be passed?
Yes
OK. IIRC, I read in your code that composed signals means the next delegate must have the (logger, signal) {...} form. Why?
 What if the user does not want a logger? Is a daemon always associated
 to a log file in OSes?
It is a general rule as the stderr and stdout files are closed. At next version I want to use duck typing for logger (or sink approach same as toString uses) and add a feature to reopen stderr/stdout to another file.
OK.
 Concerning the DaemonClient template, could you not ask for Daemon to
 generate it on demand? Or is DaemonClient always used in another
 module?
DaemonClient is designed to be used in another module, you can send signals with full Daemon template.
OK. I'd have thought that just having the name of the daemon would be enough to send it signals.
 What happens when an unhandled signal is passed to a daemon?
The event is logged down and ignored.
Is that the standard behavior for daemons in OSes? You could have the daemon stopped, after logging the unhandled signal. Or you could also block compilation if a daemon does not handle all signals... Or have a 'catch-all' delegate, as std.concurrency.receive, which uses a (Variant v) {...} delegate at the end. See: http://dlang.org/library/std/concurrency/receive.html (btw, signals could also be types and you could have a handling syntax similar to receive).
Aug 31 2014
parent reply "NCrashed" <NCrashed gmail.com> writes:
 IIRC, I read in your code that composed signals means the next
 delegate must have the (logger, signal) {...} form.
 Why?
I can (not must) have the form, the delegate params are tested independently from signal composition.
 Is that the standard behavior for daemons in OSes?
Most signals are simply ignored (except termination ones).
 You could have the daemon stopped, after logging the unhandled 
 signal.
 Or you could also block compilation if a daemon does not handle 
 all signals...
Some signals could be sent without any reason: sighup or interrogate in windows. Ignoring most signals is a better strategy, the exception could be done for terminating signals - their default handlers should set `shouldExit` flag to true.
 Or have a 'catch-all' delegate, as std.concurrency.receive, 
 which uses
 a (Variant v) {...} delegate at the end.
 See:
 http://dlang.org/library/std/concurrency/receive.html
Good idea, it will be implemented.
 (btw, signals could also be types and you could have a handling 
 syntax
 similar to receive).
Ohh, that is much more complicated feature as it may seem. Signaling in both OSes are very limited. We need an additional channel to pass arbitrary memory between processes and also restrict data to be serializable. If I continue to move in that direction, the D will occasionally obtain a library for distributed cluster computing (like Cloud Haskell) ;). On Sunday, 31 August 2014 at 19:45:32 UTC, Philippe Sigaud via Digitalmars-d-announce wrote:
 Does the user sees/uses this name in any way afterwards? 
 Because I
 think you could also produce a unique string at compile-time 
 (by using
 __FILE__ and __LINE__, unless someone has a better idea), if 
 the user
 does not provide one. Maybe he just wants an anonymous 
 daemon, or
 doesn't care, whatever.
Yes, the name is used in windows service manager (you can start/stop the daemon by control panel) and for default locations of .pid and .lock files.
OK.
 Auto generated name will prevent sending signals and could be 
 ugly displayed
 in service manager. The feature is useful for simple daemons, 
 I will play
 around with that idea to find out if it worth.
Great.
 I will add the approach in next release (it requires some more 
 additional
 templates to wrap all the mess) thanks to arguments "grammar" 
 has no
 ambiguities.
Yes, the grammar is simple, use it to simplify the life of your users.
 Is the idea that, if the delegate has two arguments, then the 
 second
 is the signal that will be passed to it, and if it has only 
 one
 argument, only the logger will be passed?
Yes
OK. IIRC, I read in your code that composed signals means the next delegate must have the (logger, signal) {...} form. Why?
 What if the user does not want a logger? Is a daemon always 
 associated
 to a log file in OSes?
It is a general rule as the stderr and stdout files are closed. At next version I want to use duck typing for logger (or sink approach same as toString uses) and add a feature to reopen stderr/stdout to another file.
OK.
 Concerning the DaemonClient template, could you not ask for 
 Daemon to
 generate it on demand? Or is DaemonClient always used in 
 another
 module?
DaemonClient is designed to be used in another module, you can send signals with full Daemon template.
OK. I'd have thought that just having the name of the daemon would be enough to send it signals.
 What happens when an unhandled signal is passed to a daemon?
The event is logged down and ignored.
Is that the standard behavior for daemons in OSes? You could have the daemon stopped, after logging the unhandled signal. Or you could also block compilation if a daemon does not handle all signals... Or have a 'catch-all' delegate, as std.concurrency.receive, which uses a (Variant v) {...} delegate at the end. See: http://dlang.org/library/std/concurrency/receive.html (btw, signals could also be types and you could have a handling syntax similar to receive).
Aug 31 2014
parent Philippe Sigaud via Digitalmars-d-announce writes:
 I can (not must) have the form, the delegate params are tested independently
 from signal composition.
OK, good.
 Is that the standard behavior for daemons in OSes?
Most signals are simply ignored (except termination ones).
I see.
 Some signals could be sent without any reason: sighup or interrogate in
 windows.
 Ignoring most signals is a better strategy, the exception could be done for
 terminating signals - their default handlers should set `shouldExit` flag to
 true.
OK, I didn't know that.
 http://dlang.org/library/std/concurrency/receive.html
Good idea, it will be implemented.
Great!
Aug 31 2014
prev sibling parent reply "Meta" <jared771 gmail.com> writes:
On Sunday, 31 August 2014 at 16:01:10 UTC, Philippe Sigaud via 
Digitalmars-d-announce wrote:
 * Custom signals
enum Signal : string { ... } nogc Signal customSignal(string name) safe pure nothrow { return cast(Signal)name; } I didn't know you could have an enum and extend it with a cast like this. Wow.
This is not a good thing. Enums are supposed to denote a *closed*, enumerated set of items. It's fine here (but IMO bad style) because the author expects there to be user-created values casted to Signal passed to functions/templates that expect a Signal, but this would wreak havoc on code that was expecting a valid enum value (by valid, I mean only one of the predefined enum values).
Aug 31 2014
parent Philippe Sigaud via Digitalmars-d-announce writes:
On Sun, Aug 31, 2014 at 11:36 PM, Meta via Digitalmars-d-announce
<digitalmars-d-announce puremagic.com> wrote:
 I didn't know you could have an enum and extend it with a cast like this.
This is not a good thing. Enums are supposed to denote a *closed*, enumerated set of items.
I agree.
 It's fine here (but IMO bad style) because the
 author expects there to be user-created values casted to Signal passed to
 functions/templates that expect a Signal, but this would wreak havoc on code
 that was expecting a valid enum value (by valid, I mean only one of the
 predefined enum values).
I was about to suggest final switch, until I saw this extension of Signal. I wonder what happens to final switch in this case.
Aug 31 2014
prev sibling next sibling parent "Rei Roldan" <raroldan gmail.com> writes:
Excellent library, thank you very much for sharing it. I was 
about to start my own when I ran into it :)
Sep 26 2014
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 8/31/14, 4:27 AM, NCrashed wrote:
 Finally I've finished library for wrapping applications into daemons or
 services (Windows). The library hides platform-specific boilerplate
 behind compile-time API:
[snip] I completely missed this. Has it been reddited? -- Andrei
Sep 26 2014
parent reply "NCrashed" <NCrashed gmail.com> writes:
On Saturday, 27 September 2014 at 03:49:31 UTC, Andrei 
Alexandrescu wrote:
 On 8/31/14, 4:27 AM, NCrashed wrote:
 Finally I've finished library for wrapping applications into 
 daemons or
 services (Windows). The library hides platform-specific 
 boilerplate
 behind compile-time API:
[snip] I completely missed this. Has it been reddited? -- Andrei
It is a first public release, the code was roaming from one application to another until I found the strength to clean up, redesign an API and publish as a separate library.
Sep 27 2014
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/27/14, 2:23 AM, NCrashed wrote:
 On Saturday, 27 September 2014 at 03:49:31 UTC, Andrei Alexandrescu wrote:
 On 8/31/14, 4:27 AM, NCrashed wrote:
 Finally I've finished library for wrapping applications into daemons or
 services (Windows). The library hides platform-specific boilerplate
 behind compile-time API:
[snip] I completely missed this. Has it been reddited? -- Andrei
It is a first public release, the code was roaming from one application to another until I found the strength to clean up, redesign an API and publish as a separate library.
I'll leave it up to you when/if to post about it. -- Andrei
Sep 27 2014