Giter Club home page Giter Club logo

node-mac's Introduction

node-mac

NPM version NGN Dependencies Build

Sponsors (as of 2020)

Follow the author on Twitter (@goldglovecb).

This README provides a pretty good overview of what node-mac has to offer, but better documentation is now available at the node-mac documentation portal.

node-mac

This is a standalone module, originally designed for internal use in NGN. However; it is capable of providing the same features for Node.js scripts independently of NGN.

For alternative versions, see node-windows and node-linux

Overview

This module helps create/manage native processes and event logs for Node.js applications on Mac OSX.

Mac

Notice Some vesions of Node will not display the pretty title (Hello World) of the process. See the underlying issue in Node core. Instead, it just displays "node", but still functions normally.

To start, install node-mac via:

npm install node-mac

node-mac is a utility for creating/managing Node.js scripts as OSX daemons. Please note that like all OSX daemons, creating one requires sudo/root privileges. To create a service with node-mac, prepare a script like:

    var Service = require('node-mac').Service;

    // Create a new service object
    var svc = new Service({
      name:'Hello World',
      description: 'The nodejs.org example web server.',
      script: '/path/to/helloworld.js'
    });

    // Listen for the "install" event, which indicates the
    // process is available as a service.
    svc.on('install',function(){
      svc.start();
    });

    svc.install();

The code above creates a new Service object, providing a pretty name and description. The script attribute identifies the Node.js script that should run as a service. Upon running this, the script will be visible from the Activity Monitor.

The Service object emits the following events:

  • install - Fired when the script is installed as a service.
  • alreadyinstalled - Fired if the script is already known to be a service.
  • invalidinstallation - Fired if an installation is detected but missing required files.
  • uninstall - Fired when an uninstallation is complete.
  • start - Fired when the new service is started.
  • stop - Fired when the service is stopped.
  • error - Fired in some instances when an error occurs.

In the example above, the script listens for the install event. Since this event is fired when a service installation is complete, it is safe to start the service.

Services created by node-mac are similar to most other services running on OSX. They can be stopped from the Activity Monitor and make logs available in the Console app.

Environment Variables

Sometimes you may want to provide a service with static data, passed in on creation of the service. You can do this by setting environment variables in the service config, as shown below:

    var svc = new Service({
      name:'Hello World',
      description: 'The nodejs.org example web server.',
      script: '/path/to/helloworld.js',
      env: {
        name: "HOME",
        value: process.env["USERPROFILE"] // service is now able to access the user who created its home directory
      }
    });

You can also supply an array to set multiple environment variables:

    var svc = new Service({
      name:'Hello World',
      description: 'The nodejs.org example web server.',
      script: '/path/to/helloworld.js',
      env: [{
        name: "HOME",
        value: process.env["USERPROFILE"] // service is now able to access the user who created its home directory
      },
      {
        name: "TEMP",
        value: path.join(process.env["USERPROFILE"],"/temp") // use a temp directory in user's home directory
      }]
    });

Cleaning Up: Uninstall a Service

Uninstalling a previously created service is syntactically similar to installation.

    var Service = require('node-mac').Service;

    // Create a new service object
    var svc = new Service({
      name:'Hello World',
      script: require('path').join(__dirname,'helloworld.js')
    });

    // Listen for the "uninstall" event so we know when it's done.
    svc.on('uninstall',function(){
      console.log('Uninstall complete.');
      console.log('The service exists: ',svc.exists);
    });

    // Uninstall the service.
    svc.uninstall();

The uninstall process only removes process-specific files. It does NOT delete your Node.js script, but it will remove the logs! This process also removes the plist file for the service.

What Makes node-mac Services Unique?

Lots of things!

Long Running Processes & Monitoring:

The built-in service recovery for OSX services is fairly limited and cannot easily be configured from code. Therefore, node-mac creates a wrapper around the Node.js script. This wrapper is responsible for restarting a failed service in an intelligent and configurable manner. For example, if your script crashes due to an unknown error, node-mac will attempt to restart it. By default, this occurs every second. However; if the script has a fatal flaw that makes it crash repeatedly, it adds unnecessary overhead to the system. node-mac handles this by increasing the time interval between restarts and capping the maximum number of restarts.

Smarter Restarts That Won't Pummel Your Server:

Using the default settings, node-mac adds 25% to the wait interval each time it needs to restart the script. With the default setting (1 second), the first restart attempt occurs after one second. The second occurs after 1.25 seconds. The third after 1.56 seconds (1.25 increased by 25%) and so on. Both the initial wait time and the growth rate are configuration options that can be passed to a new Service. For example:

    var svc = new Service({
      name:'Hello World',
      description: 'The nodejs.org example web server.',
      script: '/path/to/helloworld.js',
      wait: 2,
      grow: .5
    });

