Skip to content

Commit

Permalink
Update version to 1.1.0 in package.json, Update README.md for 1.1.0, …
Browse files Browse the repository at this point in the history
…Migrate C++ addon code to use the Native Abstractions for Node framework, Add missing shebang line '#!' to start/stop script template, Not possible to specify run levels for Linux start/stop script (added new `runLevels` item to the `options` parameter to the `add()` function
  • Loading branch information
Stephen Vickers committed Oct 9, 2015
1 parent 2642977 commit 7cec373
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 93 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
syntax: glob
build
example/*.log
node_modules/
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,9 @@ The `name` parameter specifies the name of the created service. The optional
of `process.argv[1]`
* `programArgs` - An array of strings specifying parameters to pass to
`programPath`, defaults to `[]`
* `runLevels` - An array of numbers specifying Linux run-levels at which
the service should be started for Linux platforms, defaults to
`[2, 3, 4, 5]`

The service will be set to automatically start at boot time, but not started.
The service can be started using the `net start "my-service"` command on
Expand Down Expand Up @@ -323,6 +326,13 @@ Bug reports should be sent to <[email protected]>.

* Host repository on GitHub

## Version 1.1.0 - 09/10/2015

* Migrate C++ addon code to use the Native Abstractions for Node framework
* Add missing shebang line '#!' to start/stop script template
* Not possible to specify run levels for Linux start/stop script (added new
`runLevels` item to the `options` parameter to the `add()` function

# Roadmap

Suggestions and requirements should be sent to <[email protected]>.
Expand Down
3 changes: 3 additions & 0 deletions binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
'targets': [
{
'target_name': 'service',
"include_dirs" : [
"<!(node -e \"require('nan')\")"
],
'conditions' : [
['OS=="win"', {
'libraries' : ['advapi32.lib'],
Expand Down
12 changes: 10 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@ var serviceWrap;
var runInitialised = false;

var linuxStartStopScript = [
'#!/bin/bash',
'',
'### BEGIN INIT INFO',
'# Provides: ##NAME##',
'# Required-Start: ',
'# Required-Stop: ',
'# Default-Start: 2 3 4 5',
'# Default-Start: ##RUN_LEVELS_ARR##',
'# Default-Stop: 0 1 6',
'# Short-Description: Start ##NAME## at boot time',
'# Description: Enable ##NAME## service.',
'### END INIT INFO',
'',
'# chkconfig: 2345 99 1',
'# chkconfig: ##RUN_LEVELS_STR## 99 1',
'# description: ##NAME##',
'',
'set_pid () {',
Expand Down Expand Up @@ -197,6 +199,10 @@ function add (name, options, cb) {
if (options && options.programArgs)
for (var i = 0; i < options.programArgs.length; i++)
programArgs.push ("\"" + options.programArgs[i] + "\"");

var runLevels = [2, 3, 4, 5];
if (options && options.runLevels)
runLevels = options.runLevels;

var nodeArgsStr = nodeArgs.join(" ");
var programArgsStr = programArgs.join(" ");
Expand All @@ -211,6 +217,8 @@ function add (name, options, cb) {
line = line.replace("##NODE_ARGS##", nodeArgsStr);
line = line.replace("##PROGRAM_PATH##", programPath);
line = line.replace("##PROGRAM_ARGS##", programArgsStr);
line = line.replace("##RUN_LEVELS_ARR##", runLevels.join(" "));
line = line.replace("##RUN_LEVELS_STR##", runLevels.join(""));

startStopScript.push(line);
}
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
{
"name": "os-service",
"version": "1.0.3",
"version": "1.1.0",
"description": "Run Node.JS programs as native Operating System Services.",
"main": "index.js",
"directories": {
"example": "example"
},
"dependencies": {},
"dependencies": {
"nan": "2.0.x"
},
"contributors": [
{
"name": "Stephen Vickers",
Expand Down
174 changes: 91 additions & 83 deletions src/service.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define SERVICE_CC

#include <node.h>
#include <nan.h>

#include <io.h>
#include <signal.h>
Expand Down Expand Up @@ -110,64 +111,67 @@ DWORD __stdcall run_thread (LPVOID param) {

namespace service {

void InitAll (Handle<Object> target) {
pthread_mutex_init (&status_handle_mtx, NULL);
pthread_mutex_init (&stop_requested_mtx, NULL);
pthread_mutex_init (&stop_service_mtx, NULL);
void InitAll (Handle<Object> exports) {
pthread_mutex_init(&status_handle_mtx, NULL);
pthread_mutex_init(&stop_requested_mtx, NULL);
pthread_mutex_init(&stop_service_mtx, NULL);

pthread_cond_init (&stop_service, NULL);

target->Set (String::NewSymbol ("add"),
FunctionTemplate::New (Add)->GetFunction ());
target->Set (String::NewSymbol ("isStopRequested"),
FunctionTemplate::New (IsStopRequested)->GetFunction ());
target->Set (String::NewSymbol ("remove"),
FunctionTemplate::New (Remove)->GetFunction ());
target->Set (String::NewSymbol ("run"),
FunctionTemplate::New (Run)->GetFunction ());
target->Set (String::NewSymbol ("stop"),
FunctionTemplate::New (Stop)->GetFunction ());
pthread_cond_init(&stop_service, NULL);

exports->Set(Nan::New("add").ToLocalChecked(),
Nan::GetFunction(Nan::New<FunctionTemplate>(Add)).ToLocalChecked());

exports->Set(Nan::New("isStopRequested").ToLocalChecked(),
Nan::GetFunction(Nan::New<FunctionTemplate>(IsStopRequested)).ToLocalChecked());

exports->Set(Nan::New("remove").ToLocalChecked(),
Nan::GetFunction(Nan::New<FunctionTemplate>(Remove)).ToLocalChecked());

exports->Set(Nan::New("run").ToLocalChecked(),
Nan::GetFunction(Nan::New<FunctionTemplate>(Run)).ToLocalChecked());

exports->Set(Nan::New("stop").ToLocalChecked(),
Nan::GetFunction(Nan::New<FunctionTemplate>(Stop)).ToLocalChecked());
}

NODE_MODULE(service, InitAll)

Handle<Value> Add (const Arguments& args) {
HandleScope scope;
NAN_METHOD(Add) {
Nan::HandleScope scope;

if (args.Length () < 3) {
ThrowException (Exception::Error (String::New (
"Two arguments are required")));
return scope.Close (args.This ());
if (info.Length() < 3) {
Nan::ThrowError("Two arguments are required");
return;
}

if (! args[0]->IsString ()) {
ThrowException (Exception::TypeError (String::New (
"Name argument must be a string")));
return scope.Close (args.This ());
if (! info[0]->IsString()) {
Nan::ThrowTypeError("Name argument must be a string");
return;
}
String::AsciiValue name (args[0]->ToString ());

Nan::Utf8String name(info[0]);

if (! args[1]->IsString ()) {
ThrowException (Exception::TypeError (String::New (
"Display name argument must be a string")));
return scope.Close (args.This ());
if (! info[1]->IsString ()) {
Nan::ThrowTypeError("Display name argument must be a string");
return;
}
String::AsciiValue display_name (args[1]->ToString ());

Nan::Utf8String display_name(info[1]);

if (! args[2]->IsString ()) {
ThrowException (Exception::TypeError (String::New (
"Name argument must be a string")));
return scope.Close (args.This ());
if (! info[2]->IsString ()) {
Nan::ThrowTypeError("Name argument must be a string");
return;
}
String::AsciiValue path (args[2]->ToString ());

Nan::Utf8String path(info[2]);

SC_HANDLE scm_handle = OpenSCManager (0, SERVICES_ACTIVE_DATABASE,
SC_MANAGER_ALL_ACCESS);
if (! scm_handle) {
std::string message ("OpenSCManager() failed: ");
message += raw_strerror (GetLastError ());
ThrowException (Exception::Error (String::New (message.c_str ())));
return scope.Close (args.This ());
Nan::ThrowError(message.c_str());
return;
}

SC_HANDLE svc_handle = CreateService (scm_handle, *name, *display_name,
Expand All @@ -178,113 +182,117 @@ Handle<Value> Add (const Arguments& args) {
std::string message ("CreateService() failed: ");
message += raw_strerror (GetLastError ());
CloseServiceHandle (scm_handle);
ThrowException (Exception::Error (String::New (message.c_str ())));
return scope.Close (args.This ());
Nan::ThrowError(message.c_str());
return;
}

CloseServiceHandle (svc_handle);
CloseServiceHandle (scm_handle);

return scope.Close (args.This ());
info.GetReturnValue().Set(info.This());
}

Handle<Value> IsStopRequested (const Arguments& args) {
HandleScope scope;
NAN_METHOD(IsStopRequested) {
Nan::HandleScope scope;

pthread_mutex_lock (&stop_requested_mtx);
Handle<Boolean> requested = Boolean::New (stop_requested);
bool requested = stop_requested ? true : false;
stop_requested = false;
pthread_mutex_unlock (&stop_requested_mtx);

return scope.Close (requested);
info.GetReturnValue().Set(requested);
}

Handle<Value> Remove (const Arguments& args) {
HandleScope scope;
NAN_METHOD(Remove) {
Nan::HandleScope scope;

if (args.Length () < 1) {
ThrowException (Exception::Error (String::New (
"One argument is required")));
return scope.Close (args.This ());
if (info.Length () < 1) {
Nan::ThrowError("One argument is required");
return;
}

if (! args[0]->IsString ()) {
ThrowException (Exception::TypeError (String::New (
"Name argument must be a string")));
return scope.Close (args.This ());
if (! info[0]->IsString ()) {
Nan::ThrowTypeError("Name argument must be a string");
return;
}
String::AsciiValue name (args[0]->ToString ());

Nan::Utf8String name(info[0]);

SC_HANDLE scm_handle = OpenSCManager (0, SERVICES_ACTIVE_DATABASE,
SC_MANAGER_ALL_ACCESS);
if (! scm_handle) {
std::string message ("OpenSCManager() failed: ");
message += raw_strerror (GetLastError ());
ThrowException (Exception::Error (String::New (message.c_str ())));
return scope.Close (args.This ());
Nan::ThrowError(message.c_str());
return;
}

SC_HANDLE svc_handle = OpenService (scm_handle, *name, SERVICE_ALL_ACCESS);
if (! svc_handle) {
std::string message ("OpenService() failed: ");
message += raw_strerror (GetLastError ());
CloseServiceHandle (scm_handle);
ThrowException (Exception::Error (String::New (message.c_str ())));
return scope.Close (args.This ());
Nan::ThrowError(message.c_str());
return;
}

if (! DeleteService (svc_handle)) {
std::string message ("DeleteService() failed: ");
message += raw_strerror (GetLastError ());
CloseServiceHandle (svc_handle);
CloseServiceHandle (scm_handle);
ThrowException (Exception::Error (String::New (message.c_str ())));
return scope.Close (args.This ());
Nan::ThrowError(message.c_str());
return;
}

CloseServiceHandle (svc_handle);
CloseServiceHandle (scm_handle);

return scope.Close (args.This ());
info.GetReturnValue().Set(info.This());
}

Handle<Value> Run (const Arguments& args) {
HandleScope scope;
NAN_METHOD(Run) {
Nan::HandleScope scope;

if (run_initialised)
return scope.Close (args.This ());
if (run_initialised) {
info.GetReturnValue().Set(info.This());
return;
}

node_thread_handle = GetCurrentThread ();

HANDLE handle = CreateThread (NULL, 0, run_thread, NULL, 0, NULL);
if (! handle) {
std::string message ("CreateThread() failed: ");
message += raw_strerror (GetLastError ());
ThrowException (Exception::Error (String::New (message.c_str ())));
return scope.Close (args.This ());
Nan::ThrowError(message.c_str());
return;
} else {
CloseHandle (handle);
}

run_initialised = true;

return scope.Close (args.This ());
info.GetReturnValue().Set(info.This());
}

Handle<Value> Stop (const Arguments& args) {
HandleScope scope;
NAN_METHOD(Stop) {
Nan::HandleScope scope;

if (! run_initialised)
return scope.Close (args.This ());
if (! run_initialised) {
info.GetReturnValue().Set(info.This());
return;
}

int rcode = 0;
if (args.Length () > 1) {
if (! args[0]->IsUint32 ()) {
ThrowException (Exception::TypeError (String::New (
"Name argument must be a string")));
return scope.Close (args.This ());

if (info.Length () > 1) {
if (! info[0]->IsUint32 ()) {
Nan::ThrowTypeError("Name argument must be a string");
return;
}
rcode = args[0]->ToUint32 ()->Value ();

rcode = info[0]->ToUint32()->Value();
}

set_status (SERVICE_STOP_PENDING, NO_ERROR, 0);
Expand All @@ -293,7 +301,7 @@ Handle<Value> Stop (const Arguments& args) {

set_status (SERVICE_STOPPED, NO_ERROR, rcode);

return scope.Close (args.This ());
info.GetReturnValue().Set(info.This());
}

}; /* namespace service */
Expand Down
Loading

0 comments on commit 7cec373

Please sign in to comment.