JXcore native app as windows service - windows

I make a simple echo server that i want to run as service under windows.
here's the code
var fs=require('fs');
var net = require('net');
var server = net.createServer(function(c) { //'connection' listener
console.log('client connected');
c.on('end', function() {
console.log('client disconnected');
});
c.write('hello\r\n');
c.pipe(c);
});
server.listen(8124, function() { //'listening' listener
console.log('server bound');
});
var fd=fs.openSync('log.txt', 'a');
fs.writeSync(fd, 'START\r\n');
fs.closeSync(fd);
process.on('SIGINT', function (par){
var fd=fs.openSync('log.txt', 'a');
fs.writeSync(fd, 'SIGINT ('+par+")\r\n");
fs.closeSync(fd);
process.exit(1);
});
process.on('exit', function (code){
var fd=fs.openSync('log.txt', 'a');
fs.writeSync(fd, 'EXIT ('+code+")\r\n");
fs.closeSync(fd);
});
Next i compiled with JXcore like:
jx package echoserver.js "echoserver" -native
Then the executable run from console or explorer double click and do its job! GOOD!
Now, with sc windows tool, i create a service
sc create echosvr full_path_to\echoserver.exe
And the echosvr is now correctly listed in services tab of windows taskmanager.
The bad comes now: i try to start the service in several way, but i get always a message that tells me that the service did not respond to the start or control request in a timely fashion.
what i miss?
Any suggestion?