In this example, the wait period will start at 2 seconds and increase by 50%. So, the second attempt would be 3 seconds later while the fourth would be 4.5 seconds later.

Don't DOS Yourself!

Repetitive recycling could potentially go on forever with a bad script. To handle these situations, node-mac supports two kinds of caps. Using maxRetries will cap the maximum total number of times the service restarts itself before it kills the process. By default, this is unlimited. Setting it to 3 would tell the process to stop restarting itself (i.e. leave the dead process alone) after it tries to restart it 3 times.

Another option is maxRestarts, which caps the number of restarts attempted within a 60 second period. For example, if this is set to 3 (the default) and the process crashes/restarts repeatedly, node-mac will stop restarting the process after the 3rd crash within a 60 second timeframe.

Both of these configuration options can be set, just like wait or grow.

Finally, an attribute called abortOnError can be set to true if you want your script to not restart at all when it exits with an error.

How Services Are Made

node-mac uses the launchd utility to create a unique process for each Node.js script deployed as a service. A plist file is created in /Library/LaunchDaemons by default. Additionally, two log files are generated in /Library/Logs/<name> for general output and error logging.

Event Logging

Mac log

Services created with node-mac have two event logs that can be viewed through the Console app. A log source named myappname.log provides basic logging for the service. It can be used to see when the entire service starts/stops. A second log, named myappname_error.log stores error output.

By default, any console.log, console.warn, console.error or other output will be made available in one of these two files.

License (MIT)

Copyright (c) 2013 Corey Butler

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

node-mac's People

Contributors

aziz avatar coreybutler avatar darinwarlingconcur avatar garaboncias avatar linonetwo avatar zmarouf avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

node-mac's Issues

The problem with process.cwd()

Some packages like connect-assets use process.cwd() to find files around the project.

When running as a service, the process.cwd() returns / instead of the __direname of the script file.

Is there a way to fix this easily?

Run as User Agent instead of global.

Hello,

I'm currently playing around with node-mac to run a background process which is watching some directories and uploads the content to a remote location.

As far as I understand node-mac is running the script I specify in script: 'script.js' as root. This causes some things to misbehave (scp looking into the root directory for public keys instead of the user home).

According to http://launchd.info/ there's also a way to have User Agents that are located in ~/Library/LaunchAgents and run as the currently logged in user.

Is this currently possible and I just haven't seen it or if it's not yet possible is this on the roadmap for a future release?

Thanks,

dewey

What about 'servicing' a bash script?

This is nice, but due to often configuration changes, we need to call the actual service with different parameters.
Hence, a generic option, would be to be able to register a bash script that would run

node our-sript --our-args

So every time you'd want to change the args, you just need to restart the service, or reboot the machine.

Can you please support that case as well? Thank you!

Permission error

I install my script like this:

sudo node scripts/installServer.js

with

const path = require('path');

const Service = require('node-mac').Service; // require('./node-mac-windows-linux')

const scriptsFolder = path.join(path.dirname(__filename));
const scriptPath = path.join(scriptsFolder, 'startAndWatchNodeJSWiki.js');

const service = new Service({
  name: 'TiddlyWiki',
  description: 'Local TiddlyWiki server for my knowledge management.',
  script: scriptPath,
});

service.on('install', () => {
  service.start();
  console.log(`${scriptPath} started.`)
});
service.on('alreadyinstalled', () => {
  console.log(`${scriptPath} already installed.`)
});
service.on('invalidinstallation', () => {
  console.log(`${scriptPath} is invalid.`)
});
service.on('uninstall', () => {
  console.log(`${scriptPath} is uninstalled.`)
});
service.on('error', () => {
  console.log(`${scriptPath} errors.`)
});

service.install();

and

/// node-mac-windows-linux.js
const path = require('path');
const os = require('os');
const execSync = require('child_process').execSync;
const { existsSync } = require('fs');

let Service;
if (os.platform() === 'darwin') {
  Service = require('node-mac').Service;
  if (!existsSync('/Library/Logs/TiddlyWiki')) {
    execSync('sudo mkdir -p /Library/Logs/TiddlyWiki && sudo chown -R $(whoami) /Library/Logs/TiddlyWiki');
  }
} else if (os.platform() === 'win32') {
  Service = require('node-windows').Service;
} else {
  Service = require('node-linux').Service;
}

module.exports = Service;

I got:

# TiddlyWiki Errorsinternal/fs/utils.js:230
    throw err;
    ^

Error: EPERM: operation not permitted, open '/xxx/node_modules/node-mac/lib/wrapper.js'
    at Object.openSync (fs.js:462:3)
    at Object.readFileSync (fs.js:364:35)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1166:22)
    at Module.load (internal/modules/cjs/loader.js:996:32)
    at Function.Module._load (internal/modules/cjs/loader.js:896:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
    at internal/main/run_main_module.js:17:47 {
  errno: -1,
  syscall: 'open',
  code: 'EPERM',
  path: '/xxx/node_modules/node-mac/lib/wrapper.js'
}
internal/fs/utils.js:230
    throw err;
    ^

Error: EPERM: operation not permitted, open '/xxx/node_modules/node-mac/lib/wrapper.js'
    at Object.openSync (fs.js:462:3)
    at Object.readFileSync (fs.js:364:35)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1166:22)
    at Module.load (internal/modules/cjs/loader.js:996:32)
    at Function.Module._load (internal/modules/cjs/loader.js:896:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
    at internal/main/run_main_module.js:17:47 {
  errno: -1,
  syscall: 'open',
  code: 'EPERM',
  path: '/xxx/node_modules/node-mac/lib/wrapper.js'
}

Path error

When I try to install the service I get this message:

TypeError: Path must be a string. Received undefined
at assertPath (path.js:7:11)
at Object.dirname (path.js:1326:5)
at new daemon (/Users/stephan/nodejs/uwz/node_modules/node-mac/lib/daemon.js:124:30)
at repl:1:11
at sigintHandlersWrap (vm.js:32:31)
at sigintHandlersWrap (vm.js:96:12)
at ContextifyScript.Script.runInContext (vm.js:31:12)
at REPLServer.defaultEval (repl.js:308:29)
at bound (domain.js:280:14)
at REPLServer.runBound as eval

Here's the code I wrote:

var svc = new Service({
        name:'UWZ',
        description: 'UWZ Logger.',
        script: '/Users/stephan/nodejs/uwz/server.js'
});

Run as normal user privilege

Currently, it is run as root privilege, this cause following problem when I use node-mac to run git related script:

Git commit show as System Adminstrator
Screen Shot 2020-05-06 at 1 07 27 PM

Can't commit by hand because lack of privilege, I have to reset privilege every time:
sudo chown -R $(whoami) /xxx/repo

Can't push: simonthum/git-sync#17

SO how to run a task as normal user privilege?

Does this need sudo?

I got

Error: EACCES: permission denied, mkdir '/Library/Logs/TiddlyWiki'
    at Object.mkdirSync (fs.js:887:3)
    at daemon.value [as install] (/xxx/node_modules/node-mac/lib/daemon.js:290:9)

What should I do If I want this install process more automatic(without prompt for password) Can I change log dir to /tmp ?

Service Not Responding

Install works OK and I can see the service in the Activity Monitor. However after 30 minutes or so it turns red and says "(Not Responding)".

When I reboot my Mac the service starts but it doesn't appear in the Activity Monitor.

Running Mavericks.

How to install node-webkit app as a daemon using node-mac?

Hello,

Is it possible to install a node-webkit app as a service using node-mac? I've tried to run the ff script:

var Service = require('node-mac').Service;

// Create a new service object
var svc = new Service({
  name: 'TestService',
  description: 'Test Service.',
  script: '/path/to/test.app',
});

svc.on('install',function(){
  svc.start(); 
});

svc.install();

It register the service successfully but it has some error:

module.js:340
    throw err;
          ^
Error: Cannot find module '/path/to/test.app'
    at Function.Module._resolveFilename (module.js:338:15)
    at Function.Module._load (module.js:280:25)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:902:3

Too many restarts within the last 60 seconds. Please check the script.

Any thoughts on how to run a .app or perhaps .exe?

Thanks.

Example code not working on latest macOS

The terminal says

Installation Complete
---------------------
Hello World started!
Visit http://127.0.0.1:3000 to see it in action.

But I couldn't find anything in the activity monitor also, I am just running the example code and there is no log in the ~/Library/Logs
and plist in ~/Library/LaunchAgents

When I run hello world with node the server works.

here is the screenshot
image

How to run coffeescript code?

Can not run my server.coffee as a service.
for script I'm doing something like this:

require('path').join(__dirname, '/node_modules/.bin/coffee') + ' server.coffee'

Is there anything wrong with that?

service = new ServiceBuilder({
  name: "nodejs SANDBOX",
  description: "my nodejs sandbox app running on localhost:9999",
  script: require('path').join(__dirname, '/node_modules/.bin/coffee') + ' server.coffee'
})

appear in activity monitor?

Thanks for this - great work. One quick question - if i launch directly from terminal - the service appears in activity monitor, but on restart it doesn't. The service is definitely running - but wondering if it is possible to make it appear in activity monitor.

thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.