I got it!
I downloaded nssm and moved the one executable (the 32bit one, just to try) in the windows dir, I run from command prompt the nssm.exe and configured the service as explained in the nssm link.
> nssm install echosvr
this opened a popup window for configuration, where i put my needs..
then:
> net start echosvr
and the server starts! Now it is visible in task manager services tab.
I stopped the service from task manager and the log file was like:
START
SIGINT (undefined)
EXIT (1)
That's what I need, because it signal a graceful close request!
To deploy a solution like this, nssm offers a command line setup that i will surely use!
(PS: It doesn't support service pause/resume, but this can be acceptable..)

Related

Under what conditions does RmGetList return 2 for the lpdwRebootReasons output parameter?

Background
I am designing an Inno Setup installer to install a Cygwin service, and I am puzzled by the behavior I'm seeing from the Windows Restart Manager APIs.
Specifically, when the service is running (started using the cygrunsrv utility), the RmGetList API function returns 2 (RmRebootReasonSessionMismatch) for its lpdwRebootReasons output parameter. This output parameter is an enumeration of type RM_REBOOT_REASON, and the description on MSDN for the RmRebootReasonSessionMismatch value is:
One or more processes are running in another Terminal Services session.
The Inno Setup log file contains lines like the following:
RestartManager found an application using one of our files: <executable name>
RestartManager found an application using one of our files: <service name>
Can use RestartManager to avoid reboot? No (2: Session Mismatch)
Inno Setup then proceeds trying to replace in-use files, as if Restart Manager was not used at all.
I am puzzled by this output value, because on two different machines I tested (Windows 10 1909 x64 and Windows Server 2012 R2), no terminal server/remote desktop users are logged on.
If I stop the service and start another executable (in the set of files to be replaced by the installer), RmGetList returns 0 (RmRebootReasonNone) for lpdwRebootReasons, and Inno Setup displays the normal dialog for in-use files and allows the user to select to automatically close them.
Process Explorer shows both processes (cygrunsrv.exe and the process it starts) running in session 0 and at System integrity level. Both are console subsystem executables.
Questions
Under what conditions does RmGetList return 2 (RmRebootReasonSessionMismatch) for its lpdwRebootReasons output parameter? (I'm trying to understand why this happens when the service is running.)
Does this value cause the entire Restart Manager session to fail, or can Restart Manager proceed even though it thinks applications are running in one or more different sessions?
For question 2, in the document RM_PROCESS_INFO
bRestartable
TRUE if the application can be restarted by the Restart Manager;
otherwise, FALSE. This member is always TRUE if the process is a
service. This member is always FALSE if the process is a critical
system process.
This value indicates if the application can be restarted by the Restart Manager.
For question 1, Note that services are running in session 0. If the process occupying the resource (registered in RmRegisterResources) is a service A, the RmGetList function which also running in the service process B will return lpdwRebootReasons = RmRebootReasonNone, bRestartable = TRUE.
But if A is not a service, then A & B are running in different sessions, lpdwRebootReasons = RmRebootReasonSessionMismatch and bRestartable = FALSE
other results:(B run with elevated privileges)
A & B is a console and in the same session:lpdwRebootReasons = RmRebootReasonNone, bRestartable = TRUE, ApplicationType = RmConsole.
A & B is a console and in the different session:lpdwRebootReasons = RmRebootReasonSessionMismatch, bRestartable = FALSE, ApplicationType = RmConsole.
A: service, B: console: lpdwRebootReasons = RmRebootReasonNone, bRestartable = TRUE, ApplicationType = RmService
(B not run with elevated privileges):
A & B is a console and in the different session:lpdwRebootReasons = RmRebootReasonCriticalProcess, bRestartable = FALSE, ApplicationType = RmCritical.
A: service, B: console: lpdwRebootReasons = RmRebootReasonPermissionDenied, bRestartable = FALSE, ApplicationType = RmCritical
According the document bRestartable depends on ApplicationType. And then, we can see that if the bRestartable = TRUE, then the lpdwRebootReasons = RmRebootReasonNone. But when bRestartable = FALSE, It depends on the other members RM_PROCESS_INFO.
A Clue from the RestartManager PowerShell Module
The RestartManager PowerShell module (special thanks to Heath Stewart) provides a simple PowerShell interface for the Restart Manager. My commands are as follows:
Set-Location <path to Cygwin root directory>
Start-RestartManagerSession
Get-ChildItem . -File -Include *.exe,*.dll -Recurse | RegisterRestartManagerResource
Get-RestartManagerProcess
Stop-RestartManagerProcess
These commands produce the following output:
Id : <process ID>
StartTime : <process start time>
Description : <executable started by cygrunsrv>
ServiceName :
ApplicationType : Console
ApplicationStatus : Running
IsRestartable : False
RebootReason : SessionMismatch
Id : <cygrunsrv process id>
StartTime : <cygrunsrv process start time>
Description : <description of service>
ServiceName : <service name>
ApplicationType : Service
ApplicationStatus : Running
IsRestartable : True
RebootReason : SessionMismatch
For some reason, Restart Manager sees the cygrunsrv.exe service process as restartable, but the executable it spawns as not restartable. (I am still curious about why this happens in the first place.)
An Imperfect Workaround Attempt
Based on this observed behavior, I first attempted the following workaround:
In the Inno Setup script's [Setup] section, set the following:
CloseApplications=yes
CloseApplicationsFilter=*.chm,*.pdf
RestartApplications=yes
The CloseApplicationsFilter directive specifies what files get registered with the Restart Manager. Note I do not specify *.exe or *.dll here; I want to manually specify only certain .exe files in the [Code] section.
Call the Inno Setup RegisterExtraCloseApplicationsResource function once for each .exe file in the setup that will NOT be spawned by cygrunsrv and put them in the RegisterExtraCloseApplicationsResources event procedure. Example:
[Code]
procedure RegisterExtraCloseApplicationsResources();
begin
RegisterExtraCloseApplicationsResource(false, ExpandConstant('{app}\bin\cygrunsrv.exe'));
end;
It's important not to register any executable spawned by cygrunsrv.exe or any of the Cygwin DLL files, because this will prevent the Restart Manager from taking effect in Inno Setup.
This solution is far from perfect, because executables normally started by cygrunsrv, if started separately, are not detected by Restart Manager (e.g., sshd.exe). For example, new SSH sessions are spawned in executables that the Restart Manager are not restartable.
A Better Solution
I've decided that a better solution is to detect any running executables from code and prompt the user apart from the Restart Manager functionality (which, simply put, doesn't work for Cygwin services).

Gradle ExecTask and Windows start

This question requires an understanding of both the Window's start command's behavior and a custom gradle ExecTask's handling of it.
Question
Why does start, with an application as a parameter, wait for the application to exit, only when being executed within a gradle ExecTask?
Explanation
From the command line, this works as expected (starts the application and returns, without waiting for the application to exit):
cmd /c myBuildEnvironment.cmd && start "some title for start" devenv.exe my.sln
Pretty simple, it calls a windows batch script to setup the environment and then launches my.sln in Visual Studio. Works just fine, not waiting for Visual Studio to be closed. And, this is what my gradle task is meant to achieve.
The same works "somewhat", using the following gradle ExecTask with start:
/**
* I know that the executable + args is replaced by commandLine. They're
* just there for readability.
*/
task openVsSolution(type: Exec, dependsOn: setupVsSln) {
description 'Opens the VS solution, in the appropriate version of Visual Studio.'
executable 'start'
environment = taskEnv
workingDir '../../src/solution'
args = [vsDevEnv, 'my.sln']
commandLine winCmdPrefix + executable + args
}
Gradle happily reports Build Successful, while Visual Studio remains open.
However, I say "somewhat" because start will actually ignore the executable argument and open my.sln with the default application for it, Microsoft Visual Studio Version Selector. So, after some research, I found that start assumes that the first parameter is the window title, then application, and lastly, the application arguments. So, I tried this:
task openVsSolution(type: Exec, dependsOn: setupVsSln) {
executable 'start'
environment = taskEnv
workingDir '../../src/solution'
args = ['some title for start', vsDevEnv, 'my.sln']
commandLine winCmdPrefix + executable + args
}
Everything works, start used the correct version of Visual Studio's devenv.exe, which varies based on the VC PlatformToolset, but...gradle sits in the background waiting for Visual Studio to close.
Why? How can I achieve all of the desired behavior?
Update
This might be an environmental problem. Apparently, on one dev's machine, the original gradle start task does stay open, as well. So, any invocation of start through gradle waits for it to complete, in a certain environment. The plot thickens...
Gradle is waiting for start to return is my guess, see this post - Run a background job from Gradle. Read also the comment after the answer, this is started from Java so if there is equivalent of & (run in background) for windows, it wouldn't work.
You can also try and wrapping it in a bat file, execute what you want in the bat file in the background then return right away so that gradle would be happy.
Hope this helps.

Nodejs - Windows Key/Certificate store

Does anybody have any pointers as to how I could go about interacting with the Key/Certificate store using nodeJs? I specifically want to add/remove certificates and possibly keys.
Update.
So the way to go here is to use "edge". Very nice work!
https://github.com/tjanczuk/edge
Without knowing too much about your setup here is a stab at a 'pointer' as to how to interact.
You could try using Nodes Child Process and then spawning out a process to the commandline and interact with the key/certificate store the way you would via command line. Microsofts certificate manager tool perhaps?
Rough example:
var exec = require('child_process').exec,
child;
child = exec('certmgr /add /all /c myFile.ext newFile.ext',
function (error, stdout, stderr) {
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
if (error !== null) {
console.log('exec error: ' + error);
}
});
I've just published node-windows-root-certs which uses ffi to read the windows root certificate store, and then apply these in nodejs... may provide some inspiration.
Example use to use Windows certificates rather than internal NodeJS certificates:
var windowsRootCerts = require('node-windows-root-certs');
// to read the windows root certs and patch in a single command:
windowsRootCerts.useWindowsCerts();
There is an npm package 'windows-certs' which uses edge and a .csx script to read certificates in .pem format
https://github.com/jfromaniello/node-windows-certs
https://www.npmjs.com/package/windows-certs
This should have the required functionality, but is marked as deprecated. The successor package is indicated as.win-ca. However, this seems to be lacking some of the functionality of the older package:
https://www.npmjs.com/package/win-ca
https://github.com/ukoloff/win-ca

Why am i getting a ENOTEMPTY error?

So i create a default meteor app.
Its runs great.
Now I add a simple insert in the startup function.
its is now giving me exceptions.
Here is my app.js code:
Book = new Meteor.Collection("book");
if (Meteor.isClient) {
Template.hello.greeting = function () {
return "Welcome to app_01.";
};
Template.hello.events({
'click input' : function () {
// template data, if any, is available in 'this'
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
if (Book.find().count() === 0) {
var names = ["Ada Lovelace",
"Grace Hopper",
"Marie Curie",
"Carl Friedrich Gauss",
"Nikola Tesla",
"Claude Shannon"];
for (var i = 0; i < names.length; i++)
Book.insert({name: names[i], score: Math.floor(Math.random()*10)*5});
}
});
}
And here is the exception I am getting:
No dependency info in bundle. Filesystem monitoring disabled.
Errors prevented startup:
Exception while bundling application:
Error: ENOTEMPTY, directory not empty 'C:\Users\Office\Workspace\Code\Meteor\app_01\.meteor\local\build\server'
at Object.fs.rmdirSync (fs.js:456:18)
at Object.module.exports.rm_recursive (C:\Program Files (x86)\Meteor\app\lib\files.js:256:10)
at C:\Program Files (x86)\Meteor\app\lib\files.js:254:15
at Array.forEach (native)
at Function._.each._.forEach (C:\Program Files (x86)\Meteor\lib\node_modules\underscore\underscore.js:79:11)
at Object.module.exports.rm_recursive (C:\Program Files (x86)\Meteor\app\lib\files.js:252:9)
at _.extend.write_to_directory (C:\Program Files (x86)\Meteor\app\lib\bundler.js:493:11)
at Object.exports.bundle (C:\Program Files (x86)\Meteor\app\lib\bundler.js:685:12)
at exports.run.restart_server (C:\Program Files (x86)\Meteor\app\meteor\run.js:615:26)
at C:\Program Files (x86)\Meteor\app\meteor\run.js:726:9
Please fix the problem and restart.
The console gives me no useful information at all.
Some more info:
I am using the windows version of meteor 0.5.4
My code has both tabs and spaces as indentation (should that be a problem?)
to add to my perplexity:
If i run the leaderboard example, it runs perfectly.
When I run the default project with my modified startup code, i get the exception. >.<
More info:
when the server crashed, the mongod service was still running in windows. In my infinite wisdom, i thought I'll kill it and maybe try a fresh restart of it.
now i get a new error:
PS C:\Users\Office\Workspace\Code\Meteor\app_01> meteor
[[[[[ C:\Users\Office\Workspace\Code\Meteor\app_01 ]]]]]
Unexpected mongo exit code 100. Restarting.
Unexpected mongo exit code 100. Restarting.
Unexpected mongo exit code 100. Restarting.
Can't start mongod
MongoDB had an unspecified uncaught exception.
Check to make sure that MongoDB is able to write to its database directory.
Edit: now deleted the .meteor/local/db contents. we are now back to the ENOTEMPTY error.
It is because the directory is not getting deleted during the bundling operation.
So when this happens, Stop your server with ctrl + c.
Then delete the contents of .meteor\local\db directory as well as .meteor\local\builds directory and run the server again using meteor command.
Not an ideal way, but it works.
Just tested this on my mac and on my Windows machine, it works fine for me. (Copied and pasted your code into app.js, did not change anything anywhere else, as I understood this is what you did).
There are two ways to go on:
You try meteor reset. Warning: This will empty any databases you had within this app, but I think there isn't anything all to important in there at the moment?
You delete the app and make a new one using meteor create appname, and copy the source code from above into app.js.
I think investing too much time and effort is not worth it, since it is not an app close to production stage. If, however, creating a new app causes the same problem, it will get interesting =)
EDIT: Just searched around and found this: https://github.com/TomWij/meteor/issues/18
Scroll down to the answer; apparently it might be your antivirus scanner preventing Meteor from rebuilding it. Is that possible?
Also, are you starting Meteor from anything else than cmd.exe? I just read through the issue on GitHub, and things seem to be pretty gnarly when using anything else than cmd.exe (eg the GitHub Bash Shell)
Once my pull request goes in, this issue should be resolved:
https://github.com/TomWij/meteor/pull/56

How can I use WebStorm to debug my node application across multiple processes?

My node application makes a call to child_process.exec, at which point I can't 'step into' each line of code any more, and none of the breakpoints in the node app being started by .exec seem to work.
How can I used WebStorm to debug this application as though calls to child_process.exec were like any other function call?
'exec' starts a new process so you need to pass '--debug=port' command line argument to it. Your code will look like child_process.exec('node --debug=8787 ' + __dirname + '/childProcess.js');
After that you can create a NodeJS Remote Debug configuration in Run | 'Edit Configurations' dialog and press 'Debug' button to connect to the child process.

Resources