├── pm2 ├── LICENSE ├── bin │ ├── pm2-dev │ ├── pm2-docker │ └── pm2-runtime ├── .gitattributes ├── lib │ ├── templates │ │ ├── ecosystem-simple.tpl │ │ ├── Dockerfiles │ │ │ ├── Dockerfile-nodejs.tpl │ │ │ ├── Dockerfile-java.tpl │ │ │ └── Dockerfile-ruby.tpl │ │ ├── logrotate.d │ │ │ └── pm2 │ │ ├── init-scripts │ │ │ ├── systemd.tpl │ │ │ ├── systemd-online.tpl │ │ │ ├── rcd.tpl │ │ │ ├── launchd.tpl │ │ │ ├── openrc.tpl │ │ │ ├── pm2-init-amazon.sh │ │ │ └── upstart.tpl │ │ └── ecosystem.tpl │ ├── API │ │ ├── interpreter.json │ │ ├── Keymetrics │ │ │ ├── motd │ │ │ └── kmapi.js │ │ ├── Spinner.js │ │ ├── schema.json │ │ ├── Deploy.js │ │ ├── Interaction.js │ │ ├── Modules │ │ │ ├── Modularizerv1.js │ │ │ └── Modules.js │ │ └── Monit.js │ ├── motd.update │ ├── tools │ │ ├── IsAbsolute.js │ │ ├── open.js │ │ ├── fmt.js │ │ ├── promise.min.js │ │ └── isbinaryfile.js │ ├── Interactor │ │ ├── internal-ip.js │ │ ├── RemoteActions │ │ │ ├── ScopedExecution.js │ │ │ └── CustomActions.js │ │ ├── Cipher.js │ │ ├── WatchDog.js │ │ ├── HttpRequest.js │ │ ├── Password.js │ │ ├── pm2-interface.js │ │ ├── Filter.js │ │ └── ReverseInteractor.js │ ├── Event.js │ ├── completion.sh │ ├── motd │ ├── HttpInterface.js │ ├── ProcessContainerFork.js │ ├── TreeKill.js │ ├── binaries │ │ ├── Runtime.js │ │ ├── DevCLI.js │ │ └── Runtime4Docker.js │ ├── Watcher.js │ ├── God │ │ ├── ClusterMode.js │ │ └── Reload.js │ └── Worker.js ├── index.js ├── .travis.yml ├── types │ └── tsconfig.json ├── Makefile ├── changelogTemplate.md ├── .drone.yml ├── .changelogrc ├── CONTRIBUTING.md ├── paths.js ├── constants.js └── package.json ├── pm2-app ├── pm2 ├── startDaemon ├── package.json ├── app.json ├── app2.json ├── pm2_connect.js ├── app.js └── app2.js ├── test-script ├── nodeshell.js ├── os.js ├── spawn ├── mainProcess.js └── node-version.js ├── .gitignore ├── spawn ├── ProcessContainer.js ├── unit.js ├── spawn.js ├── app.js └── processContainerFork.js ├── cluster ├── ProcessContainer.js ├── cluster1-unit.js ├── cluster2-unit.js ├── cluster3.js ├── cluster.js ├── cluster2.js └── app.js ├── startServer.sh ├── package.json ├── tagg.js ├── npm-debug.log.2939786434 ├── v8profiler └── index.js ├── axon ├── pub-emitter │ ├── axon.js │ └── axon-client.js ├── req │ ├── req.js │ └── rep.js └── pm2-axon │ ├── req.js │ └── rep.js ├── node-require ├── b.js └── a.js ├── eventeniiter2 └── index.js ├── demain └── domain.js └── vizion └── index.js /pm2/LICENSE: -------------------------------------------------------------------------------- 1 | GNU-AGPL-3.0.txt 2 | -------------------------------------------------------------------------------- /pm2-app/pm2: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | DEBUG=pm2-read ../pm2/bin/pm2 $* -------------------------------------------------------------------------------- /pm2-app/startDaemon: -------------------------------------------------------------------------------- 1 | DEBUG=pm2-read node ../pm2/lib/Daemon.js 2 | -------------------------------------------------------------------------------- /test-script/nodeshell.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | console.log(process.pid) 4 | -------------------------------------------------------------------------------- /pm2/bin/pm2-dev: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require('../lib/binaries/DevCLI.js'); 4 | -------------------------------------------------------------------------------- /pm2/.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.sh eol=lf 3 | bin/** eol=lf 4 | test/fixtures/** eol=lf 5 | -------------------------------------------------------------------------------- /pm2/bin/pm2-docker: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require('../lib/binaries/Runtime4Docker.js'); 4 | -------------------------------------------------------------------------------- /pm2/bin/pm2-runtime: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require('../lib/binaries/Runtime4Docker.js'); 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | node_modules/* 3 | .idea 4 | .cache 5 | npm-debug.log 6 | .DS_Store 7 | .DS_Store/* -------------------------------------------------------------------------------- /test-script/os.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jianchen on 2018/5/10. 3 | */ 4 | let os = require('os'); 5 | 6 | 7 | console.log(os.cpus().length) -------------------------------------------------------------------------------- /pm2/lib/templates/ecosystem-simple.tpl: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | apps : [{ 3 | name : "app1", 4 | script : "./app.js" 5 | }] 6 | } 7 | -------------------------------------------------------------------------------- /pm2/lib/templates/Dockerfiles/Dockerfile-nodejs.tpl: -------------------------------------------------------------------------------- 1 | FROM keymetrics/pm2:latest 2 | 3 | RUN mkdir -p /var/app 4 | 5 | WORKDIR /var/app 6 | 7 | COPY ./package.json /var/app 8 | RUN npm install 9 | -------------------------------------------------------------------------------- /pm2/lib/templates/Dockerfiles/Dockerfile-java.tpl: -------------------------------------------------------------------------------- 1 | FROM anapsix/alpine-java:latest 2 | 3 | RUN apk update && apk add git && rm -rf /var/cache/apk/* 4 | RUN npm install pm2@next -g 5 | RUN mkdir -p /var/app 6 | 7 | WORKDIR /var/app 8 | -------------------------------------------------------------------------------- /pm2/lib/templates/Dockerfiles/Dockerfile-ruby.tpl: -------------------------------------------------------------------------------- 1 | FROM anapsix/alpine-ruby:latest 2 | 3 | RUN apk update && apk add git && rm -rf /var/cache/apk/* 4 | RUN npm install pm2@next -g 5 | RUN mkdir -p /var/app 6 | 7 | WORKDIR /var/app 8 | -------------------------------------------------------------------------------- /pm2-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pm2-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "scripts": { 6 | "daemon": "./startDaemon", 7 | "app": "./pm2 start app.json" 8 | }, 9 | "author": "", 10 | "license": "ISC" 11 | } 12 | -------------------------------------------------------------------------------- /spawn/ProcessContainer.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | 3 | let execfile = path.resolve(__dirname,'app.js'); 4 | require.main.filename = execfile; 5 | process.title = 'node app.js --name spawn' 6 | 7 | // Resets global paths for require() 8 | require('module')._initPaths(); -------------------------------------------------------------------------------- /cluster/ProcessContainer.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | 3 | process.title = 'node app.js --name cluster3' 4 | 5 | let execfile = path.resolve(__dirname,'app.js'); 6 | require.main.filename = execfile; 7 | 8 | // Resets global paths for require() 9 | require('module')._initPaths(); -------------------------------------------------------------------------------- /pm2/lib/templates/logrotate.d/pm2: -------------------------------------------------------------------------------- 1 | %HOME_PATH%/pm2.log %HOME_PATH%/logs/*.log { 2 | rotate 12 3 | weekly 4 | missingok 5 | notifempty 6 | compress 7 | delaycompress 8 | copytruncate 9 | create 0640 %USER% %USER% 10 | } 11 | -------------------------------------------------------------------------------- /spawn/unit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jianchen on 2018/5/11. 3 | */ 4 | const axios = require('axios'); 5 | 6 | setInterval(() => { 7 | axios.post('http://localhost:8001').then((res) => { 8 | console.log(res.data); 9 | }).catch((e) => { 10 | console.log(e) 11 | }) 12 | }, 500); -------------------------------------------------------------------------------- /cluster/cluster1-unit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jianchen on 2018/5/11. 3 | */ 4 | const axios = require('axios'); 5 | 6 | setInterval(() => { 7 | axios.post('http://localhost:8000').then((res) => { 8 | console.log(res.data); 9 | }).catch((e) => { 10 | console.log(e) 11 | }) 12 | }, 500); -------------------------------------------------------------------------------- /cluster/cluster2-unit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jianchen on 2018/5/11. 3 | */ 4 | const axios = require('axios'); 5 | 6 | setInterval(() => { 7 | axios.post('http://localhost:8001').then((res) => { 8 | console.log(res.data); 9 | }).catch((e) => { 10 | console.log(e) 11 | }) 12 | }, 500); -------------------------------------------------------------------------------- /pm2/lib/API/interpreter.json: -------------------------------------------------------------------------------- 1 | { 2 | ".sh" : "bash", 3 | ".py" : "python", 4 | ".rb" : "ruby", 5 | ".php" : "php", 6 | ".pl" : "perl", 7 | ".js" : "node", 8 | ".coffee" : "coffee", 9 | ".ls" : "lsc", 10 | ".ts" : "ts-node", 11 | ".tsx" : "ts-node" 12 | } 13 | -------------------------------------------------------------------------------- /startServer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export NVM_DIR="$HOME/.nvm" 3 | [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" 4 | 5 | echo 'change node version' 6 | nvm use v8.6.0 7 | echo 'check node version' 8 | node --version 9 | echo 'change work path' 10 | cd /Users/jianchen/workspace/react-webstorm/helloka2 11 | npm start -------------------------------------------------------------------------------- /test-script/spawn: -------------------------------------------------------------------------------- 1 | const { spawn } = require('child_process'); 2 | const ls = spawn('ls', ['-lh', '/usr']); 3 | 4 | ls.stdout.on('data', (data) => { 5 | console.log(`输出:${data}`); 6 | }); 7 | 8 | ls.stderr.on('data', (data) => { 9 | console.log(`错误:${data}`); 10 | }); 11 | 12 | ls.on('close', (code) => { 13 | console.log(`子进程退出码:${code}`); 14 | }); -------------------------------------------------------------------------------- /pm2/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 the PM2 project authors. All rights reserved. 3 | * Use of this source code is governed by a license that 4 | * can be found in the LICENSE file. 5 | */ 6 | 7 | process.env.PM2_PROGRAMMATIC = 'true'; 8 | 9 | var API = require('./lib/API.js'); 10 | 11 | module.exports = new API; 12 | module.exports.custom = API; 13 | -------------------------------------------------------------------------------- /pm2/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "10" 4 | - "4" 5 | - "6" 6 | - "8" 7 | - "0.12" 8 | os: 9 | - linux 10 | before_install: 11 | - sudo apt-get -qq update 12 | - sudo apt-get install python3 13 | - sudo apt-get install php5-cli 14 | services: 15 | - docker 16 | notifications: 17 | slack: pm2-nodejs:5Lolyw2LMnwy8fziqOGILQxG 18 | -------------------------------------------------------------------------------- /pm2/lib/API/Keymetrics/motd: -------------------------------------------------------------------------------- 1 | __ __ __ _ 2 | / //_/__ __ ______ ___ ___ / /______(_)_________ 3 | / ,< / _ \/ / / / __ `__ \/ _ \/ __/ ___/ / ___/ ___/ 4 | / /| / __/ /_/ / / / / / / __/ /_/ / / / /__(__ ) 5 | /_/ |_\___/\__, /_/ /_/ /_/\___/\__/_/ /_/\___/____/ 6 | /____/ 7 | 8 | Harden your Node.js application, today 9 | -------------------------------------------------------------------------------- /pm2-app/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "apps": [{ 3 | "name": "app", 4 | "script": "./app.js", 5 | "node_args": "--harmony", 6 | "autorestart": true, 7 | "watch": false, 8 | "ignore_watch" : ["public"], 9 | "exec_interpreter": "node", 10 | "instances" : 3, 11 | "exec_mode": "cluster", 12 | "log_date_format": "YYYY - MM - DD HH: mm Z", 13 | "env": { 14 | "NODE_ENV": "dev" 15 | } 16 | }] 17 | } 18 | -------------------------------------------------------------------------------- /pm2-app/app2.json: -------------------------------------------------------------------------------- 1 | { 2 | "apps": [{ 3 | "name": "app2", 4 | "script": "./app2.js", 5 | "node_args": "--harmony", 6 | "autorestart": true, 7 | "watch": false, 8 | "ignore_watch" : ["public"], 9 | "exec_interpreter": "node", 10 | "instances" : 3, 11 | "exec_mode": "cluster", 12 | "log_date_format": "YYYY - MM - DD HH: mm Z", 13 | "env": { 14 | "NODE_ENV": "dev" 15 | } 16 | }] 17 | } 18 | -------------------------------------------------------------------------------- /cluster/cluster3.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jianchen on 2018/5/11. 3 | */ 4 | let cluster = require('cluster'); 5 | let http = require('http'); 6 | let numCPUs = require("os").cpus().length; 7 | let path = require('path') 8 | 9 | cluster.setupMaster({ 10 | windowsHide: true, // windows系统创建的进程关闭其console出来的内容 11 | exec : path.resolve(__dirname,'ProcessContainer.js') 12 | }); 13 | 14 | for (let i = 0; i < numCPUs; i++) { 15 | cluster.fork(); 16 | } -------------------------------------------------------------------------------- /cluster/cluster.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jianchen on 2018/5/11. 3 | */ 4 | let cluster = require('cluster'); 5 | let http = require('http'); 6 | let numCPUs = require("os").cpus().length; 7 | 8 | if (cluster.isMaster) { 9 | for (let i = 0; i < numCPUs; i++) { 10 | cluster.fork(); 11 | } 12 | } else { 13 | http.createServer(function(req, res) { 14 | res.writeHead(200); 15 | res.end('process ' + process.pid + 'port: 8000 says hello!'); 16 | }).listen(8000); 17 | } -------------------------------------------------------------------------------- /cluster/cluster2.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jianchen on 2018/5/11. 3 | */ 4 | let cluster = require('cluster'); 5 | let http = require('http'); 6 | let numCPUs = require("os").cpus().length; 7 | 8 | if (cluster.isMaster) { 9 | for (let i = 0; i < numCPUs; i++) { 10 | cluster.fork(); 11 | } 12 | } else { 13 | http.createServer(function(req, res) { 14 | res.writeHead(200); 15 | res.end('process ' + process.pid + ' port: 8001 says hello!'); 16 | }).listen(8001); 17 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodeprocess", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "mainProcess.js", 6 | "scripts": { 7 | "start": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "axios": "^0.18.0", 13 | "axon": "2.0.3", 14 | "os": "^0.1.1", 15 | "pm2": "^2.10.3", 16 | "threads_a_gogo": "^0.1.13", 17 | "v8-profiler": "^5.7.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /pm2/types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "lib": ["es6"], 5 | "noImplicitAny": true, 6 | "noImplicitThis": true, 7 | "strictNullChecks": true, 8 | 9 | // If the library is an external module (uses `export`), this allows your test file to import "mylib" instead of "./index". 10 | // If the library is global (cannot be imported via `import` or `require`), leave this out. 11 | "baseUrl": ".", 12 | "paths": { "mylib": ["."] } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /pm2/lib/templates/init-scripts/systemd.tpl: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=PM2 process manager 3 | Documentation=https://pm2.keymetrics.io/ 4 | After=network.target 5 | 6 | [Service] 7 | Type=forking 8 | User=%USER% 9 | LimitNOFILE=infinity 10 | LimitNPROC=infinity 11 | LimitCORE=infinity 12 | Environment=PATH=%NODE_PATH%:/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin 13 | Environment=PM2_HOME=%HOME_PATH% 14 | PIDFile=%HOME_PATH%/pm2.pid 15 | 16 | ExecStart=%PM2_PATH% resurrect 17 | ExecReload=%PM2_PATH% reload all 18 | ExecStop=%PM2_PATH% kill 19 | 20 | [Install] 21 | WantedBy=multi-user.target 22 | -------------------------------------------------------------------------------- /pm2/lib/motd.update: -------------------------------------------------------------------------------- 1 | 2 | ------------- 3 | __ __ __ _ 4 | / //_/__ __ ______ ___ ___ / /______(_)_________ 5 | / ,< / _ \/ / / / __ `__ \/ _ \/ __/ ___/ / ___/ ___/ 6 | / /| / __/ /_/ / / / / / / __/ /_/ / / / /__(__ ) 7 | /_/ |_\___/\__, /_/ /_/ /_/\___/\__/_/ /_/\___/____/ 8 | /____/ 9 | 10 | Start monitoring your Node.js application today 11 | 12 | $ pm2 monitor 13 | 14 | To know more about the PM2 monitoring solution: 15 | 16 | https://keymetrics.io/ 17 | 18 | ------------- 19 | -------------------------------------------------------------------------------- /pm2/Makefile: -------------------------------------------------------------------------------- 1 | 2 | gen_deb: 3 | ./packager/build-dist.sh 4 | ./packager/build-deb.sh 5 | 6 | publish: 7 | ./packager/deploy.sh 8 | 9 | clean: 10 | find node_modules \( -name "example" -o -name "examples" -o -name "docs" -o -name "jsdoc" -o -name "jsdocs" -o -name "test" -o -name "tests" -o -name "*\.md" -o -name "*\.html" -o -name "*\.eot" -o -name "*\.svg" -o -name "*\.woff" \) -print -exec rm -rf {} \; 11 | find node_modules -type d \( -name "example" -o -name "examples" -o -name "docs" -o -name "jsdoc" -o -name "jsdocs" -o -name "test" -o -name "tests" -o -name "*\.md" -o -name "*\.html" -o -name "*\.eot" -o -name "*\.svg" -o -name "*\.woff" \) -print -exec rm -rf {} \; 12 | -------------------------------------------------------------------------------- /pm2/lib/templates/init-scripts/systemd-online.tpl: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=PM2 process manager 3 | Documentation=https://pm2.keymetrics.io/ 4 | After=network.target network-online.target 5 | Wants=network-online.target 6 | 7 | [Service] 8 | Type=forking 9 | User=%USER% 10 | LimitNOFILE=infinity 11 | LimitNPROC=infinity 12 | LimitCORE=infinity 13 | Environment=PATH=%NODE_PATH%:/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin 14 | Environment=PM2_HOME=%HOME_PATH% 15 | PIDFile=%HOME_PATH%/pm2.pid 16 | 17 | ExecStart=%PM2_PATH% resurrect 18 | ExecReload=%PM2_PATH% reload all 19 | ExecStop=%PM2_PATH% kill 20 | 21 | [Install] 22 | WantedBy=multi-user.target network-online.target 23 | -------------------------------------------------------------------------------- /pm2/lib/tools/IsAbsolute.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function posix(path) { 4 | return path.charAt(0) === '/'; 5 | } 6 | 7 | function win32(path) { 8 | // https://github.com/nodejs/node/blob/b3fcc245fb25539909ef1d5eaa01dbf92e168633/lib/path.js#L56 9 | var splitDeviceRe = /^([a-zA-Z]:|[\\/]{2}[^\\/]+[\\/]+[^\\/]+)?([\\/])?([\s\S]*?)$/; 10 | var result = splitDeviceRe.exec(path); 11 | var device = result[1] || ''; 12 | var isUnc = Boolean(device && device.charAt(1) !== ':'); 13 | 14 | // UNC paths are always absolute 15 | return Boolean(result[2] || isUnc); 16 | } 17 | 18 | module.exports = process.platform === 'win32' ? win32 : posix; 19 | module.exports.posix = posix; 20 | module.exports.win32 = win32; 21 | -------------------------------------------------------------------------------- /pm2/lib/templates/init-scripts/rcd.tpl: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # PROVIDE: pm2 4 | # REQUIRE: LOGIN 5 | # KEYWORD: shutdown 6 | 7 | . /etc/rc.subr 8 | 9 | name="%SERVICE_NAME%" 10 | rcvar="%SERVICE_NAME%_enable" 11 | 12 | start_cmd="pm2_start" 13 | stop_cmd="pm2_stop" 14 | reload_cmd="pm2_reload" 15 | status_cmd="pm2_status" 16 | extra_commands="reload status" 17 | 18 | pm2() 19 | { 20 | env PATH="$PATH:%NODE_PATH%" PM2_HOME="%HOME_PATH%" su -m "%USER%" -c "%PM2_PATH% $*" 21 | } 22 | 23 | pm2_start() 24 | { 25 | pm2 resurrect 26 | } 27 | 28 | pm2_stop() 29 | { 30 | pm2 kill 31 | } 32 | 33 | pm2_reload() 34 | { 35 | pm2 reload all 36 | } 37 | 38 | pm2_status() 39 | { 40 | pm2 list 41 | } 42 | 43 | load_rc_config $name 44 | run_rc_command "$1" 45 | -------------------------------------------------------------------------------- /pm2-app/pm2_connect.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jianchen on 2018/6/11. 3 | */ 4 | var pm2 = require('pm2'); 5 | 6 | pm2.connect(function(err) { 7 | if (err) { 8 | console.error(err); 9 | process.exit(2); 10 | } 11 | 12 | pm2.start({ 13 | script : 'app1.js', // Script to be run 14 | exec_mode : 'cluster', // Allows your app to be clustered 15 | instances : 4, // Optional: Scales your app by 4 16 | max_memory_restart : '100M' // Optional: Restarts your app if it reaches 100Mo 17 | }, function(err, apps) { 18 | pm2.disconnect(); // Disconnects from PM2 19 | console.log('script not found,Disconnects from PM2') 20 | if (err) throw err 21 | }); 22 | }); -------------------------------------------------------------------------------- /pm2/changelogTemplate.md: -------------------------------------------------------------------------------- 1 | <% if(logo) { %>pm2 logo <% } %> 2 | <% if(logo) { %># <%= title %> <% } %> 3 | <% if(intro) { %><%= '\n' %><%= intro %><%= '\n' %><% } %> 4 | <% if(version && (version.name || version.number)) { %>##<% if(version.name){%> <%= version.name %><% } %> <% if(version.date){ %>( <%= version.date %> )<% } %><%= '\n' %><% } %> 5 | <% _.forEach(sections, function(section){ 6 | if(section.commitsCount > 0) { %> 7 | ## <%= section.title %> 8 | <% _.forEach(section.commits, function(commit){ %> - <%= printCommit(commit, true) %><% }) %> 9 | <% _.forEach(section.components, function(component){ %> - **<%= component.name %>** 10 | <% _.forEach(component.commits, function(commit){ %> - <%= printCommit(commit, true) %><% }) %> 11 | <% }) %> 12 | <% } %> 13 | <% }) %> 14 | -------------------------------------------------------------------------------- /pm2/lib/Interactor/internal-ip.js: -------------------------------------------------------------------------------- 1 | var os = require('os'); 2 | 3 | var type = { 4 | v4: { 5 | def: '127.0.0.1', 6 | family: 'IPv4' 7 | }, 8 | v6: { 9 | def: '::1', 10 | family: 'IPv6' 11 | } 12 | }; 13 | 14 | function internalIp(version) { 15 | var options = type[version]; 16 | var ret = options.def; 17 | var interfaces = os.networkInterfaces(); 18 | 19 | Object.keys(interfaces).forEach(function (el) { 20 | interfaces[el].forEach(function (el2) { 21 | if (!el2.internal && el2.family === options.family) { 22 | ret = el2.address; 23 | } 24 | }); 25 | }); 26 | 27 | return ret; 28 | } 29 | 30 | function v4() { 31 | return internalIp('v4'); 32 | } 33 | 34 | function v6() { 35 | return internalIp('v6'); 36 | } 37 | 38 | module.exports = v4; 39 | module.exports.v4 = v4; 40 | module.exports.v6 = v6; 41 | -------------------------------------------------------------------------------- /tagg.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jianchen on 2018/5/10. 3 | */ 4 | function fibo (n) { 5 | return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1; 6 | } 7 | console.time('8 thread'); 8 | var numThreads= 8; //创建线程池,最大数为8 9 | var threadPool= require('threads_a_gogo').createPool(numThreads).all.eval(fibo); //为线程池注册程序 10 | var i=8; 11 | var cb = function(err,data){ //注册线程执行完毕的回调函数 12 | console.log(data); 13 | if(!--i){ 14 | threadPool.destroy(); 15 | console.timeEnd('8 thread'); 16 | } 17 | } 18 | threadPool.any.eval('fibo(40)', cb); //开始向线程池中执行fibo(40)这个任务 19 | 20 | threadPool.any.eval('fibo(40)', cb); 21 | 22 | threadPool.any.eval('fibo(40)', cb); 23 | 24 | threadPool.any.eval('fibo(40)', cb); 25 | 26 | threadPool.any.eval('fibo(40)', cb); 27 | 28 | threadPool.any.eval('fibo(40)', cb); 29 | 30 | threadPool.any.eval('fibo(40)', cb); 31 | 32 | threadPool.any.eval('fibo(40)', cb); -------------------------------------------------------------------------------- /pm2/.drone.yml: -------------------------------------------------------------------------------- 1 | pipeline: 2 | build_rpm: 3 | group: build 4 | image: node:stretch 5 | commands: 6 | - apt-get update 7 | - apt-get install -y git ruby ruby-dev rubygems build-essential fakeroot lintian rpm 8 | - gem install rake 9 | - gem install --no-ri --no-rdoc fpm 10 | - gem install --no-ri --no-rdoc package_cloud 11 | - ./packager/build-dist.sh 12 | - echo $${PACKAGECLOUD_TOKEN} > /root/.packagecloud 13 | - ./packager/build-deb-rpm.sh 14 | - ./packager/publish_deb_rpm.sh 15 | secrets: ['packagecloud_token'] 16 | when: 17 | event: tag 18 | build_apk: 19 | group: build 20 | image: keymetrics/alpine-pm2-builder:latest 21 | environment: 22 | - AWS_REPO_BUCKET=alpine-apk.pm2.io 23 | secrets: ['apk_rsa_priv_key', 'apk_rsa_pub_key', 'aws_access_key_id', 'aws_secret_access_key'] 24 | when: 25 | event: tag 26 | -------------------------------------------------------------------------------- /test-script/mainProcess.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jianchen on 2018/5/9. 3 | */ 4 | 5 | let {execFile} = require('child_process'); 6 | // 7 | // const startServer = async () => { 8 | // const {stderr, stdout} = execFile('nvm',['use v8.6.0'], {cwd: '/Users/jianchen/workspace/react-webstorm/helloka2'}); 9 | // // const {stderr1, stdout1} = execFile('npm',['start'], {cwd: '/Users/jianchen/workspace/react-webstorm/helloka2'}); 10 | // if(stderr) { 11 | // console.log('stderr',stderr); 12 | // return; 13 | // } 14 | // console.log('stdout', stdout) 15 | // } 16 | // 17 | // startServer(); 18 | 19 | var ls = execFile('bash',['startServer.sh'], (err, stdout, stderr) => { 20 | if(err){ 21 | throw err; 22 | } 23 | console.log(stdout); 24 | console.log(stderr) 25 | execFile('npm',['start'], (err, stdout, stderr) => { 26 | if(err){ 27 | throw err; 28 | } 29 | console.log(stdout); 30 | console.log(stderr) 31 | }); 32 | }); 33 | 34 | 35 | -------------------------------------------------------------------------------- /spawn/spawn.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jianchen on 2018/5/11. 3 | */ 4 | let spawn = require('child_process').spawn 5 | let numCPUs = require("os").cpus().length; 6 | let path = require('path'); 7 | var async = require('async') 8 | let cluster = require('cluster') 9 | 10 | let exec_file = path.resolve(__dirname,'processContainerFork.js'); 11 | cluster.setupMaster({ 12 | windowsHide: true, // windows系统创建的进程关闭其console出来的内容 13 | exec : path.resolve(__dirname,'ProcessContainer.js') 14 | }); 15 | 16 | 17 | console.log('exec_file',exec_file) 18 | 19 | async.timesLimit(numCPUs, 1, function (n, next) { 20 | var cspr = spawn('node',['--harmony',exec_file],{ 21 | detached:true, 22 | stdio : ['pipe', 'pipe', 'pipe', 'ipc'] //Same as fork() in node core} 23 | }); 24 | cspr.process = {}; 25 | cspr.process.pid = cspr.pid; 26 | cspr.stderr.on('data',(err) => { 27 | console.log(err.toString()) 28 | }) 29 | cspr.unref(); 30 | next(null, cspr) 31 | }) -------------------------------------------------------------------------------- /pm2/lib/Interactor/RemoteActions/ScopedExecution.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 the PM2 project authors. All rights reserved. 3 | * Use of this source code is governed by a license that 4 | * can be found in the LICENSE file. 5 | */ 6 | 7 | var pm2 = require('../../..'); 8 | var domain = require('domain'); 9 | var Utility = require('../../Utility.js'); 10 | 11 | var d = domain.create(); 12 | 13 | d.once('error', function(err) { 14 | process.send(JSON.stringify({err: err.stack, isFinished : true})); 15 | }); 16 | 17 | d.run(function() { 18 | var params = JSON.parse(process.env.fork_params); 19 | 20 | console.log('Executing: pm2 %s %s', 21 | params.action, 22 | params.opts.args ? params.opts.args.join(' ') : ''); 23 | 24 | pm2.connect(function() { 25 | pm2.remoteV2(params.action, params.opts, function(err, dt) { 26 | process.send(JSON.stringify(Utility.clone({ 27 | err: err, 28 | dt: dt, 29 | isFinished : true 30 | }))); 31 | pm2.disconnect(process.exit); 32 | }); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /pm2/lib/templates/init-scripts/launchd.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Label 6 | com.PM2 7 | UserName 8 | %USER% 9 | KeepAlive 10 | 11 | ProgramArguments 12 | 13 | /bin/sh 14 | -c 15 | %PM2_PATH% resurrect 16 | 17 | RunAtLoad 18 | 19 | OnDemand 20 | 21 | LaunchOnlyOnce 22 | 23 | EnvironmentVariables 24 | 25 | PATH 26 | %NODE_PATH% 27 | PM2_HOME 28 | %HOME_PATH% 29 | 30 | StandardErrorPath 31 | /tmp/com.PM2.err 32 | StandardOutPath 33 | /tmp/com.PM2.out 34 | 35 | 36 | -------------------------------------------------------------------------------- /npm-debug.log.2939786434: -------------------------------------------------------------------------------- 1 | 0 info it worked if it ends with ok 2 | 1 verbose cli [ '/Users/jianchen/.nvm/versions/node/v6.11.0/bin/node', 3 | 1 verbose cli '/Users/jianchen/.nvm/versions/node/v6.11.0/bin/npm', 4 | 1 verbose cli 'config', 5 | 1 verbose cli '--loglevel=warn', 6 | 1 verbose cli 'get', 7 | 1 verbose cli 'prefix' ] 8 | 2 info using npm@3.10.10 9 | 3 info using node@v6.11.0 10 | 4 verbose exit [ 0, true ] 11 | 5 verbose stack Error: write EPIPE 12 | 5 verbose stack at exports._errnoException (util.js:1018:11) 13 | 5 verbose stack at WriteWrap.afterWrite (net.js:800:14) 14 | 6 verbose cwd /Users/jianchen/WebstormProjects/nodeprocess 15 | 7 error Darwin 16.7.0 16 | 8 error argv "/Users/jianchen/.nvm/versions/node/v6.11.0/bin/node" "/Users/jianchen/.nvm/versions/node/v6.11.0/bin/npm" "config" "--loglevel=warn" "get" "prefix" 17 | 9 error node v6.11.0 18 | 10 error npm v3.10.10 19 | 11 error code EPIPE 20 | 12 error errno EPIPE 21 | 13 error syscall write 22 | 14 error write EPIPE 23 | 15 error If you need help, you may report this error at: 24 | 15 error 25 | 16 verbose exit [ 1, true ] 26 | -------------------------------------------------------------------------------- /pm2/lib/Event.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 the PM2 project authors. All rights reserved. 3 | * Use of this source code is governed by a license that 4 | * can be found in the LICENSE file. 5 | */ 6 | 7 | var Utility = require('./Utility.js'); 8 | 9 | module.exports = function(God) { 10 | 11 | God.notify = function(action_name, data, manually) { 12 | God.bus.emit('process:event', { 13 | event : action_name, 14 | manually : typeof(manually) == 'undefined' ? false : true, 15 | process : Utility.formatCLU(data), 16 | at : Utility.getDate() 17 | }); 18 | }; 19 | 20 | God.notifyByProcessId = function(opts, cb) { 21 | if (typeof(opts.id) === 'undefined') { return cb(new Error('process id missing')); } 22 | var proc = God.clusters_db[opts.id]; 23 | if (!proc) { return cb(new Error('process id doesnt exists')); } 24 | 25 | God.bus.emit('process:event', { 26 | event : opts.action_name, 27 | manually : typeof(opts.manually) == 'undefined' ? false : true, 28 | process : Utility.formatCLU(proc), 29 | at : Utility.getDate() 30 | }); 31 | 32 | process.nextTick(function() { 33 | return cb ? cb(null) : false; 34 | }); 35 | return false; 36 | }; 37 | }; 38 | -------------------------------------------------------------------------------- /pm2/lib/Interactor/Cipher.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 the PM2 project authors. All rights reserved. 3 | * Use of this source code is governed by a license that 4 | * can be found in the LICENSE file. 5 | */ 6 | 7 | var crypto = require('crypto'); 8 | 9 | const CIPHER_ALGORITHM = 'aes256'; 10 | 11 | var Cipher = module.exports = {}; 12 | 13 | /** 14 | * Description 15 | * @method decipherMessage 16 | * @param {} msg 17 | * @return ret 18 | */ 19 | Cipher.decipherMessage = function(msg, key) { 20 | var ret = {}; 21 | 22 | try { 23 | var decipher = crypto.createDecipher(CIPHER_ALGORITHM, key); 24 | var decipheredMessage = decipher.update(msg, 'hex', 'utf8'); 25 | decipheredMessage += decipher.final('utf8'); 26 | ret = JSON.parse(decipheredMessage); 27 | } catch(e) { 28 | return null; 29 | } 30 | 31 | return ret; 32 | } 33 | 34 | /** 35 | * Description 36 | * @method cipherMessage 37 | * @param {} data 38 | * @param {} key 39 | * @return 40 | */ 41 | Cipher.cipherMessage = function(data, key) { 42 | try { 43 | var cipher = crypto.createCipher(CIPHER_ALGORITHM, key); 44 | var cipheredData = cipher.update(data, 'utf8', 'hex'); 45 | cipheredData += cipher.final('hex'); 46 | return cipheredData; 47 | } catch(e) { 48 | return null; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /pm2/lib/completion.sh: -------------------------------------------------------------------------------- 1 | ###-begin-pm2-completion-### 2 | ### credits to npm for the completion file model 3 | # 4 | # Installation: pm2 completion >> ~/.bashrc (or ~/.zshrc) 5 | # 6 | 7 | COMP_WORDBREAKS=${COMP_WORDBREAKS/=/} 8 | COMP_WORDBREAKS=${COMP_WORDBREAKS/@/} 9 | export COMP_WORDBREAKS 10 | 11 | if type complete &>/dev/null; then 12 | _pm2_completion () { 13 | local si="$IFS" 14 | IFS=$'\n' COMPREPLY=($(COMP_CWORD="$COMP_CWORD" \ 15 | COMP_LINE="$COMP_LINE" \ 16 | COMP_POINT="$COMP_POINT" \ 17 | pm2 completion -- "${COMP_WORDS[@]}" \ 18 | 2>/dev/null)) || return $? 19 | IFS="$si" 20 | } 21 | complete -o default -F _pm2_completion pm2 22 | elif type compctl &>/dev/null; then 23 | _pm2_completion () { 24 | local cword line point words si 25 | read -Ac words 26 | read -cn cword 27 | let cword-=1 28 | read -l line 29 | read -ln point 30 | si="$IFS" 31 | IFS=$'\n' reply=($(COMP_CWORD="$cword" \ 32 | COMP_LINE="$line" \ 33 | COMP_POINT="$point" \ 34 | pm2 completion -- "${words[@]}" \ 35 | 2>/dev/null)) || return $? 36 | IFS="$si" 37 | } 38 | compctl -K _pm2_completion + -f + pm2 39 | fi 40 | ###-end-pm2-completion-### 41 | -------------------------------------------------------------------------------- /pm2/lib/templates/ecosystem.tpl: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | /** 3 | * Application configuration section 4 | * http://pm2.keymetrics.io/docs/usage/application-declaration/ 5 | */ 6 | apps : [ 7 | 8 | // First application 9 | { 10 | name : 'API', 11 | script : 'app.js', 12 | env: { 13 | COMMON_VARIABLE: 'true' 14 | }, 15 | env_production : { 16 | NODE_ENV: 'production' 17 | } 18 | }, 19 | 20 | // Second application 21 | { 22 | name : 'WEB', 23 | script : 'web.js' 24 | } 25 | ], 26 | 27 | /** 28 | * Deployment section 29 | * http://pm2.keymetrics.io/docs/usage/deployment/ 30 | */ 31 | deploy : { 32 | production : { 33 | user : 'node', 34 | host : '212.83.163.1', 35 | ref : 'origin/master', 36 | repo : 'git@github.com:repo.git', 37 | path : '/var/www/production', 38 | 'post-deploy' : 'npm install && pm2 reload ecosystem.config.js --env production' 39 | }, 40 | dev : { 41 | user : 'node', 42 | host : '212.83.163.1', 43 | ref : 'origin/master', 44 | repo : 'git@github.com:repo.git', 45 | path : '/var/www/development', 46 | 'post-deploy' : 'npm install && pm2 reload ecosystem.config.js --env dev', 47 | env : { 48 | NODE_ENV: 'dev' 49 | } 50 | } 51 | } 52 | }; 53 | -------------------------------------------------------------------------------- /pm2/lib/motd: -------------------------------------------------------------------------------- 1 | 2 | ------------- 3 | 4 | __/\\\\\\\\\\\\\____/\\\\____________/\\\\____/\\\\\\\\\_____ 5 | _\/\\\/////////\\\_\/\\\\\\________/\\\\\\__/\\\///////\\\___ 6 | _\/\\\_______\/\\\_\/\\\//\\\____/\\\//\\\_\///______\//\\\__ 7 | _\/\\\\\\\\\\\\\/__\/\\\\///\\\/\\\/_\/\\\___________/\\\/___ 8 | _\/\\\/////////____\/\\\__\///\\\/___\/\\\________/\\\//_____ 9 | _\/\\\_____________\/\\\____\///_____\/\\\_____/\\\//________ 10 | _\/\\\_____________\/\\\_____________\/\\\___/\\\/___________ 11 | _\/\\\_____________\/\\\_____________\/\\\__/\\\\\\\\\\\\\\\_ 12 | _\///______________\///______________\///__\///////////////__ 13 | 14 | 15 | Community Edition 16 | 17 | Production Process Manager for Node.js applications 18 | with a built-in Load Balancer. 19 | 20 | 21 | Start and Daemonize any application: 22 | $ pm2 start app.js 23 | 24 | Load Balance 4 instances of api.js: 25 | $ pm2 start api.js -i 4 26 | 27 | Monitor in production: 28 | $ pm2 monitor 29 | 30 | Make pm2 auto-boot at server restart: 31 | $ pm2 startup 32 | 33 | To go further checkout: 34 | http://pm2.io/ 35 | 36 | 37 | ------------- 38 | -------------------------------------------------------------------------------- /pm2/lib/templates/init-scripts/openrc.tpl: -------------------------------------------------------------------------------- 1 | #!/sbin/openrc-run 2 | # Copyright 2017 the PM2 project authors. All rights reserved. 3 | # Init script automatically generated by pm2 startup 4 | 5 | description="Production process manager for Node.js apps with a built-in load balancer." 6 | 7 | extra_started_commands="reload" 8 | 9 | PM2="%PM2_PATH%" 10 | user=${PM2_USER:-%USER%} 11 | export PM2_HOME=$(eval echo ~${user})"/.pm2/" 12 | # Options for start-stop-daemon (default start function) 13 | command=${PM2} 14 | command_user=${user} 15 | command_args="resurrect" 16 | pidfile=${PM2_HOME}/pm2.pid 17 | 18 | run_pm2_as_user() { 19 | einfo "${PM2} $@" 20 | eval su -l ${user} -c \'${PM2} $@\' 21 | } 22 | 23 | depend() { 24 | need net 25 | need localmount 26 | after bootmisc 27 | } 28 | 29 | start_post() { 30 | if [ "${user}" == "root" ]; then 31 | ewarn "PM2: Better run this daemon as a non root user. To set this user create" 32 | ewarn "PM2: /etc/conf.d/pm2 file and define 'PM2_USER=user' there." 33 | ewarn "PM2: Note user MUST have home directory for PM2 logs/state/etc..." 34 | fi 35 | einfo "PM2: Process Manager started. To start services run:" 36 | einfo "PM2: # su -l ${user} -c '$PM2 start /path/to/app'" 37 | } 38 | 39 | stop() { 40 | ebegin "Stopping PM2 process manager..." 41 | run_pm2_as_user dump 42 | run_pm2_as_user delete all 43 | run_pm2_as_user kill 44 | eend $? 45 | } 46 | 47 | reload() { 48 | ebegin "Reloading pm2" 49 | run_pm2_as_user reload all 50 | eend $? 51 | } 52 | 53 | # vim: ts=4 54 | -------------------------------------------------------------------------------- /pm2/.changelogrc: -------------------------------------------------------------------------------- 1 | { 2 | "app_name": "", 3 | "logo": "", 4 | "intro": "", 5 | "branch" : "master", 6 | "repo_url": "https://github.com/Unitech/pm2", 7 | "version_name" : "v2.10.3", 8 | "tag": "2.10.2", 9 | "file": "currentTagChangelog.md", 10 | "template": "changelogTemplate.md", 11 | "sections": [ 12 | { 13 | "title": "Bug Fixes", 14 | "grep": "^fix" 15 | }, 16 | { 17 | "title": "Hot Fixes", 18 | "grep": "^hotfix" 19 | }, 20 | { 21 | "title": "Features", 22 | "grep": "^feat" 23 | }, 24 | { 25 | "title": "Documentation", 26 | "grep": "^docs" 27 | }, 28 | { 29 | "title": "Breaking changes", 30 | "grep": "BREAKING" 31 | }, 32 | { 33 | "title": "Refactor", 34 | "grep": "^refactor" 35 | }, 36 | { 37 | "title": "Performance improvement", 38 | "grep": "^perf" 39 | }, 40 | { 41 | "title": "Style", 42 | "grep": "^style" 43 | }, 44 | { 45 | "title": "Test", 46 | "grep": "^test" 47 | }, 48 | { 49 | "title": "Chore", 50 | "grep": "^chore" 51 | }, 52 | { 53 | "title": "Branchs merged", 54 | "grep": "^Merge branch" 55 | }, 56 | { 57 | "title" : "Pull requests merged", 58 | "grep": "^Merge pull request" 59 | } 60 | ] 61 | } 62 | -------------------------------------------------------------------------------- /pm2/lib/tools/open.js: -------------------------------------------------------------------------------- 1 | var exec = require('child_process').exec 2 | , path = require('path') 3 | ; 4 | 5 | 6 | /** 7 | * open a file or uri using the default application for the file type. 8 | * 9 | * @return {ChildProcess} - the child process object. 10 | * @param {string} target - the file/uri to open. 11 | * @param {string} appName - (optional) the application to be used to open the 12 | * file (for example, "chrome", "firefox") 13 | * @param {function(Error)} callback - called with null on success, or 14 | * an error object that contains a property 'code' with the exit 15 | * code of the process. 16 | */ 17 | 18 | module.exports = open; 19 | 20 | function open(target, appName, callback) { 21 | var opener; 22 | 23 | if (typeof(appName) === 'function') { 24 | callback = appName; 25 | appName = null; 26 | } 27 | 28 | switch (process.platform) { 29 | case 'darwin': 30 | if (appName) { 31 | opener = 'open -a "' + escape(appName) + '"'; 32 | } else { 33 | opener = 'open'; 34 | } 35 | break; 36 | case 'win32': 37 | // if the first parameter to start is quoted, it uses that as the title 38 | // so we pass a blank title so we can quote the file we are opening 39 | if (appName) { 40 | opener = 'start "" "' + escape(appName) + '"'; 41 | } else { 42 | opener = 'start ""'; 43 | } 44 | break; 45 | default: 46 | if (appName) { 47 | opener = escape(appName); 48 | } else { 49 | // use Portlands xdg-open everywhere else 50 | opener = path.join(__dirname, './xdg-open'); 51 | } 52 | break; 53 | } 54 | 55 | if (process.env.SUDO_USER) { 56 | opener = 'sudo -u ' + process.env.SUDO_USER + ' ' + opener; 57 | } 58 | return exec(opener + ' "' + escape(target) + '"', callback); 59 | } 60 | 61 | function escape(s) { 62 | return s.replace(/"/g, '\\\"'); 63 | } 64 | -------------------------------------------------------------------------------- /pm2/lib/templates/init-scripts/pm2-init-amazon.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # pm2 Process manager for NodeJS 4 | # 5 | # chkconfig: 345 80 20 6 | # 7 | # description: PM2 next gen process manager for Node.js 8 | # processname: pm2 9 | # 10 | ### BEGIN INIT INFO 11 | # Provides: pm2 12 | # Required-Start: $local_fs $remote_fs 13 | # Required-Stop: $local_fs $remote_fs 14 | # Should-Start: $network 15 | # Should-Stop: $network 16 | # Default-Start: 2 3 4 5 17 | # Default-Stop: 0 1 6 18 | # Short-Description: PM2 init script 19 | # Description: PM2 is the next gen process manager for Node.js 20 | ### END INIT INFO 21 | 22 | NAME=pm2 23 | PM2=%PM2_PATH% 24 | USER=%USER% 25 | 26 | export PATH=%NODE_PATH%:$PATH 27 | export PM2_HOME="%HOME_PATH%" 28 | 29 | lockfile="/var/lock/subsys/pm2-init.sh" 30 | 31 | super() { 32 | su - $USER -c "PATH=$PATH; PM2_HOME=$PM2_HOME $*" 33 | } 34 | 35 | start() { 36 | echo "Starting $NAME" 37 | super $PM2 resurrect 38 | retval=$? 39 | [ $retval -eq 0 ] && touch $lockfile 40 | } 41 | 42 | stop() { 43 | echo "Stopping $NAME" 44 | #super $PM2 dump 45 | super $PM2 delete all 46 | super $PM2 kill 47 | rm -f $lockfile 48 | } 49 | 50 | restart() { 51 | echo "Restarting $NAME" 52 | stop 53 | start 54 | } 55 | 56 | reload() { 57 | echo "Reloading $NAME" 58 | super $PM2 reload all 59 | } 60 | 61 | status() { 62 | echo "Status for $NAME:" 63 | super $PM2 list 64 | RETVAL=$? 65 | } 66 | 67 | case "$1" in 68 | start) 69 | start 70 | ;; 71 | stop) 72 | stop 73 | ;; 74 | status) 75 | status 76 | ;; 77 | restart) 78 | restart 79 | ;; 80 | reload) 81 | reload 82 | ;; 83 | *) 84 | echo "Usage: {start|stop|status|restart|reload}" 85 | exit 1 86 | ;; 87 | esac 88 | exit $RETVAL 89 | -------------------------------------------------------------------------------- /test-script/node-version.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | * * # # 5 | * # _oo0oo_ # 6 | * # o8888888o # 7 | * # 88" . "88 # 8 | * # (| -_- |) # 9 | * # 0\ = /0 # 10 | * # ___/`---'\___ # 11 | * # .' \\| |# '. # 12 | * # / \\||| : |||# \ # 13 | * # / _||||| -:- |||||- \ # 14 | * # | | \\\ - #/ | | # 15 | * # | \_| ''\---/'' |_/ | # 16 | * # \ .-\__ '-' ___/-. / # 17 | * # ___'. .' /--.--\ `. .'___ # 18 | * # ."" '< `.___\_<|>_/___.' >' "". # 19 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | # 20 | * # \ \ `_. \_ __\ /__ _/ .-` / / # 21 | * # =====`-.____`.___ \_____/___.-`___.-'===== # 22 | * # `=---=' # 23 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 24 | * # # 25 | * # 佛祖保佑 永无BUG # 26 | * # # 27 | * * 28 | * Created by jianchen on 2018/5/24. 29 | */ 30 | console.log('process.versions.node:',process.versions.node) -------------------------------------------------------------------------------- /pm2/lib/API/Spinner.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Spinner 3 | * Handle TTY and non-TTY based terminals 4 | */ 5 | 6 | var defaultSpinnerString = [ 7 | "⠋", 8 | "⠙", 9 | "⠹", 10 | "⠸", 11 | "⠼", 12 | "⠴", 13 | "⠦", 14 | "⠧", 15 | "⠇", 16 | "⠏" 17 | ].join(''); 18 | 19 | var InteractiveSpinner = function(textToShow){ 20 | this.text = textToShow || ''; 21 | this.setSpinnerString(defaultSpinnerString); // use default spinner string 22 | }; 23 | 24 | InteractiveSpinner.setDefaultSpinnerString = function(value) { 25 | defaultSpinnerString = value; 26 | }; 27 | 28 | InteractiveSpinner.prototype.start = function() { 29 | var current = 0; 30 | var self = this; 31 | this.id = setInterval(function() { 32 | try { 33 | process.stdout.clearLine(); 34 | process.stdout.cursorTo(0); 35 | process.stdout.write(self.chars[current] + ' ' + self.text); 36 | current = ++current % self.chars.length; 37 | } catch(e) { // ignore error if term is not tty, just display nothing 38 | } 39 | }, 80); 40 | }; 41 | 42 | InteractiveSpinner.prototype.setSpinnerString = function(str) { 43 | this.chars = str.split(""); 44 | }; 45 | 46 | InteractiveSpinner.prototype.stop = function() { 47 | try { 48 | process.stdout.clearLine(); 49 | process.stdout.cursorTo(0); 50 | } catch(e) {} 51 | clearInterval(this.id); 52 | }; 53 | 54 | /** 55 | * Display dots if non TTY terminal 56 | */ 57 | var StaticSpinner = function(text) { 58 | console.log(text); 59 | } 60 | 61 | StaticSpinner.prototype.start = function() { 62 | this.interval = setInterval(function() { 63 | process.stdout.write('.'); 64 | }, 500); 65 | }; 66 | 67 | StaticSpinner.prototype.stop = function() { 68 | clearInterval(this.interval); 69 | console.log(); 70 | }; 71 | 72 | module.exports = function(text) { 73 | if (process.stdout.isTTY) 74 | return new InteractiveSpinner(text); 75 | else 76 | return new StaticSpinner(text); 77 | }; 78 | -------------------------------------------------------------------------------- /v8profiler/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | * * # # 5 | * # _oo0oo_ # 6 | * # o8888888o # 7 | * # 88" . "88 # 8 | * # (| -_- |) # 9 | * # 0\ = /0 # 10 | * # ___/`---'\___ # 11 | * # .' \\| |# '. # 12 | * # / \\||| : |||# \ # 13 | * # / _||||| -:- |||||- \ # 14 | * # | | \\\ - #/ | | # 15 | * # | \_| ''\---/'' |_/ | # 16 | * # \ .-\__ '-' ___/-. / # 17 | * # ___'. .' /--.--\ `. .'___ # 18 | * # ."" '< `.___\_<|>_/___.' >' "". # 19 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | # 20 | * # \ \ `_. \_ __\ /__ _/ .-` / / # 21 | * # =====`-.____`.___ \_____/___.-`___.-'===== # 22 | * # `=---=' # 23 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 24 | * # # 25 | * # 佛祖保佑 永无BUG # 26 | * # # 27 | * * 28 | * Created by jianchen on 2018/5/24. 29 | */ 30 | var profiler = require('v8-profiler'); 31 | var snapshot1 = profiler.takeSnapshot('cpu'); 32 | 33 | console.log(snapshot1) -------------------------------------------------------------------------------- /cluster/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | * * # # 5 | * # _oo0oo_ # 6 | * # o8888888o # 7 | * # 88" . "88 # 8 | * # (| -_- |) # 9 | * # 0\ = /0 # 10 | * # ___/`---'\___ # 11 | * # .' \\| |# '. # 12 | * # / \\||| : |||# \ # 13 | * # / _||||| -:- |||||- \ # 14 | * # | | \\\ - #/ | | # 15 | * # | \_| ''\---/'' |_/ | # 16 | * # \ .-\__ '-' ___/-. / # 17 | * # ___'. .' /--.--\ `. .'___ # 18 | * # ."" '< `.___\_<|>_/___.' >' "". # 19 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | # 20 | * # \ \ `_. \_ __\ /__ _/ .-` / / # 21 | * # =====`-.____`.___ \_____/___.-`___.-'===== # 22 | * # `=---=' # 23 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 24 | * # # 25 | * # 佛祖保佑 永无BUG # 26 | * # # 27 | * * 28 | * Created by jianchen on 2018/6/4. 29 | */ 30 | http.createServer(function(req, res) { 31 | res.writeHead(200); 32 | res.end('process ' + process.pid + ' port: 8001 says hello!'); 33 | }).listen(8001); -------------------------------------------------------------------------------- /spawn/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | * * # # 5 | * # _oo0oo_ # 6 | * # o8888888o # 7 | * # 88" . "88 # 8 | * # (| -_- |) # 9 | * # 0\ = /0 # 10 | * # ___/`---'\___ # 11 | * # .' \\| |# '. # 12 | * # / \\||| : |||# \ # 13 | * # / _||||| -:- |||||- \ # 14 | * # | | \\\ - #/ | | # 15 | * # | \_| ''\---/'' |_/ | # 16 | * # \ .-\__ '-' ___/-. / # 17 | * # ___'. .' /--.--\ `. .'___ # 18 | * # ."" '< `.___\_<|>_/___.' >' "". # 19 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | # 20 | * # \ \ `_. \_ __\ /__ _/ .-` / / # 21 | * # =====`-.____`.___ \_____/___.-`___.-'===== # 22 | * # `=---=' # 23 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 24 | * # # 25 | * # 佛祖保佑 永无BUG # 26 | * # # 27 | * * 28 | * Created by jianchen on 2018/6/4. 29 | */ 30 | let http = require('http'); 31 | http.createServer(function(req, res) { 32 | res.writeHead(200); 33 | res.end('process ' + process.pid + ' port: 8001 says hello!'); 34 | }).listen(8001); -------------------------------------------------------------------------------- /pm2-app/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | * * # # 5 | * # _oo0oo_ # 6 | * # o8888888o # 7 | * # 88" . "88 # 8 | * # (| -_- |) # 9 | * # 0\ = /0 # 10 | * # ___/`---'\___ # 11 | * # .' \\| |# '. # 12 | * # / \\||| : |||# \ # 13 | * # / _||||| -:- |||||- \ # 14 | * # | | \\\ - #/ | | # 15 | * # | \_| ''\---/'' |_/ | # 16 | * # \ .-\__ '-' ___/-. / # 17 | * # ___'. .' /--.--\ `. .'___ # 18 | * # ."" '< `.___\_<|>_/___.' >' "". # 19 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | # 20 | * # \ \ `_. \_ __\ /__ _/ .-` / / # 21 | * # =====`-.____`.___ \_____/___.-`___.-'===== # 22 | * # `=---=' # 23 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 24 | * # # 25 | * # 佛祖保佑 永无BUG # 26 | * # # 27 | * * 28 | * Created by jianchen on 2018/5/15. 29 | */ 30 | let http = require('http'); 31 | 32 | http.createServer(function(req, res) { 33 | res.writeHead(200); 34 | res.end('process ' + process.pid + ' port: 8001 says hello!'); 35 | }).listen(8001); -------------------------------------------------------------------------------- /pm2-app/app2.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | * * # # 5 | * # _oo0oo_ # 6 | * # o8888888o # 7 | * # 88" . "88 # 8 | * # (| -_- |) # 9 | * # 0\ = /0 # 10 | * # ___/`---'\___ # 11 | * # .' \\| |# '. # 12 | * # / \\||| : |||# \ # 13 | * # / _||||| -:- |||||- \ # 14 | * # | | \\\ - #/ | | # 15 | * # | \_| ''\---/'' |_/ | # 16 | * # \ .-\__ '-' ___/-. / # 17 | * # ___'. .' /--.--\ `. .'___ # 18 | * # ."" '< `.___\_<|>_/___.' >' "". # 19 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | # 20 | * # \ \ `_. \_ __\ /__ _/ .-` / / # 21 | * # =====`-.____`.___ \_____/___.-`___.-'===== # 22 | * # `=---=' # 23 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 24 | * # # 25 | * # 佛祖保佑 永无BUG # 26 | * # # 27 | * * 28 | * Created by jianchen on 2018/5/15. 29 | */ 30 | let http = require('http'); 31 | 32 | http.createServer(function(req, res) { 33 | res.writeHead(200); 34 | res.end('process ' + process.pid + ' port: 8002 says hello!'); 35 | }).listen(8002); -------------------------------------------------------------------------------- /axon/pub-emitter/axon.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | * * # # 5 | * # _oo0oo_ # 6 | * # o8888888o # 7 | * # 88" . "88 # 8 | * # (| -_- |) # 9 | * # 0\ = /0 # 10 | * # ___/`---'\___ # 11 | * # .' \\| |# '. # 12 | * # / \\||| : |||# \ # 13 | * # / _||||| -:- |||||- \ # 14 | * # | | \\\ - #/ | | # 15 | * # | \_| ''\---/'' |_/ | # 16 | * # \ .-\__ '-' ___/-. / # 17 | * # ___'. .' /--.--\ `. .'___ # 18 | * # ."" '< `.___\_<|>_/___.' >' "". # 19 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | # 20 | * # \ \ `_. \_ __\ /__ _/ .-` / / # 21 | * # =====`-.____`.___ \_____/___.-`___.-'===== # 22 | * # `=---=' # 23 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 24 | * # # 25 | * # 佛祖保佑 永无BUG # 26 | * # # 27 | * * 28 | * Created by jianchen on 2018/5/15. 29 | */ 30 | var axon = require('axon'); 31 | var sock = axon.socket('pub-emitter'); 32 | 33 | sock.connect(3000); 34 | 35 | setInterval(function(){ 36 | sock.emit('login', { name: 'tobi' }); 37 | }, 500); -------------------------------------------------------------------------------- /node-require/b.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | * * # # 5 | * # _oo0oo_ # 6 | * # o8888888o # 7 | * # 88" . "88 # 8 | * # (| -_- |) # 9 | * # 0\ = /0 # 10 | * # ___/`---'\___ # 11 | * # .' \\| |# '. # 12 | * # / \\||| : |||# \ # 13 | * # / _||||| -:- |||||- \ # 14 | * # | | \\\ - #/ | | # 15 | * # | \_| ''\---/'' |_/ | # 16 | * # \ .-\__ '-' ___/-. / # 17 | * # ___'. .' /--.--\ `. .'___ # 18 | * # ."" '< `.___\_<|>_/___.' >' "". # 19 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | # 20 | * # \ \ `_. \_ __\ /__ _/ .-` / / # 21 | * # =====`-.____`.___ \_____/___.-`___.-'===== # 22 | * # `=---=' # 23 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 24 | * # # 25 | * # 佛祖保佑 永无BUG # 26 | * # # 27 | * * 28 | * Created by jianchen on 2018/5/25. 29 | */ 30 | var a = require('./a'); 31 | var B = module.exports = function(){ 32 | console.log('B') 33 | } 34 | B.prototype.start = () => { 35 | console.log('B.satrt') 36 | } 37 | 38 | var b = new B(); 39 | b.start(); -------------------------------------------------------------------------------- /axon/req/req.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | * * # # 5 | * # _oo0oo_ # 6 | * # o8888888o # 7 | * # 88" . "88 # 8 | * # (| -_- |) # 9 | * # 0\ = /0 # 10 | * # ___/`---'\___ # 11 | * # .' \\| |# '. # 12 | * # / \\||| : |||# \ # 13 | * # / _||||| -:- |||||- \ # 14 | * # | | \\\ - #/ | | # 15 | * # | \_| ''\---/'' |_/ | # 16 | * # \ .-\__ '-' ___/-. / # 17 | * # ___'. .' /--.--\ `. .'___ # 18 | * # ."" '< `.___\_<|>_/___.' >' "". # 19 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | # 20 | * # \ \ `_. \_ __\ /__ _/ .-` / / # 21 | * # =====`-.____`.___ \_____/___.-`___.-'===== # 22 | * # `=---=' # 23 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 24 | * # # 25 | * # 佛祖保佑 永无BUG # 26 | * # # 27 | * * 28 | * Created by jianchen on 2018/5/15. 29 | */ 30 | var axon = require('pm2-axon'); 31 | var sock = axon.socket('req'); 32 | 33 | sock.bind(3000); 34 | 35 | setInterval(() => { 36 | sock.send('hello world','chenjianhui',function(res){ 37 | console.log(res) 38 | }); 39 | },1000) -------------------------------------------------------------------------------- /node-require/a.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | * * # # 5 | * # _oo0oo_ # 6 | * # o8888888o # 7 | * # 88" . "88 # 8 | * # (| -_- |) # 9 | * # 0\ = /0 # 10 | * # ___/`---'\___ # 11 | * # .' \\| |# '. # 12 | * # / \\||| : |||# \ # 13 | * # / _||||| -:- |||||- \ # 14 | * # | | \\\ - #/ | | # 15 | * # | \_| ''\---/'' |_/ | # 16 | * # \ .-\__ '-' ___/-. / # 17 | * # ___'. .' /--.--\ `. .'___ # 18 | * # ."" '< `.___\_<|>_/___.' >' "". # 19 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | # 20 | * # \ \ `_. \_ __\ /__ _/ .-` / / # 21 | * # =====`-.____`.___ \_____/___.-`___.-'===== # 22 | * # `=---=' # 23 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 24 | * # # 25 | * # 佛祖保佑 永无BUG # 26 | * # # 27 | * * 28 | * Created by jianchen on 2018/5/25. 29 | */ 30 | var A = module.exports = function(){ 31 | console.log('A'); 32 | } 33 | 34 | A.prototype.start = () => { 35 | console.log('A.start'); 36 | } 37 | 38 | 39 | setTimeout(()=> { 40 | var a = new A(); 41 | a.start(); 42 | },500) -------------------------------------------------------------------------------- /pm2/lib/Interactor/WatchDog.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 the PM2 project authors. All rights reserved. 3 | * Use of this source code is governed by a license that 4 | * can be found in the LICENSE file. 5 | */ 6 | 7 | var PM2 = require('../..'); 8 | var debug = require('debug')('interface:watchdog'); 9 | var shelljs = require('shelljs'); 10 | var csts = require('../../constants'); 11 | var path = require('path'); 12 | 13 | process.env.PM2_AGENT_ONLINE = true; 14 | 15 | var WatchDog = module.exports = { 16 | start : function(p) { 17 | var self = this; 18 | this.ipm2 = p.conf.ipm2; 19 | this.relaunching = false; 20 | this.pm2_instance = p.conf.pm2_instance; 21 | 22 | /** 23 | * Handle PM2 connection state changes 24 | */ 25 | this.ipm2.on('ready', function() { 26 | console.log('[WATCHDOG] Connected to PM2'); 27 | self.relaunching = false; 28 | self.autoDump(); 29 | }); 30 | 31 | console.log('[WATCHDOG] Launching'); 32 | 33 | this.ipm2.on('reconnecting', function() { 34 | console.log('[WATCHDOG] PM2 is disconnected - Relaunching PM2'); 35 | 36 | if (self.relaunching === true) return console.log('[WATCHDOG] Already relaunching PM2'); 37 | self.relaunching = true; 38 | 39 | if (self.dump_interval) 40 | clearInterval(self.dump_interval); 41 | 42 | return WatchDog.resurrect(); 43 | }); 44 | }, 45 | resurrect : function() { 46 | var self = this; 47 | 48 | console.log('[WATCHDOG] Trying to launch PM2 #1'); 49 | 50 | shelljs.exec('node ' + path.resolve(__dirname, '../../bin/pm2') + ' resurrect', function() { 51 | setTimeout(function() { 52 | self.relaunching = false; 53 | }, 2500); 54 | }); 55 | }, 56 | autoDump : function() { 57 | var self = this; 58 | 59 | this.dump_interval = setInterval(function() { 60 | if (self.relaunching == true) return false; 61 | 62 | self.pm2_instance.dump(function(err) { 63 | if (err) return console.error('[WATCHDOG] Error when dumping'); 64 | debug('PM2 process list dumped'); 65 | return false; 66 | }); 67 | }, 5 * 60 * 1000); 68 | } 69 | }; 70 | -------------------------------------------------------------------------------- /pm2/lib/Interactor/HttpRequest.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 the PM2 project authors. All rights reserved. 3 | * Use of this source code is governed by a license that 4 | * can be found in the LICENSE file. 5 | */ 6 | 7 | var http = require('http'); 8 | var https = require('https'); 9 | var url = require('url') 10 | var debug = require('debug')('interface:http'); 11 | 12 | var HttpRequest = module.exports = {}; 13 | 14 | HttpRequest.post = function(opts, cb) { 15 | if (!(opts.data && opts.url)) { 16 | return cb({ 17 | msg: 'missing parameters', 18 | port: opts.port, 19 | data: opts.data, 20 | url: opts.url 21 | }) 22 | } 23 | 24 | if (!opts.port) { 25 | var parsed = url.parse(opts.url) 26 | if (parsed.hostname && parsed.port) { 27 | opts.port = parseInt(parsed.port) 28 | opts.url = parsed.hostname 29 | } else { 30 | opts.port = 443 31 | } 32 | } 33 | 34 | var options = { 35 | hostname: opts.url, 36 | path: '/api/node/verifyPM2', 37 | method: 'POST', 38 | port: opts.port, 39 | rejectUnauthorized: false, 40 | headers: { 41 | 'Content-Type': 'application/json', 42 | 'Content-Length': Buffer.byteLength(JSON.stringify(opts.data)) 43 | } 44 | } 45 | 46 | var client = (opts.port === 443) ? https : http; 47 | 48 | var req = client.request(options, function(res){ 49 | var dt = ''; 50 | 51 | res.on('data', function (chunk) { 52 | dt += chunk; 53 | }); 54 | 55 | res.on('end',function(){ 56 | try { 57 | cb(null, JSON.parse(dt)); 58 | } catch(e) { 59 | cb(e); 60 | } 61 | }); 62 | 63 | res.on('error', function(e){ 64 | cb(e); 65 | }); 66 | }); 67 | 68 | req.on('socket', function (socket) { 69 | /** 70 | * Configure request timeout 71 | */ 72 | socket.setTimeout(7000); 73 | socket.on('timeout', function() { 74 | debug('Connection timeout when retrieveing PM2 metadata', options); 75 | req.abort(); 76 | }); 77 | }); 78 | 79 | req.on('error', function(e) { 80 | cb(e); 81 | }); 82 | 83 | req.write(JSON.stringify(opts.data)); 84 | 85 | req.end(); 86 | }; 87 | -------------------------------------------------------------------------------- /spawn/processContainerFork.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | * * # # 5 | * # _oo0oo_ # 6 | * # o8888888o # 7 | * # 88" . "88 # 8 | * # (| -_- |) # 9 | * # 0\ = /0 # 10 | * # ___/`---'\___ # 11 | * # .' \\| |# '. # 12 | * # / \\||| : |||# \ # 13 | * # / _||||| -:- |||||- \ # 14 | * # | | \\\ - #/ | | # 15 | * # | \_| ''\---/'' |_/ | # 16 | * # \ .-\__ '-' ___/-. / # 17 | * # ___'. .' /--.--\ `. .'___ # 18 | * # ."" '< `.___\_<|>_/___.' >' "". # 19 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | # 20 | * # \ \ `_. \_ __\ /__ _/ .-` / / # 21 | * # =====`-.____`.___ \_____/___.-`___.-'===== # 22 | * # `=---=' # 23 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 24 | * # # 25 | * # 佛祖保佑 永无BUG # 26 | * # # 27 | * * 28 | * Created by jianchen on 2018/6/5. 29 | */ 30 | var path = require('path') 31 | 32 | let exec_file = path.resolve(__dirname,'app.js'); 33 | 34 | require('module')._load(exec_file, null, true); 35 | 36 | process.title = 'node app.js --name spawn' 37 | 38 | process.mainModule = process.mainModule || {}; 39 | process.mainModule.loaded = false; 40 | require.main = process.mainModule; -------------------------------------------------------------------------------- /axon/pm2-axon/req.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | * * # # 5 | * # _oo0oo_ # 6 | * # o8888888o # 7 | * # 88" . "88 # 8 | * # (| -_- |) # 9 | * # 0\ = /0 # 10 | * # ___/`---'\___ # 11 | * # .' \\| |# '. # 12 | * # / \\||| : |||# \ # 13 | * # / _||||| -:- |||||- \ # 14 | * # | | \\\ - #/ | | # 15 | * # | \_| ''\---/'' |_/ | # 16 | * # \ .-\__ '-' ___/-. / # 17 | * # ___'. .' /--.--\ `. .'___ # 18 | * # ."" '< `.___\_<|>_/___.' >' "". # 19 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | # 20 | * # \ \ `_. \_ __\ /__ _/ .-` / / # 21 | * # =====`-.____`.___ \_____/___.-`___.-'===== # 22 | * # `=---=' # 23 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 24 | * # # 25 | * # 佛祖保佑 永无BUG # 26 | * # # 27 | * * 28 | * Created by jianchen on 2018/5/15. 29 | */ 30 | var axon = require('pm2-axon'); 31 | var sock = axon.socket('req'); 32 | var path = require('path') 33 | var fs = require('fs') 34 | let pub_socket_file = path.resolve('./','sock.file') 35 | 36 | sock.bind(pub_socket_file); 37 | 38 | setInterval(() => { 39 | sock.send('hello world','chenjianhui',function(res){ 40 | console.log(res) 41 | }); 42 | },1000) -------------------------------------------------------------------------------- /axon/req/rep.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | * * # # 5 | * # _oo0oo_ # 6 | * # o8888888o # 7 | * # 88" . "88 # 8 | * # (| -_- |) # 9 | * # 0\ = /0 # 10 | * # ___/`---'\___ # 11 | * # .' \\| |# '. # 12 | * # / \\||| : |||# \ # 13 | * # / _||||| -:- |||||- \ # 14 | * # | | \\\ - #/ | | # 15 | * # | \_| ''\---/'' |_/ | # 16 | * # \ .-\__ '-' ___/-. / # 17 | * # ___'. .' /--.--\ `. .'___ # 18 | * # ."" '< `.___\_<|>_/___.' >' "". # 19 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | # 20 | * # \ \ `_. \_ __\ /__ _/ .-` / / # 21 | * # =====`-.____`.___ \_____/___.-`___.-'===== # 22 | * # `=---=' # 23 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 24 | * # # 25 | * # 佛祖保佑 永无BUG # 26 | * # # 27 | * * 28 | * Created by jianchen on 2018/5/15. 29 | */ 30 | var axon = require('axon'); 31 | var sock = axon.socket('rep'); 32 | 33 | sock.connect(3000); 34 | 35 | sock.on('message', function(task, username, reply){ 36 | // resize the image 37 | switch (task){ 38 | case 'hello world': 39 | reply('hello '+ username); 40 | break; 41 | case 'bye bye': 42 | reply('滚:'+username); 43 | break; 44 | } 45 | }); -------------------------------------------------------------------------------- /axon/pub-emitter/axon-client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | * * # # 5 | * # _oo0oo_ # 6 | * # o8888888o # 7 | * # 88" . "88 # 8 | * # (| -_- |) # 9 | * # 0\ = /0 # 10 | * # ___/`---'\___ # 11 | * # .' \\| |# '. # 12 | * # / \\||| : |||# \ # 13 | * # / _||||| -:- |||||- \ # 14 | * # | | \\\ - #/ | | # 15 | * # | \_| ''\---/'' |_/ | # 16 | * # \ .-\__ '-' ___/-. / # 17 | * # ___'. .' /--.--\ `. .'___ # 18 | * # ."" '< `.___\_<|>_/___.' >' "". # 19 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | # 20 | * # \ \ `_. \_ __\ /__ _/ .-` / / # 21 | * # =====`-.____`.___ \_____/___.-`___.-'===== # 22 | * # `=---=' # 23 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 24 | * # # 25 | * # 佛祖保佑 永无BUG # 26 | * # # 27 | * * 28 | * Created by jianchen on 2018/5/15. 29 | */ 30 | var axon = require('axon'); 31 | var sock = axon.socket('sub-emitter'); 32 | 33 | sock.bind(3000); 34 | 35 | sock.on('user:login', function(user){ 36 | console.log('%s signed in', user.name); 37 | }); 38 | 39 | sock.on('user:*', function(action, user){ 40 | console.log('%s %s', user.name, action); 41 | }); 42 | 43 | sock.on('*', function(event){ 44 | console.log(arguments); 45 | }); -------------------------------------------------------------------------------- /pm2/lib/templates/init-scripts/upstart.tpl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ### BEGIN INIT INFO 3 | # Provides: pm2 4 | # Required-Start: $local_fs $remote_fs $network 5 | # Required-Stop: $local_fs $remote_fs $network 6 | # Default-Start: 2 3 4 5 7 | # Default-Stop: 0 1 6 8 | # Short-Description: PM2 Init script 9 | # Description: PM2 process manager 10 | ### END INIT INFO 11 | 12 | NAME=pm2 13 | PM2=%PM2_PATH% 14 | USER=%USER% 15 | DEFAULT=/etc/default/$NAME 16 | 17 | export PATH=%NODE_PATH%:$PATH 18 | export PM2_HOME="%HOME_PATH%" 19 | 20 | # The following variables can be overwritten in $DEFAULT 21 | 22 | # maximum number of open files 23 | MAX_OPEN_FILES= 24 | 25 | # overwrite settings from default file 26 | if [ -f "$DEFAULT" ]; then 27 | . "$DEFAULT" 28 | fi 29 | 30 | # set maximum open files if set 31 | if [ -n "$MAX_OPEN_FILES" ]; then 32 | ulimit -n $MAX_OPEN_FILES 33 | fi 34 | 35 | get_user_shell() { 36 | local shell=$(getent passwd ${1:-`whoami`} | cut -d: -f7 | sed -e 's/[[:space:]]*$//') 37 | 38 | if [[ $shell == *"/sbin/nologin" ]] || [[ $shell == "/bin/false" ]] || [[ -z "$shell" ]]; 39 | then 40 | shell="/bin/bash" 41 | fi 42 | 43 | echo "$shell" 44 | } 45 | 46 | super() { 47 | local shell=$(get_user_shell $USER) 48 | su - $USER -s $shell -c "PATH=$PATH; PM2_HOME=$PM2_HOME $*" 49 | } 50 | 51 | start() { 52 | echo "Starting $NAME" 53 | super $PM2 resurrect 54 | } 55 | 56 | stop() { 57 | super $PM2 kill 58 | } 59 | 60 | restart() { 61 | echo "Restarting $NAME" 62 | stop 63 | start 64 | } 65 | 66 | reload() { 67 | echo "Reloading $NAME" 68 | super $PM2 reload all 69 | } 70 | 71 | status() { 72 | echo "Status for $NAME:" 73 | super $PM2 list 74 | RETVAL=$? 75 | } 76 | 77 | case "$1" in 78 | start) 79 | start 80 | ;; 81 | stop) 82 | stop 83 | ;; 84 | status) 85 | status 86 | ;; 87 | restart) 88 | restart 89 | ;; 90 | reload) 91 | reload 92 | ;; 93 | force-reload) 94 | reload 95 | ;; 96 | *) 97 | echo "Usage: {start|stop|status|restart|reload|force-reload}" 98 | exit 1 99 | ;; 100 | esac 101 | exit $RETVAL 102 | -------------------------------------------------------------------------------- /axon/pm2-axon/rep.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | * * # # 5 | * # _oo0oo_ # 6 | * # o8888888o # 7 | * # 88" . "88 # 8 | * # (| -_- |) # 9 | * # 0\ = /0 # 10 | * # ___/`---'\___ # 11 | * # .' \\| |# '. # 12 | * # / \\||| : |||# \ # 13 | * # / _||||| -:- |||||- \ # 14 | * # | | \\\ - #/ | | # 15 | * # | \_| ''\---/'' |_/ | # 16 | * # \ .-\__ '-' ___/-. / # 17 | * # ___'. .' /--.--\ `. .'___ # 18 | * # ."" '< `.___\_<|>_/___.' >' "". # 19 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | # 20 | * # \ \ `_. \_ __\ /__ _/ .-` / / # 21 | * # =====`-.____`.___ \_____/___.-`___.-'===== # 22 | * # `=---=' # 23 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 24 | * # # 25 | * # 佛祖保佑 永无BUG # 26 | * # # 27 | * * 28 | * Created by jianchen on 2018/5/15. 29 | */ 30 | var axon = require('pm2-axon'); 31 | var sock = axon.socket('rep'); 32 | var path = require('path') 33 | var fs = require('fs') 34 | let pub_socket_file = path.resolve('./','sock.file') 35 | 36 | sock.connect(pub_socket_file); 37 | 38 | sock.on('message', function(task, username, reply){ 39 | // resize the image 40 | switch (task){ 41 | case 'hello world': 42 | reply('hello '+ username); 43 | break; 44 | case 'bye bye': 45 | reply('滚:'+username); 46 | break; 47 | } 48 | }); -------------------------------------------------------------------------------- /pm2/lib/tools/fmt.js: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------------------------------------------------------------- 2 | // 3 | // fmt.js - Command line output formatting. 4 | // 5 | // Copyright (c) 2012 Andrew Chilton - http://chilts.org/ 6 | // Written by Andrew Chilton 7 | // 8 | // License: http://opensource.org/licenses/MIT 9 | // 10 | // -------------------------------------------------------------------------------------------------------------------- 11 | 12 | var util = require('util'); 13 | 14 | // -------------------------------------------------------------------------------------------------------------------- 15 | 16 | var sep = '==============================================================================='; 17 | var line = '-------------------------------------------------------------------------------'; 18 | var field = ' '; 19 | 20 | // -------------------------------------------------------------------------------------------------------------------- 21 | 22 | // separator 23 | module.exports.separator = function() { 24 | console.log(sep); 25 | }; 26 | 27 | // alias the above 28 | module.exports.sep = module.exports.separator; 29 | 30 | // line 31 | module.exports.line = function() { 32 | console.log(line); 33 | }; 34 | 35 | // title 36 | module.exports.title = function(title) { 37 | var out = '--- ' + title + ' '; 38 | out += line.substr(out.length); 39 | console.log(out); 40 | }; 41 | 42 | // field 43 | module.exports.field = function(key, value) { 44 | console.log('' + key + field.substr(key.length) + ' : ' + value); 45 | }; 46 | 47 | // subfield 48 | module.exports.subfield = function(key, value) { 49 | console.log('- ' + key + field.substr(key.length + 2) + ' : ' + value); 50 | }; 51 | 52 | // list item 53 | module.exports.li = function(msg) { 54 | console.log('* ' + msg); 55 | }; 56 | 57 | // dump 58 | module.exports.dump = function(data, name) { 59 | if ( name ) { 60 | console.log(name + ' :', util.inspect(data, false, null, true)); 61 | } 62 | else { 63 | console.log(util.inspect(data, false, null, true)); 64 | } 65 | }; 66 | 67 | // msg 68 | module.exports.msg = function(msg) { 69 | console.log(msg); 70 | }; 71 | 72 | // -------------------------------------------------------------------------------------------------------------------- 73 | -------------------------------------------------------------------------------- /pm2/lib/tools/promise.min.js: -------------------------------------------------------------------------------- 1 | !function(e){function n(){}function t(e,n){return function(){e.apply(n,arguments)}}function o(e){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],s(e,this)}function i(e,n){for(;3===e._state;)e=e._value;return 0===e._state?void e._deferreds.push(n):(e._handled=!0,void o._immediateFn(function(){var t=1===e._state?n.onFulfilled:n.onRejected;if(null===t)return void(1===e._state?r:u)(n.promise,e._value);var o;try{o=t(e._value)}catch(e){return void u(n.promise,e)}r(n.promise,o)}))}function r(e,n){try{if(n===e)throw new TypeError("A promise cannot be resolved with itself.");if(n&&("object"==typeof n||"function"==typeof n)){var i=n.then;if(n instanceof o)return e._state=3,e._value=n,void f(e);if("function"==typeof i)return void s(t(i,n),e)}e._state=1,e._value=n,f(e)}catch(n){u(e,n)}}function u(e,n){e._state=2,e._value=n,f(e)}function f(e){2===e._state&&0===e._deferreds.length&&o._immediateFn(function(){e._handled||o._unhandledRejectionFn(e._value)});for(var n=0,t=e._deferreds.length;n= 0; i--) { 51 | var proc = data.processes[i]; 52 | 53 | // Strip important environment variables 54 | if (typeof proc.pm2_env === 'undefined' && typeof proc.pm2_env.env === 'undefined') return; 55 | 56 | delete proc.pm2_env.env; 57 | } 58 | } 59 | 60 | res.statusCode = 200; 61 | res.write(JSON.stringify(data)); 62 | return res.end(); 63 | 64 | }) 65 | } 66 | else { 67 | // 404 68 | res.statusCode = 404; 69 | res.write(JSON.stringify({err : '404'})); 70 | return res.end(); 71 | } 72 | }).listen(process.env.PM2_WEB_PORT || cst.WEB_PORT, cst.WEB_IPADDR, function() { 73 | console.log('Web interface listening on %s:%s', cst.WEB_IPADDR, cst.WEB_PORT); 74 | }); 75 | 76 | } 77 | -------------------------------------------------------------------------------- /pm2/lib/Interactor/Password.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 the PM2 project authors. All rights reserved. 3 | * Use of this source code is governed by a license that 4 | * can be found in the LICENSE file. 5 | */ 6 | var crypto = require('crypto'); 7 | 8 | var saltChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 9 | var saltCharsCount = saltChars.length; 10 | 11 | function generateSalt(len) { 12 | if (typeof len != 'number' || len <= 0 || len !== parseInt(len, 10)) throw new Error('Invalid salt length'); 13 | if (crypto.randomBytes) { 14 | return crypto.randomBytes(Math.ceil(len / 2)).toString('hex').substring(0, len); 15 | } else { 16 | for (var i = 0, salt = ''; i < len; i++) { 17 | salt += saltChars.charAt(Math.floor(Math.random() * saltCharsCount)); 18 | } 19 | return salt; 20 | } 21 | } 22 | 23 | function generateHash(algorithm, salt, password, iterations) { 24 | iterations = iterations || 1; 25 | try { 26 | var hash = password; 27 | for(var i=0; i_/___.' >' "". # 19 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | # 20 | * # \ \ `_. \_ __\ /__ _/ .-` / / # 21 | * # =====`-.____`.___ \_____/___.-`___.-'===== # 22 | * # `=---=' # 23 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 24 | * # # 25 | * # 佛祖保佑 永无BUG # 26 | * # # 27 | * * 28 | * Created by jianchen on 2018/5/24. 29 | */ 30 | var EventEmitter2 = require('eventemitter2').EventEmitter2; 31 | var server = new EventEmitter2({ 32 | 33 | // 34 | // set this to `true` to use wildcards. It defaults to `false`. 35 | // 36 | wildcard: true, 37 | 38 | // 39 | // the delimiter used to segment namespaces, defaults to `.`. 40 | // 41 | delimiter: '::', 42 | 43 | // 44 | // set this to `true` if you want to emit the newListener event. The default value is `true`. 45 | // 46 | newListener: false, 47 | 48 | // 49 | // the maximum amount of listeners that can be assigned to an event, default 10. 50 | // 51 | maxListeners: 20, 52 | 53 | // 54 | // show event name in memory leak message when more than maximum amount of listeners is assigned, default false 55 | // 56 | verboseMemoryLeak: false 57 | }); 58 | 59 | server.on('foo', function(value1, value2) { 60 | console.log(this.event, value1, value2); 61 | }); 62 | 63 | server.emit('foo',12,12) -------------------------------------------------------------------------------- /pm2/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Cloning PM2 development 4 | 5 | ```bash 6 | $ git clone https://github.com/Unitech/pm2.git 7 | $ cd pm2 8 | $ git checkout development 9 | $ npm install 10 | ``` 11 | 12 | I recommend having a pm2 alias pointing to the development version to make it easier to use pm2 development: 13 | 14 | ``` 15 | $ cd pm2/ 16 | $ echo "alias pm2='`pwd`/bin/pm2'" >> ~/.bashrc 17 | ``` 18 | 19 | You are now able to use pm2 in dev mode: 20 | 21 | ``` 22 | $ pm2 update 23 | $ pm2 ls 24 | ``` 25 | 26 | ## Project structure 27 | 28 | ``` 29 | . 30 | ├── bin // pm2, pmd, pm2-dev, pm2-docker are there 31 | ├── examples // examples files 32 | ├── lib // source files 33 | ├── pres // presentation files 34 | ├── test // test files 35 | └── types // TypeScript definition files 36 | ``` 37 | 38 | ## Modifying the Daemon 39 | 40 | When you modify the Daemon (lib/Daemon.js, lib/God.js, lib/God/*, lib/Watcher.js), you must restart the pm2 Daemon by doing: 41 | 42 | ``` 43 | $ pm2 update 44 | ``` 45 | 46 | ## Commit rules 47 | 48 | ### Commit message 49 | 50 | A good commit message should describe what changed and why. 51 | 52 | It should : 53 | * contain a short description of the change (preferably 50 characters or less) 54 | * be entirely in lowercase with the exception of proper nouns, acronyms, and the words that refer to code, like function/variable names 55 | * be prefixed with one of the following word 56 | * fix : bug fix 57 | * hotfix : urgent bug fix 58 | * feat : new or updated feature 59 | * docs : documentation updates 60 | * BREAKING : if commit is a breaking change 61 | * refactor : code refactoring (no functional change) 62 | * perf : performance improvement 63 | * style : UX and display updates 64 | * test : tests and CI updates 65 | * chore : updates on build, tools, configuration ... 66 | * Merge branch : when merging branch 67 | * Merge pull request : when merging PR 68 | 69 | ## Tests 70 | 71 | There are two tests type. Programmatic and Behavioral. 72 | The main test command is `npm test` 73 | 74 | ### Programmatic 75 | 76 | Programmatic tests are runned by doing 77 | 78 | ``` 79 | $ bash test/pm2_programmatic_tests.sh 80 | ``` 81 | 82 | This test files are located in test/programmatic/* 83 | 84 | ### Behavioral 85 | 86 | Behavioral tests are runned by doing: 87 | 88 | ``` 89 | $ bash test/pm2_behavior_tests.sh 90 | ``` 91 | 92 | This test files are located in test/bash/* 93 | 94 | ## File of interest 95 | 96 | - `$HOME/.pm2` contain all PM2 related files 97 | - `$HOME/.pm2/logs` contain all applications logs 98 | - `$HOME/.pm2/pids` contain all applications pids 99 | - `$HOME/.pm2/pm2.log` PM2 logs 100 | - `$HOME/.pm2/pm2.pid` PM2 pid 101 | - `$HOME/.pm2/rpc.sock` Socket file for remote commands 102 | - `$HOME/.pm2/pub.sock` Socket file for publishable events 103 | -------------------------------------------------------------------------------- /pm2/lib/tools/isbinaryfile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 the PM2 project authors. All rights reserved. 3 | * Use of this source code is governed by a license that 4 | * can be found in the LICENSE file. 5 | */ 6 | var fs = require('fs'); 7 | var path = require("path"); 8 | var max_bytes = 512; 9 | 10 | module.exports = function(bytes, size) { 11 | // Read the file with no encoding for raw buffer access. 12 | if (size === undefined) { 13 | var file = bytes; 14 | try { 15 | if(!fs.statSync(file).isFile()) return false; 16 | } catch (err) { 17 | // otherwise continue on 18 | } 19 | var descriptor = fs.openSync(file, 'r'); 20 | try { 21 | bytes = new Buffer(max_bytes); 22 | size = fs.readSync(descriptor, bytes, 0, bytes.length, 0); 23 | } finally { 24 | fs.closeSync(descriptor); 25 | } 26 | } 27 | // async version has a function instead of a `size` 28 | else if (typeof size === "function") { 29 | var file = bytes, callback = size; 30 | fs.stat(file, function(err, stat) { 31 | if (err || !stat.isFile()) return callback(null, false); 32 | 33 | fs.open(file, 'r', function(err, descriptor){ 34 | if (err) return callback(err); 35 | var bytes = new Buffer(max_bytes); 36 | // Read the file with no encoding for raw buffer access. 37 | fs.read(descriptor, bytes, 0, bytes.length, 0, function(err, size, bytes){ 38 | fs.close(descriptor, function(err2){ 39 | if (err || err2) 40 | return callback(err || err2); 41 | return callback(null, isBinaryCheck(bytes, size)); 42 | }); 43 | }); 44 | }); 45 | }); 46 | } 47 | 48 | return isBinaryCheck(bytes, size); 49 | } 50 | 51 | function isBinaryCheck(bytes, size) { 52 | if (size === 0) 53 | return false; 54 | 55 | var suspicious_bytes = 0; 56 | var total_bytes = Math.min(size, max_bytes); 57 | 58 | if (size >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) { 59 | // UTF-8 BOM. This isn't binary. 60 | return false; 61 | } 62 | 63 | for (var i = 0; i < total_bytes; i++) { 64 | if (bytes[i] === 0) { // NULL byte--it's binary! 65 | return true; 66 | } 67 | else if ((bytes[i] < 7 || bytes[i] > 14) && (bytes[i] < 32 || bytes[i] > 127)) { 68 | // UTF-8 detection 69 | if (bytes[i] > 193 && bytes[i] < 224 && i + 1 < total_bytes) { 70 | i++; 71 | if (bytes[i] > 127 && bytes[i] < 192) { 72 | continue; 73 | } 74 | } 75 | else if (bytes[i] > 223 && bytes[i] < 240 && i + 2 < total_bytes) { 76 | i++; 77 | if (bytes[i] > 127 && bytes[i] < 192 && bytes[i + 1] > 127 && bytes[i + 1] < 192) { 78 | i++; 79 | continue; 80 | } 81 | } 82 | suspicious_bytes++; 83 | // Read at least 32 bytes before making a decision 84 | if (i > 32 && (suspicious_bytes * 100) / total_bytes > 10) { 85 | return true; 86 | } 87 | } 88 | } 89 | 90 | if ((suspicious_bytes * 100) / total_bytes > 10) { 91 | return true; 92 | } 93 | 94 | return false; 95 | } 96 | -------------------------------------------------------------------------------- /pm2/lib/ProcessContainerFork.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 the PM2 project authors. All rights reserved. 3 | * Use of this source code is governed by a license that 4 | * can be found in the LICENSE file. 5 | */ 6 | // Inject custom modules 7 | if (process.env.pmx !== 'false') { 8 | require('pmx').init({ 9 | transactions: (process.env.km_link === 'true' && (process.env.trace === 'true' || process.env.deep_monitoring === 'true')) || false, 10 | http: process.env.km_link === 'true' || false, 11 | v8: process.env.v8 === 'true' || process.env.deep_monitoring === 'true' || false, 12 | event_loop_dump: process.env.event_loop_inspector === 'true' || process.env.deep_monitoring === 'true' || false, 13 | deep_metrics: process.env.deep_monitoring === 'true' || false 14 | }); 15 | } 16 | 17 | if (typeof(process.env.source_map_support) != "undefined" && 18 | process.env.source_map_support !== "false") { 19 | require('source-map-support').install(); 20 | } 21 | 22 | // Cron restart feature 23 | if (process.env.cron_restart) { 24 | var cron_pattern = process.env.cron_restart; 25 | var cronJob = require('cron').CronJob; 26 | var job = new cronJob({ 27 | cronTime: cron_pattern, 28 | onTick: function () { 29 | if (process.connected && process.send) { 30 | process.send({ 31 | 'cron_restart': 1 32 | }); 33 | } else { 34 | process.exit(0); 35 | } 36 | }, 37 | start: false 38 | }); 39 | job.start(); 40 | } 41 | 42 | 43 | // Rename the process 44 | // 我们在控制台看到的进程名就是在这里命名的 45 | process.title = process.env.PROCESS_TITLE || 'node ' + process.env.pm_exec_path; 46 | 47 | if (process.connected && 48 | process.send && 49 | process.versions && 50 | process.versions.node) 51 | process.send({ 52 | 'node_version': process.versions.node 53 | }); 54 | 55 | // uid/gid management 56 | if (process.env.uid || process.env.gid) { 57 | 58 | if (typeof(process.env.uid) === 'string') { 59 | process.env.HOME = '/home/' + process.env.uid; 60 | process.env.USER = process.env.uid; 61 | } 62 | 63 | try { 64 | if (process.env.gid) 65 | process.setgid(process.env.gid); 66 | if (process.env.uid){ 67 | // If no gid specified - set gid to uid 68 | var new_gid = process.env.gid == null ? process.env.uid : process.env.gid; 69 | process.initgroups(process.env.uid, new_gid); 70 | process.setgid(new_gid); 71 | process.setuid(process.env.uid); 72 | } 73 | } catch(e) { 74 | setTimeout(function() { 75 | console.error('%s on call %s', e.message, e.syscall); 76 | console.error('%s is not accessible', process.env.uid); 77 | return process.exit(1); 78 | }, 100); 79 | } 80 | } 81 | 82 | // Require the real application 真正加载要执行的模块 83 | if (process.env.pm_exec_path) 84 | require('module')._load(process.env.pm_exec_path, null, true); 85 | else 86 | throw new Error('Could not _load() the script'); 87 | 88 | // Change some values to make node think that the user's application 89 | // was started directly such as `node app.js` 90 | process.mainModule = process.mainModule || {}; 91 | process.mainModule.loaded = false; 92 | require.main = process.mainModule; 93 | -------------------------------------------------------------------------------- /demain/domain.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | * * # # 5 | * # _oo0oo_ # 6 | * # o8888888o # 7 | * # 88" . "88 # 8 | * # (| -_- |) # 9 | * # 0\ = /0 # 10 | * # ___/`---'\___ # 11 | * # .' \\| |# '. # 12 | * # / \\||| : |||# \ # 13 | * # / _||||| -:- |||||- \ # 14 | * # | | \\\ - #/ | | # 15 | * # | \_| ''\---/'' |_/ | # 16 | * # \ .-\__ '-' ___/-. / # 17 | * # ___'. .' /--.--\ `. .'___ # 18 | * # ."" '< `.___\_<|>_/___.' >' "". # 19 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | # 20 | * # \ \ `_. \_ __\ /__ _/ .-` / / # 21 | * # =====`-.____`.___ \_____/___.-`___.-'===== # 22 | * # `=---=' # 23 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 24 | * # # 25 | * # 佛祖保佑 永无BUG # 26 | * # # 27 | * * 28 | * Created by jianchen on 2018/5/24. 29 | */ 30 | /*** 31 | * 好忧桑的翻译,凑合着看吧 32 | * Domains provide a way to handle multiple different IO operations as a single group. If any of the event emitters(发出) or callbacks(回收) registered to a domain emit an 'error' event, or throw an error, then the domain object will be notified(通告), rather than losing the context of the error in the process.on('uncaughtException') handler, or causing the program to exit immediately with an error code. 33 | * domain 将系列io操作划分成组的方式进行处理,如果任何事件或者回调注册在domain的error事件上,当错误触发时将会通知到domain的错误处理上, 34 | * 不会被上下文忽略(这种方式之前一般用process.on('uncaughtException')解决)或者引起程序崩溃 35 | */ 36 | 37 | var domain = require('domain'); 38 | var fs = require('fs'); 39 | const d = domain.create(); 40 | 41 | function readSomeFile(filename, cb) { 42 | fs.readFile(filename, 'utf8', d.bind((er, data) => { 43 | // if this throws, it will also be passed to the domain 44 | if(er) throw er; 45 | return cb(er, data ? JSON.parse(data) : null); 46 | })); 47 | } 48 | 49 | d.on('error', (er) => { 50 | // an error occurred somewhere. 51 | // if we throw it now, it will crash the program 52 | // with the normal line number and stack message. 53 | console.log(er) 54 | }); 55 | 56 | readSomeFile('sd', function (err, data) { 57 | console.log('err', err) 58 | }); 59 | 60 | fs.readFile('sd', 'utf8', (err, data) => { 61 | if(err) throw err; 62 | // if this throws, it will also be passed to the domain 63 | return data; 64 | }); 65 | 66 | -------------------------------------------------------------------------------- /pm2/lib/TreeKill.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // From https://raw.githubusercontent.com/pkrumins/node-tree-kill/master/index.js 4 | 5 | var childProcess = require('child_process'); 6 | var spawn = childProcess.spawn; 7 | var exec = childProcess.exec; 8 | 9 | module.exports = function (pid, signal, callback) { 10 | var tree = {}; 11 | var pidsToProcess = {}; 12 | tree[pid] = []; 13 | pidsToProcess[pid] = 1; 14 | 15 | switch (process.platform) { 16 | case 'win32': 17 | exec('taskkill /pid ' + pid + ' /T /F', { windowsHide: true }, callback); 18 | break; 19 | case 'darwin': 20 | buildProcessTree(pid, tree, pidsToProcess, function (parentPid) { 21 | return spawn('pgrep', ['-P', parentPid]); 22 | }, function () { 23 | killAll(tree, signal, callback); 24 | }); 25 | break; 26 | // case 'sunos': 27 | // buildProcessTreeSunOS(pid, tree, pidsToProcess, function () { 28 | // killAll(tree, signal, callback); 29 | // }); 30 | // break; 31 | default: // Linux 32 | buildProcessTree(pid, tree, pidsToProcess, function (parentPid) { 33 | return spawn('ps', ['-o', 'pid', '--no-headers', '--ppid', parentPid]); 34 | }, function () { 35 | killAll(tree, signal, callback); 36 | }); 37 | break; 38 | } 39 | }; 40 | 41 | function killAll (tree, signal, callback) { 42 | var killed = {}; 43 | try { 44 | Object.keys(tree).forEach(function (pid) { 45 | tree[pid].forEach(function (pidpid) { 46 | if (!killed[pidpid]) { 47 | killPid(pidpid, signal); 48 | killed[pidpid] = 1; 49 | } 50 | }); 51 | if (!killed[pid]) { 52 | killPid(pid, signal); 53 | killed[pid] = 1; 54 | } 55 | }); 56 | } catch (err) { 57 | if (callback) { 58 | return callback(err); 59 | } else { 60 | console.error(err); 61 | } 62 | } 63 | if (callback) { 64 | return callback(); 65 | } 66 | } 67 | 68 | function killPid(pid, signal) { 69 | try { 70 | process.kill(parseInt(pid, 10), signal); 71 | } 72 | catch (err) { 73 | if (err.code !== 'ESRCH') 74 | console.error(err); 75 | } 76 | } 77 | 78 | function buildProcessTree (parentPid, tree, pidsToProcess, spawnChildProcessesList, cb) { 79 | var ps = spawnChildProcessesList(parentPid); 80 | var allData = ''; 81 | 82 | ps.on('error', function(err) { 83 | console.error(err); 84 | }); 85 | 86 | if (ps.stdout) { 87 | ps.stdout.on('data', function (data) { 88 | data = data.toString('ascii'); 89 | allData += data; 90 | }); 91 | } 92 | 93 | var onClose = function (code) { 94 | delete pidsToProcess[parentPid]; 95 | 96 | if (code !== 0) { 97 | // no more parent processes 98 | if (Object.keys(pidsToProcess).length == 0) { 99 | cb(); 100 | } 101 | return; 102 | } 103 | var pids = allData.match(/\d+/g) || []; 104 | if (pids.length === 0) 105 | return cb(); 106 | 107 | pids.forEach(function (pid) { 108 | pid = parseInt(pid, 10); 109 | tree[parentPid].push(pid); 110 | tree[pid] = []; 111 | pidsToProcess[pid] = 1; 112 | buildProcessTree(pid, tree, pidsToProcess, spawnChildProcessesList, cb); 113 | }); 114 | }; 115 | 116 | ps.on('close', onClose); 117 | } 118 | -------------------------------------------------------------------------------- /pm2/paths.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 the PM2 project authors. All rights reserved. 3 | * Use of this source code is governed by a license that 4 | * can be found in the LICENSE file. 5 | */ 6 | 7 | var debug = require('debug')('pm2:paths'); 8 | var p = require('path'); 9 | var readLog = require('debug')('pm2-read'); 10 | 11 | function getDefaultPM2Home() { 12 | var PM2_ROOT_PATH; 13 | 14 | if (process.env.PM2_HOME) 15 | PM2_ROOT_PATH = process.env.PM2_HOME; 16 | else if (process.env.HOME && !process.env.HOMEPATH) 17 | PM2_ROOT_PATH = p.resolve(process.env.HOME, '.pm2'); 18 | else if (process.env.HOME || process.env.HOMEPATH) 19 | PM2_ROOT_PATH = p.resolve(process.env.HOMEDRIVE, process.env.HOME || process.env.HOMEPATH, '.pm2'); 20 | else { 21 | console.error('[PM2][Initialization] Environment variable HOME (Linux) or HOMEPATH (Windows) are not set!'); 22 | console.error('[PM2][Initialization] Defaulting to /etc/.pm2'); 23 | PM2_ROOT_PATH = p.resolve('/etc', '.pm2'); 24 | } 25 | 26 | debug('pm2 home resolved to %s', PM2_ROOT_PATH, process.env.HOME); 27 | return PM2_ROOT_PATH; 28 | } 29 | 30 | module.exports = function(PM2_HOME) { 31 | if (!PM2_HOME) 32 | PM2_HOME = getDefaultPM2Home() 33 | readLog('PM2_HOME: %s',PM2_HOME) 34 | var pm2_file_stucture = { 35 | PM2_HOME : PM2_HOME, 36 | PM2_ROOT_PATH : PM2_HOME, 37 | 38 | PM2_CONF_FILE : p.resolve(PM2_HOME, 'conf.js'), 39 | PM2_MODULE_CONF_FILE : p.resolve(PM2_HOME, 'module_conf.json'), 40 | 41 | PM2_LOG_FILE_PATH : p.resolve(PM2_HOME, 'pm2.log'), 42 | PM2_PID_FILE_PATH : p.resolve(PM2_HOME, 'pm2.pid'), 43 | 44 | PM2_RELOAD_LOCKFILE : p.resolve(PM2_HOME, 'reload.lock'), 45 | 46 | DEFAULT_PID_PATH : p.resolve(PM2_HOME, 'pids'), 47 | DEFAULT_LOG_PATH : p.resolve(PM2_HOME, 'logs'), 48 | DEFAULT_MODULE_PATH : p.resolve(PM2_HOME, 'modules'), 49 | KM_ACCESS_TOKEN : p.resolve(PM2_HOME, 'km-access-token'), 50 | DUMP_FILE_PATH : p.resolve(PM2_HOME, 'dump.pm2'), 51 | DUMP_BACKUP_FILE_PATH : p.resolve(PM2_HOME, 'dump.pm2.bak'), 52 | 53 | DAEMON_RPC_PORT : p.resolve(PM2_HOME, 'rpc.sock'), 54 | DAEMON_PUB_PORT : p.resolve(PM2_HOME, 'pub.sock'), 55 | INTERACTOR_RPC_PORT : p.resolve(PM2_HOME, 'interactor.sock'), 56 | 57 | INTERACTOR_LOG_FILE_PATH : p.resolve(PM2_HOME, 'agent.log'), 58 | INTERACTOR_PID_PATH : p.resolve(PM2_HOME, 'agent.pid'), 59 | INTERACTION_CONF : p.resolve(PM2_HOME, 'agent.json5') 60 | }; 61 | 62 | // allow overide of file paths via environnement 63 | var paths = Object.keys(pm2_file_stucture); 64 | paths.forEach(function (key) { 65 | var envKey = key.indexOf('PM2_') > -1 ? key : 'PM2_' + key; 66 | if (process.env[envKey] && key !== 'PM2_HOME' && key !== 'PM2_ROOT_PATH') { 67 | pm2_file_stucture[key] = process.env[envKey]; 68 | } 69 | }); 70 | 71 | if (process.platform === 'win32' || 72 | process.platform === 'win64') { 73 | //@todo instead of static unique rpc/pub file custom with PM2_HOME or UID 74 | pm2_file_stucture.DAEMON_RPC_PORT = '\\\\.\\pipe\\rpc.sock'; 75 | pm2_file_stucture.DAEMON_PUB_PORT = '\\\\.\\pipe\\pub.sock'; 76 | pm2_file_stucture.INTERACTOR_RPC_PORT = '\\\\.\\pipe\\interactor.sock'; 77 | } 78 | 79 | return pm2_file_stucture; 80 | }; 81 | -------------------------------------------------------------------------------- /pm2/lib/binaries/Runtime.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | var commander = require('commander'); 5 | 6 | var debug = require('debug')('pm2:cli'); 7 | var PM2 = require('../..'); 8 | var Log = require('../../lib/API/Log'); 9 | var cst = require('../../constants.js'); 10 | var pkg = require('../../package.json'); 11 | var path = require('path'); 12 | 13 | var pm2; 14 | 15 | // Do not print banner 16 | process.env.PM2_DISCRETE_MODE = true; 17 | 18 | commander.version(pkg.version) 19 | .description('pm2-runtime is an automatic pmx injection that runs in simulated no-daemon environment') 20 | .option('--auto-manage', 'keep application online after command exit') 21 | .option('--fast-boot', 'boot app faster by keeping pm2 runtime online in background (effective at second exit/start)') 22 | .option('--web [port]', 'launch process web api on [port] default to 9615') 23 | .option('--secret [key]', 'keymetrics secret key') 24 | .option('--public [key]', 'keymetrics public key') 25 | .option('--machine-name [name]', 'keymetrics machine name') 26 | .option('--env [name]', 'select env_[name] env variables in process config file') 27 | .option('--watch', 'Watch and Restart') 28 | .option('-i --instances ', 'launch [number] instances with load-balancer') 29 | .usage('pm2-runtime app.js'); 30 | 31 | commander.command('*') 32 | .action(function(cmd){ 33 | pm2 = new PM2.custom({ 34 | pm2_home : path.join(process.env.HOME, '.pm3'), 35 | secret_key : process.env.KEYMETRICS_SECRET || commander.secret, 36 | public_key : process.env.KEYMETRICS_PUBLIC || commander.public, 37 | machine_name : process.env.INSTANCE_NAME || commander.machineName 38 | }); 39 | 40 | pm2.connect(function() { 41 | if (commander.web) { 42 | var port = commander.web === true ? cst.WEB_PORT : commander.web; 43 | pm2.web(port); 44 | } 45 | 46 | pm2.start(cmd, commander, function(err, obj) { 47 | if (process.env.PM2_RUNTIME_DEBUG) { 48 | return pm2.disconnect(function() {}); 49 | } 50 | 51 | if (err) { 52 | console.error(err); 53 | return process.exit(1); 54 | } 55 | 56 | var pm_id = obj[0].pm2_env.pm_id; 57 | 58 | if (commander.instances == undefined) { 59 | return pm2.attach(pm_id, function() { 60 | exitPM2(); 61 | }); 62 | } 63 | 64 | if (commander.json === true) 65 | Log.jsonStream(pm2.Client, pm_id); 66 | else if (commander.format === true) 67 | Log.formatStream(pm2.Client, pm_id, false, 'YYYY-MM-DD-HH:mm:ssZZ'); 68 | else 69 | Log.stream(pm2.Client, 'all', true); 70 | }); 71 | }); 72 | }); 73 | 74 | if (process.argv.length == 2) { 75 | commander.outputHelp(); 76 | process.exit(1); 77 | } 78 | 79 | process.on('SIGINT', function() { 80 | exitPM2(); 81 | }); 82 | 83 | process.on('SIGTERM', function() { 84 | exitPM2(); 85 | }); 86 | 87 | commander.parse(process.argv); 88 | 89 | function exitPM2() { 90 | console.log('Exited at %s', new Date()); 91 | if (commander.autoManage) 92 | return process.exit(0); 93 | 94 | if (commander.fastBoot) { 95 | return pm2.delete('all', function() { 96 | process.exit(0); 97 | }); 98 | } 99 | pm2.kill(function() { 100 | process.exit(0); 101 | }); 102 | } 103 | -------------------------------------------------------------------------------- /pm2/lib/Watcher.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 the PM2 project authors. All rights reserved. 3 | * Use of this source code is governed by a license that 4 | * can be found in the LICENSE file. 5 | */ 6 | var chokidar = require('chokidar'); 7 | var p = require('path'); 8 | var util = require('util'); 9 | var log = require('debug')('pm2:watch'); 10 | 11 | module.exports = function ClusterMode(God) { 12 | /** 13 | * Watch folder for changes and restart 14 | * @method watch 15 | * @param {Object} pm2_env pm2 app environnement 16 | * @return MemberExpression 17 | */ 18 | God.watch = {}; 19 | 20 | God.watch._watchers = {}; 21 | 22 | God.watch.enable = function(pm2_env) { 23 | if (God.watch._watchers[pm2_env.pm_id]) { 24 | God.watch._watchers[pm2_env.pm_id].close(); 25 | God.watch._watchers[pm2_env.pm_id] = null; 26 | delete God.watch._watchers[pm2_env.pm_id]; 27 | } 28 | 29 | log('Initial watch ', pm2_env.watch) 30 | 31 | var watch = pm2_env.watch 32 | 33 | if(typeof watch == 'boolean' || util.isArray(watch) && watch.length === 0) 34 | watch = pm2_env.pm_cwd; 35 | 36 | log('Watching %s', watch); 37 | 38 | var watch_options = { 39 | ignored : pm2_env.ignore_watch || /[\/\\]\.|node_modules/, 40 | persistent : true, 41 | ignoreInitial : true, 42 | cwd: pm2_env.pm_cwd 43 | }; 44 | 45 | if (pm2_env.watch_options) { 46 | watch_options = util._extend(watch_options, pm2_env.watch_options); 47 | } 48 | 49 | log('Watch opts', watch_options); 50 | 51 | var watcher = chokidar.watch(watch, watch_options); 52 | 53 | console.log('[Watch] Start watching', pm2_env.name); 54 | 55 | watcher.on('all', function(event, path) { 56 | var self = this; 57 | 58 | if (self.restarting === true) { 59 | log('Already restarting, skipping'); 60 | return false; 61 | } 62 | 63 | self.restarting = true; 64 | 65 | console.error('Change detected on path %s for app %s - restarting', path, pm2_env.name); 66 | 67 | God.restartProcessName(pm2_env.name, function(err, list) { 68 | self.restarting = false; 69 | 70 | if (err) { 71 | log('Error while restarting', err); 72 | return false; 73 | } 74 | 75 | return log('Process restarted'); 76 | }); 77 | 78 | return false; 79 | }); 80 | 81 | watcher.on('error', function(e) { 82 | console.error(e.stack || e); 83 | }); 84 | 85 | God.watch._watchers[pm2_env.pm_id] = watcher; 86 | 87 | //return God.watch._watchers[pm2_env.name]; 88 | }, 89 | /** 90 | * Description 91 | * @method close 92 | * @param {} id 93 | * @return 94 | */ 95 | God.watch.disableAll = function() { 96 | var watchers = God.watch._watchers; 97 | 98 | console.log('[Watch] PM2 is being killed. Watch is disabled to avoid conflicts'); 99 | for (var i in watchers) { 100 | watchers[i].close && watchers[i].close(); 101 | watchers.splice(i, 1); 102 | } 103 | }, 104 | 105 | God.watch.disable = function(pm2_env) { 106 | var watcher = God.watch._watchers[pm2_env.pm_id] 107 | if (watcher) { 108 | console.log('[Watch] Stop watching', pm2_env.name); 109 | watcher.close(); 110 | delete God.watch._watchers[pm2_env.pm_id]; 111 | return true; 112 | } else { 113 | return false; 114 | } 115 | } 116 | }; 117 | -------------------------------------------------------------------------------- /pm2/lib/Interactor/RemoteActions/CustomActions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 the PM2 project authors. All rights reserved. 3 | * Use of this source code is governed by a license that 4 | * can be found in the LICENSE file. 5 | */ 6 | 7 | var debug = require('debug')('interface:driver'); 8 | var Cipher = require('../Cipher.js'); 9 | 10 | var CustomActions = module.exports = { 11 | /** 12 | * Method to trigger custom actions (axm actions) 13 | */ 14 | axmCustomActions : function() { 15 | var self = this; 16 | 17 | this.socket.data('trigger:action', function(raw_msg) { 18 | var msg = {}; 19 | 20 | if (process.env.NODE_ENV && (process.env.NODE_ENV == 'test' || 21 | process.env.NODE_ENV == 'local_test')) 22 | msg = raw_msg; 23 | else 24 | msg = Cipher.decipherMessage(raw_msg, self.conf.SECRET_KEY); 25 | 26 | if (!msg) return console.error('Error while receiving message! #axmCustomActions'); 27 | 28 | console.log('New remote action %s triggered for process %s', msg.action_name, msg.process_id); 29 | self.pm2_instance.msgProcess({ 30 | id : msg.process_id, 31 | msg : msg.action_name, 32 | opts: msg.opts || null 33 | }, function(err, data) { 34 | if (err) { 35 | return self.socket.send('trigger:action:failure', { 36 | success : false, 37 | err : err.message, 38 | id : msg.process_id, 39 | action_name : msg.action_name 40 | }); 41 | } 42 | console.log('[REVERSE INTERACTOR] Message received from AXM for proc_id : %s and action name %s', 43 | msg.process_id, msg.action_name); 44 | 45 | return self.socket.send('trigger:action:success', { 46 | success : true, 47 | id : msg.process_id, 48 | action_name : msg.action_name 49 | }); 50 | }); 51 | }); 52 | 53 | this.socket.data('trigger:scoped_action', function(raw_msg) { 54 | var msg = {}; 55 | 56 | if (process.env.NODE_ENV && (process.env.NODE_ENV == 'test' || 57 | process.env.NODE_ENV == 'local_test')) 58 | msg = raw_msg; 59 | else 60 | msg = Cipher.decipherMessage(raw_msg, self.conf.SECRET_KEY); 61 | 62 | if (!msg) return console.error('Error while receiving message! #axmCustomActions'); 63 | 64 | console.log('New SCOPED action %s triggered for process %s', msg.action_name, msg.process.pm_id); 65 | 66 | self.pm2_instance.msgProcess({ 67 | id : msg.process.pm_id, 68 | action_name : msg.action_name, 69 | msg : msg.action_name, 70 | opts : msg.options || {}, 71 | uuid : msg.uuid 72 | }, function(err, data) { 73 | if (err) { 74 | return self.socket.send('trigger:action:failure', { 75 | success : false, 76 | err : err.message, 77 | id : msg.process.pm_id, 78 | action_name : msg.action_name 79 | }); 80 | } 81 | console.log('[REVERSE INTERACTOR] Message received from AXM for proc_id : %s and action name %s', 82 | msg.process_id, msg.action_name); 83 | 84 | return self.socket.send('trigger:action:success', { 85 | success : true, 86 | id : msg.process.pm_id, 87 | action_name : msg.action_name 88 | }); 89 | }); 90 | }); 91 | } 92 | }; 93 | -------------------------------------------------------------------------------- /pm2/lib/Interactor/pm2-interface.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 the PM2 project authors. All rights reserved. 3 | * Use of this source code is governed by a license that 4 | * can be found in the LICENSE file. 5 | */ 6 | 7 | /** 8 | * Dependencies 9 | */ 10 | 11 | var axon = require('pm2-axon'); 12 | var cst = require('../../constants.js'); 13 | var util = require('util'); 14 | var rpc = require('pm2-axon-rpc'); 15 | var log = require('debug')('pm2:interface'); 16 | var EventEmitter = require('events').EventEmitter; 17 | 18 | /** 19 | * Export with conf 20 | */ 21 | module.exports = function(opts){ 22 | var sub_port = opts && opts.sub_port || cst.DAEMON_PUB_PORT; 23 | var rpc_port = opts && opts.rpc_port || cst.DAEMON_RPC_PORT; 24 | 25 | return new IPM2(sub_port, rpc_port); 26 | }; 27 | 28 | /** 29 | * IPM2, Pm2 Interface 30 | */ 31 | 32 | var IPM2 = function(sub_port, rpc_port) { 33 | if (!(this instanceof IPM2)) return new IPM2(sub_port, rpc_port); 34 | var self = this; 35 | 36 | EventEmitter.call(this); 37 | 38 | this.sub_port = sub_port; 39 | this.rpc_port = rpc_port; 40 | 41 | 42 | var sub = axon.socket('sub-emitter'); 43 | var sub_sock = this.sub_sock = sub.connect(sub_port); 44 | this.bus = sub; 45 | 46 | var req = axon.socket('req'); 47 | var rpc_sock = this.rpc_sock = req.connect(rpc_port); 48 | this.rpc_client = new rpc.Client(req); 49 | 50 | this.rpc = {}; 51 | 52 | rpc_sock.on('connect', function() { 53 | log('rpc_sock:ready'); 54 | self.emit('rpc_sock:ready'); 55 | generateMethods(function() { 56 | self.emit('ready'); 57 | }); 58 | }); 59 | 60 | rpc_sock.on('close', function() { 61 | log('rpc_sock:closed'); 62 | self.emit('close'); 63 | }); 64 | 65 | rpc_sock.on('reconnect attempt', function() { 66 | log('rpc_sock:reconnecting'); 67 | self.emit('reconnecting'); 68 | }); 69 | 70 | sub_sock.on('connect', function() { 71 | log('sub_sock ready'); 72 | self.emit('sub_sock:ready'); 73 | }); 74 | 75 | sub_sock.on('close', function() { 76 | log('sub_sock:closed'); 77 | self.emit('closed'); 78 | }); 79 | 80 | sub_sock.on('reconnect attempt', function() { 81 | log('sub_sock:reconnecting'); 82 | self.emit('reconnecting'); 83 | }); 84 | 85 | /** 86 | * Disconnect socket connections. This will allow Node to exit automatically. 87 | * Further calls to PM2 from this object will throw an error. 88 | */ 89 | this.disconnect = function () { 90 | self.sub_sock.close(); 91 | self.rpc_sock.close(); 92 | }; 93 | 94 | /** 95 | * Generate method by requesting exposed methods by PM2 96 | * You can now control/interact with PM2 97 | */ 98 | var generateMethods = function(cb) { 99 | log('Requesting and generating RPC methods'); 100 | self.rpc_client.methods(function(err, methods) { 101 | Object.keys(methods).forEach(function(key) { 102 | var method_signature, md; 103 | method_signature = md = methods[key]; 104 | 105 | log('+-- Creating %s method', md.name); 106 | 107 | (function(name) { 108 | self.rpc[name] = function() { 109 | log(name); 110 | var args = Array.prototype.slice.call(arguments); 111 | args.unshift(name); 112 | self.rpc_client.call.apply(self.rpc_client, args); 113 | }; 114 | })(md.name); 115 | 116 | }); 117 | return cb(); 118 | }); 119 | }; 120 | }; 121 | 122 | util.inherits(IPM2, EventEmitter); 123 | -------------------------------------------------------------------------------- /pm2/lib/God/ClusterMode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 the PM2 project authors. All rights reserved. 3 | * Use of this source code is governed by a license that 4 | * can be found in the LICENSE file. 5 | */ 6 | 'use strict'; 7 | 8 | /** 9 | * @file Cluster execution functions related 10 | * @author Alexandre Strzelewicz 11 | * @project PM2 12 | */ 13 | var cluster = require('cluster'); 14 | var cst = require('../../constants.js'); 15 | var Utility = require('../Utility.js'); 16 | var pkg = require('../../package.json'); 17 | var readLog = require('debug')('pm2-read'); 18 | 19 | /** 20 | * Description 21 | * @method exports 22 | * @param {} God 23 | * @return 24 | */ 25 | module.exports = function ClusterMode(God) { 26 | 27 | /** 28 | * For Node apps - Cluster mode 29 | * It will wrap the code and enable load-balancing mode 30 | * @method nodeApp 31 | * @param {} env_copy 32 | * @param {} cb 33 | * @return Literal 34 | */ 35 | God.nodeApp = function nodeApp(env_copy, cb){ 36 | var clu = null; 37 | 38 | console.log('Starting execution sequence in -cluster mode- for app name:%s id:%s', 39 | env_copy.name, 40 | env_copy.pm_id); 41 | 42 | if (env_copy.node_args && Array.isArray(env_copy.node_args)) { 43 | cluster.settings.execArgv = env_copy.node_args; 44 | } 45 | 46 | env_copy._pm2_version = pkg.version; 47 | 48 | try { 49 | // node.js cluster clients can not receive deep-level objects or arrays in the forked process, e.g.: 50 | // { "args": ["foo", "bar"], "env": { "foo1": "bar1" }} will be parsed to 51 | // { "args": "foo, bar", "env": "[object Object]"} 52 | // So we passing a stringified JSON here. 53 | // readLog('pm2_env环境变量,%s',JSON.stringify(env_copy)) 54 | clu = cluster.fork({pm2_env: JSON.stringify(env_copy), windowsHide: true}); 55 | } catch(e) { 56 | readLog('pm2_env环境变量') 57 | God.logAndGenerateError(e); 58 | return cb(e); 59 | } 60 | 61 | clu.pm2_env = env_copy; 62 | 63 | /** 64 | * Broadcast message to God 65 | */ 66 | clu.on('message', function cluMessage(msg) { 67 | /********************************* 68 | * If you edit this function 69 | * Do the same in ForkMode.js ! 70 | *********************************/ 71 | if (msg.data && msg.type) { 72 | return God.bus.emit(msg.type ? msg.type : 'process:msg', { 73 | at : Utility.getDate(), 74 | data : msg.data, 75 | process : { 76 | pm_id : clu.pm2_env.pm_id, 77 | name : clu.pm2_env.name, 78 | rev : (clu.pm2_env.versioning && clu.pm2_env.versioning.revision) ? clu.pm2_env.versioning.revision : null 79 | } 80 | }); 81 | } 82 | else { 83 | 84 | if (typeof msg == 'object' && 'node_version' in msg) { 85 | clu.pm2_env.node_version = msg.node_version; 86 | return false; 87 | } else if (typeof msg == 'object' && 'cron_restart' in msg) { 88 | return God.restartProcessId({ 89 | id : clu.pm2_env.pm_id 90 | }, function() { 91 | console.log('Application %s has been restarted via CRON', clu.pm2_env.name); 92 | }); 93 | } 94 | 95 | return God.bus.emit('process:msg', { 96 | at : Utility.getDate(), 97 | raw : msg, 98 | process : { 99 | pm_id : clu.pm2_env.pm_id, 100 | name : clu.pm2_env.name 101 | } 102 | }); 103 | } 104 | }); 105 | 106 | return cb(null, clu); 107 | }; 108 | }; 109 | -------------------------------------------------------------------------------- /pm2/lib/Interactor/Filter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 the PM2 project authors. All rights reserved. 3 | * Use of this source code is governed by a license that 4 | * can be found in the LICENSE file. 5 | */ 6 | 7 | /** 8 | * @file Filter process and system data to be sent to server 9 | * @author Alexandre Strzelewicz 10 | * @project Interface 11 | */ 12 | 13 | var os = require('os'); 14 | 15 | var cpu_info = { 16 | number : 0, 17 | info : 'no-data' 18 | }; 19 | 20 | try { 21 | cpu_info = { 22 | number : os.cpus().length, 23 | info : os.cpus()[0].model 24 | }; 25 | } catch(e) { 26 | } 27 | 28 | var SERVER_META = { 29 | totalMem : os.totalmem(), 30 | hostname : os.hostname(), 31 | type : os.type(), 32 | platform : os.platform(), 33 | arch : os.arch() 34 | }; 35 | 36 | var Filter = {}; 37 | 38 | Filter.getProcessID = function(machine_name, name, id) { 39 | return machine_name + ':' + name + ':' + id; 40 | }; 41 | 42 | Filter.machineSnapshot = function(processes, conf) { 43 | if (!processes) return null; 44 | 45 | var filter_procs = []; 46 | 47 | processes.forEach(function(proc) { 48 | if (proc.pm2_env.pm_id.toString().indexOf('_old_') == -1) 49 | filter_procs.push({ 50 | pid : proc.pid, 51 | name : proc.pm2_env.name, 52 | interpreter : proc.pm2_env.exec_interpreter, 53 | restart_time : proc.pm2_env.restart_time, 54 | created_at : proc.pm2_env.created_at, 55 | exec_mode : proc.pm2_env.exec_mode, 56 | watching : proc.pm2_env.watch, 57 | pm_uptime : proc.pm2_env.pm_uptime, 58 | status : proc.pm2_env.status, 59 | pm_id : proc.pm2_env.pm_id, 60 | 61 | cpu : Math.floor(proc.monit.cpu) || 0, 62 | memory : Math.floor(proc.monit.memory) || 0, 63 | 64 | versioning : proc.pm2_env.versioning || null, 65 | 66 | node_env : proc.pm2_env.NODE_ENV || null, 67 | 68 | axm_actions : proc.pm2_env.axm_actions || [], 69 | axm_monitor : proc.pm2_env.axm_monitor || {}, 70 | axm_options : proc.pm2_env.axm_options || {}, 71 | axm_dynamic : proc.pm2_env.dynamic || {} 72 | }); 73 | }); 74 | 75 | var node_version = process.version || ''; 76 | 77 | if (node_version != '') { 78 | if (node_version.indexOf('v1.') === 0 || node_version.indexOf('v2.') === 0 || node_version.indexOf('v3.') === 0) 79 | node_version = 'iojs ' + node_version; 80 | } 81 | var username = process.env.SUDO_USER || process.env.C9_USER || process.env.LOGNAME || 82 | process.env.USER || process.env.LNAME || process.env.USERNAME; 83 | 84 | return { 85 | process : filter_procs, 86 | server : { 87 | loadavg : os.loadavg(), 88 | total_mem : SERVER_META.totalMem, 89 | free_mem : os.freemem(), 90 | cpu : cpu_info, 91 | hostname : SERVER_META.hostname, 92 | uptime : os.uptime(), 93 | type : SERVER_META.type, 94 | platform : SERVER_META.platform, 95 | arch : SERVER_META.arch, 96 | user : username, 97 | interaction : conf.REVERSE_INTERACT, 98 | pm2_version : conf.PM2_VERSION, 99 | node_version : node_version 100 | } 101 | }; 102 | }; 103 | 104 | Filter.monitoring = function(processes, conf) { 105 | if (!processes) return null; 106 | 107 | var filter_procs = {}; 108 | 109 | processes.forEach(function(proc) { 110 | filter_procs[Filter.getProcessID(conf.MACHINE_NAME, proc.pm2_env.name,proc.pm2_env.pm_id)] = [ 111 | Math.floor(proc.monit.cpu), 112 | Math.floor(proc.monit.memory) 113 | ]; 114 | }); 115 | 116 | return { 117 | loadavg : os.loadavg(), 118 | total_mem : SERVER_META.totalMem, 119 | free_mem : os.freemem(), 120 | processes : filter_procs 121 | }; 122 | }; 123 | 124 | module.exports = Filter; 125 | -------------------------------------------------------------------------------- /pm2/lib/Interactor/ReverseInteractor.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 the PM2 project authors. All rights reserved. 3 | * Use of this source code is governed by a license that 4 | * can be found in the LICENSE file. 5 | */ 6 | 7 | var debug = require('debug')('interface:driver'); 8 | var nssocket = require('nssocket'); 9 | var Url = require('url'); 10 | var Cipher = require('./Cipher.js'); 11 | var util = require('util'); 12 | 13 | var ReverseInteract = { 14 | changeUrl : function(url) { 15 | if (!this.connected) return; 16 | console.log('[REV] Changing URL to %s', url); 17 | 18 | this.network = Url.parse(url); 19 | this.socket.connect(parseInt(this.network.port), this.network.hostname); 20 | this.socket.reconnect(); 21 | }, 22 | destroy : function() { 23 | this.socket.destroy(); 24 | }, 25 | reconnect : function() { 26 | console.log('[REV] Reconnecting to %s', this.network.hostname); 27 | this.socket.reconnect(); 28 | }, 29 | start : function(opts) { 30 | var self = this; 31 | 32 | if (!opts.url) 33 | throw new Error('url not declared'); 34 | if (!opts.conf) 35 | throw new Error('Conf not passed to ReverseInteractor'); 36 | 37 | this.connected = false; 38 | this.conf = opts.conf; 39 | this.network = Url.parse(opts.url); 40 | this.pm2_instance = opts.conf.pm2_instance; 41 | 42 | this.socket = new nssocket.NsSocket({ 43 | type : 'tcp4', 44 | reconnect : true, 45 | retryInterval : 2000, 46 | max : Infinity, 47 | maxListeners : 50 48 | }); 49 | 50 | this.socket.on('error', function(e) { 51 | self.connected = false; 52 | console.error('[REV] %s', e.message || e); 53 | }); 54 | 55 | this.socket.on('close', function(dt) { 56 | self.connected = false; 57 | }); 58 | 59 | this.socket.on('start', function() { 60 | self.connected = true; 61 | opts.conf.rev_con = true; 62 | console.log('[REV] Connected to %s:%s', self.network.hostname, self.network.port); 63 | }); 64 | 65 | console.log('[REV] Connecting to %s:%s', this.network.hostname, this.network.port); 66 | 67 | this.socket.connect(parseInt(this.network.port), this.network.hostname); 68 | this.onMessage(); 69 | }, 70 | /** 71 | * Listening to remote events from Keymetrics 72 | */ 73 | onMessage : function() { 74 | if (!this.socket) return console.error('Reverse interaction not initialized'); 75 | 76 | /** 77 | * Identify this agent to Keymetrics 78 | * via PUBLIC/PRIVATE key exchange 79 | */ 80 | ReverseInteract.introduceToKeymetrics(); 81 | 82 | ReverseInteract.axmCustomActions(); 83 | 84 | /** 85 | * From Pm2Actions.js 86 | */ 87 | ReverseInteract.pm2Actions(); 88 | 89 | ReverseInteract.pm2ScopedActions(); 90 | 91 | return false; 92 | }, 93 | /** 94 | * First method called to identify this agent 95 | */ 96 | introduceToKeymetrics : function() { 97 | var self = this; 98 | 99 | this.socket.data('ask', function(raw_msg) { 100 | if (process.env.NODE_ENV && process.env.NODE_ENV == 'test') { 101 | // Dont cipher data in test environment 102 | self.socket.send('ask:rep', { 103 | success : true, 104 | machine_name : self.conf.MACHINE_NAME, 105 | public_key : self.conf.PUBLIC_KEY 106 | }); 107 | } else { 108 | var ciphered_data = Cipher.cipherMessage(JSON.stringify({ 109 | machine_name : self.conf.MACHINE_NAME 110 | }), self.conf.SECRET_KEY); 111 | 112 | if (!ciphered_data) 113 | return console.error('Got wrong ciphering data %s %s', self.conf.MACHINE_NAME, self.conf.SECRET_KEY); 114 | 115 | self.socket.send('ask:rep', { 116 | data : ciphered_data, 117 | public_key : self.conf.PUBLIC_KEY 118 | }); 119 | } 120 | return false; 121 | }); 122 | } 123 | }; 124 | 125 | util._extend(ReverseInteract, require('./RemoteActions/Pm2Actions.js')); 126 | util._extend(ReverseInteract, require('./RemoteActions/CustomActions.js')); 127 | 128 | module.exports = ReverseInteract; 129 | -------------------------------------------------------------------------------- /pm2/constants.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 the PM2 project authors. All rights reserved. 3 | * Use of this source code is governed by a license that 4 | * can be found in the LICENSE file. 5 | */ 6 | 7 | var debug = require('debug')('pm2:conf'); 8 | var p = require('path'); 9 | var util = require('util'); 10 | var chalk = require('chalk'); 11 | var semver = require('semver'); 12 | 13 | /** 14 | * Get PM2 path structure 15 | */ 16 | var path_structure = require('./paths.js')(process.env.OVER_HOME); 17 | 18 | /** 19 | * Constants variables used by PM2 20 | */ 21 | var csts = { 22 | PREFIX_MSG : chalk.green('[PM2] '), 23 | PREFIX_MSG_ERR : chalk.red('[PM2][ERROR] '), 24 | PREFIX_MSG_MOD : chalk.bold.green('[PM2][Module] '), 25 | PREFIX_MSG_MOD_ERR : chalk.red('[PM2][Module][ERROR] '), 26 | PREFIX_MSG_WARNING : chalk.yellow('[PM2][WARN] '), 27 | PREFIX_MSG_SUCCESS : chalk.cyan('[PM2] '), 28 | 29 | TEMPLATE_FOLDER : p.join(__dirname, 'lib/templates'), 30 | 31 | APP_CONF_DEFAULT_FILE : 'ecosystem.json', 32 | APP_CONF_TPL : 'ecosystem.tpl', 33 | APP_CONF_TPL_SIMPLE : 'ecosystem-simple.tpl', 34 | SAMPLE_CONF_FILE : 'sample-conf.js', 35 | LOGROTATE_SCRIPT : 'logrotate.d/pm2', 36 | 37 | DOCKERFILE_NODEJS : 'Dockerfiles/Dockerfile-nodejs.tpl', 38 | DOCKERFILE_JAVA : 'Dockerfiles/Dockerfile-java.tpl', 39 | DOCKERFILE_RUBY : 'Dockerfiles/Dockerfile-ruby.tpl', 40 | 41 | SUCCESS_EXIT : 0, 42 | ERROR_EXIT : 1, 43 | CODE_UNCAUGHTEXCEPTION : 1, 44 | 45 | IS_WINDOWS : (process.platform === 'win32' || process.platform === 'win64'), 46 | ONLINE_STATUS : 'online', 47 | STOPPED_STATUS : 'stopped', 48 | STOPPING_STATUS : 'stopping', 49 | LAUNCHING_STATUS : 'launching', 50 | ERRORED_STATUS : 'errored', 51 | ONE_LAUNCH_STATUS : 'one-launch-status', 52 | 53 | CLUSTER_MODE_ID : 'cluster_mode', 54 | FORK_MODE_ID : 'fork_mode', 55 | 56 | LOW_MEMORY_ENVIRONMENT : process.env.PM2_OPTIMIZE_MEMORY || false, 57 | 58 | KEYMETRICS_ROOT_URL : process.env.KEYMETRICS_NODE || 'root.keymetrics.io', 59 | KEYMETRICS_BANNER : '../lib/motd', 60 | KEYMETRICS_UPDATE : '../lib/motd.update', 61 | DEFAULT_MODULE_JSON : 'package.json', 62 | 63 | REMOTE_PORT_TCP : isNaN(parseInt(process.env.KEYMETRICS_PUSH_PORT)) ? 80 : parseInt(process.env.KEYMETRICS_PUSH_PORT), 64 | REMOTE_PORT : 41624, 65 | REMOTE_HOST : 's1.keymetrics.io', 66 | SEND_INTERVAL : 1000, 67 | RELOAD_LOCK_TIMEOUT : parseInt(process.env.PM2_RELOAD_LOCK_TIMEOUT) || 30000, 68 | GRACEFUL_TIMEOUT : parseInt(process.env.PM2_GRACEFUL_TIMEOUT) || 8000, 69 | GRACEFUL_LISTEN_TIMEOUT : parseInt(process.env.PM2_GRACEFUL_LISTEN_TIMEOUT) || 3000, 70 | LOGS_BUFFER_SIZE : 8, 71 | CONTEXT_ON_ERROR : 2, 72 | AGGREGATION_DURATION : process.env.PM2_DEBUG || process.env.NODE_ENV === 'local_test' || process.env.NODE_ENV === 'development' ? 3000 : 5 * 60000, 73 | TRACE_FLUSH_INTERVAL : process.env.PM2_DEBUG || process.env.NODE_ENV === 'local_test' ? 1000 : 60000, 74 | 75 | // Concurrent actions when doing start/restart/reload 76 | CONCURRENT_ACTIONS : (function() { 77 | var default_concurrent_actions = 1; 78 | if (semver.satisfies(process.versions.node, '>= 4.0.0')) 79 | default_concurrent_actions = 2; 80 | var concurrent_actions = parseInt(process.env.PM2_CONCURRENT_ACTIONS) || default_concurrent_actions; 81 | debug('Using %d parallelism (CONCURRENT_ACTIONS)', concurrent_actions); 82 | return concurrent_actions; 83 | })(), 84 | 85 | DEBUG : process.env.PM2_DEBUG || false, 86 | WEB_IPADDR : process.env.PM2_API_IPADDR || '0.0.0.0', 87 | WEB_PORT : parseInt(process.env.PM2_API_PORT) || 9615, 88 | WEB_STRIP_ENV_VARS : process.env.PM2_WEB_STRIP_ENV_VARS || false, 89 | MODIFY_REQUIRE : process.env.PM2_MODIFY_REQUIRE || false, 90 | 91 | WORKER_INTERVAL : process.env.PM2_WORKER_INTERVAL || 30000, 92 | KILL_TIMEOUT : process.env.PM2_KILL_TIMEOUT || 1600, 93 | PM2_PROGRAMMATIC : typeof(process.env.pm_id) !== 'undefined' || process.env.PM2_PROGRAMMATIC, 94 | PM2_LOG_DATE_FORMAT : process.env.PM2_LOG_DATE_FORMAT !== undefined ? process.env.PM2_LOG_DATE_FORMAT : 'YYYY-MM-DD HH:mm:ss' 95 | 96 | }; 97 | 98 | module.exports = util._extend(csts, path_structure); 99 | -------------------------------------------------------------------------------- /vizion/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | * * # # 5 | * # _oo0oo_ # 6 | * # o8888888o # 7 | * # 88" . "88 # 8 | * # (| -_- |) # 9 | * # 0\ = /0 # 10 | * # ___/`---'\___ # 11 | * # .' \\| |# '. # 12 | * # / \\||| : |||# \ # 13 | * # / _||||| -:- |||||- \ # 14 | * # | | \\\ - #/ | | # 15 | * # | \_| ''\---/'' |_/ | # 16 | * # \ .-\__ '-' ___/-. / # 17 | * # ___'. .' /--.--\ `. .'___ # 18 | * # ."" '< `.___\_<|>_/___.' >' "". # 19 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | # 20 | * # \ \ `_. \_ __\ /__ _/ .-` / / # 21 | * # =====`-.____`.___ \_____/___.-`___.-'===== # 22 | * # `=---=' # 23 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 24 | * # # 25 | * # 佛祖保佑 永无BUG # 26 | * # # 27 | * * 28 | * Created by jianchen on 2018/5/25. 29 | */ 30 | var vizion = require('vizion'); 31 | 32 | /** 33 | * Grab metadata for svn/git/hg repositories 34 | */ 35 | console.log('process.cwd()',`${process.cwd()}/../`) 36 | /*** 37 | * 因为我的当前执行目录没有git仓库的索引,所以指到外层去做 38 | */ 39 | vizion.analyze({ 40 | folder :`${process.cwd()}/../` 41 | }, function(err, meta) { 42 | if (err) throw new Error(err); 43 | console.log(meta) 44 | }); 45 | // 46 | // /** 47 | // * Check if a local repository is up to date with its remote 48 | // */ 49 | // vizion.isUpToDate({ 50 | // folder : '/tmp/folder' 51 | // }, function(err, meta) { 52 | // if (err) throw new Error(err); 53 | // 54 | // /** 55 | // * 56 | // * meta = { 57 | // * is_up_to_date : false, 58 | // * new_revision : '6d6932dac9c82f8a29ff40c1d5300569c24aa2c8' 59 | // * current_revision : 'f0a1d45936cf7a3c969e4caba96546fd23255796' 60 | // * } 61 | // * 62 | // */ 63 | // }); 64 | // 65 | // /** 66 | // * Update the local repository to latest commit found on the remote for its current branch 67 | // * - on fail it rollbacks to the latest commit 68 | // */ 69 | // vizion.update({ 70 | // folder : '/tmp/folder' 71 | // }, function(err, meta) { 72 | // if (err) throw new Error(err); 73 | // 74 | // /** 75 | // * 76 | // * meta = { 77 | // * success : true, 78 | // * current_revision : '6d6932dac9c82f8a29ff40c1d5300569c24aa2c8' 79 | // * } 80 | // * 81 | // */ 82 | // }); 83 | // 84 | // /** 85 | // * Revert to a specified commit 86 | // * - Eg: this does a git reset --hard 87 | // */ 88 | // vizion.revertTo({ 89 | // revision : 'f0a1d45936cf7a3c969e4caba96546fd23255796', 90 | // folder : '/tmp/folder' 91 | // }, function(err, data) { 92 | // if (err) throw new Error(err); 93 | // 94 | // /** 95 | // * 96 | // * data = { 97 | // * success : true, 98 | // * } 99 | // * 100 | // */ 101 | // }); 102 | // 103 | // /** 104 | // * If a previous commit exists it checkouts on it 105 | // */ 106 | // vizion.prev({ 107 | // folder : '/tmp/folder' 108 | // }, function(err, meta) { 109 | // if (err) throw new Error(err); 110 | // 111 | // /** 112 | // * 113 | // * meta = { 114 | // * success : true, 115 | // * current_revision : '6d6932dac9c82f8a29ff40c1d5300569c24aa2c8' 116 | // * } 117 | // * 118 | // */ 119 | // }); 120 | // 121 | // /** 122 | // * If a more recent commit exists it chekouts on it 123 | // */ 124 | // vizion.next({ 125 | // folder : '/tmp/folder' 126 | // }, function(err, meta) { 127 | // if (err) throw new Error(err); 128 | // 129 | // /** 130 | // * 131 | // * meta = { 132 | // * success : false, 133 | // * current_revision : '6d6932dac9c82f8a29ff40c1d5300569c24aa2c8' 134 | // * } 135 | // * 136 | // */ 137 | // }); 138 | -------------------------------------------------------------------------------- /pm2/lib/API/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "script": { 3 | "type": "string", 4 | "require": true, 5 | "alias" : "exec" 6 | }, 7 | "args": { 8 | "type": [ 9 | "array", 10 | "string" 11 | ] 12 | }, 13 | "node_args": { 14 | "type": [ 15 | "array", 16 | "string" 17 | ], 18 | "alias": ["interpreterArgs", "interpreter_args"] 19 | }, 20 | "name": { 21 | "type": "string" 22 | }, 23 | "max_memory_restart": { 24 | "type": [ 25 | "string", 26 | "number" 27 | ], 28 | "regex": "^\\d+(G|M|K)?$", 29 | "ext_type": "sbyte", 30 | "desc": "it should be a NUMBER - byte, \"[NUMBER]G\"(Gigabyte), \"[NUMBER]M\"(Megabyte) or \"[NUMBER]K\"(Kilobyte)" 31 | }, 32 | "uid" : { 33 | "type" : "string" 34 | }, 35 | "gid" : { 36 | "type" : "string" 37 | }, 38 | "restart_delay": { 39 | "type" : "number" 40 | }, 41 | "source_map_support" : { 42 | "type": "boolean" 43 | }, 44 | "wait_ready" : { 45 | "type": "boolean" 46 | }, 47 | "disable_source_map_support" : { 48 | "type": "boolean" 49 | }, 50 | "instances": { 51 | "type": "number" 52 | }, 53 | "kill_timeout": { 54 | "type": "number" 55 | }, 56 | "listen_timeout": { 57 | "type": "number" 58 | }, 59 | "port": { 60 | "type": "number" 61 | }, 62 | "log_file": { 63 | "type": [ 64 | "boolean", 65 | "string" 66 | ], 67 | "alias": "log" 68 | }, 69 | "error_file": { 70 | "type": "string", 71 | "alias": ["error", "err", "err_file", "err_log"] 72 | }, 73 | "log_type": { 74 | "type": "string" 75 | }, 76 | "out_file": { 77 | "type": "string", 78 | "alias": ["output", "out", "out_log"] 79 | }, 80 | "pid_file": { 81 | "type": "string", 82 | "alias": "pid" 83 | }, 84 | "cron_restart": { 85 | "type": "string", 86 | "alias": "cron" 87 | }, 88 | "cwd": { 89 | "type": "string" 90 | }, 91 | "merge_logs": { 92 | "type": "boolean", 93 | "alias" : "combine_logs" 94 | }, 95 | "vizion" : { 96 | "type": "boolean", 97 | "default" : true 98 | }, 99 | "pmx" : { 100 | "type": "boolean", 101 | "default" : true 102 | }, 103 | "automation" : { 104 | "type": "boolean", 105 | "default" : true 106 | }, 107 | "autorestart" : { 108 | "type": "boolean", 109 | "default" : true 110 | }, 111 | "treekill" : { 112 | "type": "boolean", 113 | "default" : true 114 | }, 115 | "watch": { 116 | "type": [ 117 | "boolean", 118 | "array", 119 | "string" 120 | ] 121 | }, 122 | "ignore_watch": { 123 | "type": [ 124 | "array", 125 | "string" 126 | ] 127 | }, 128 | "watch_options": { 129 | "type": "object" 130 | }, 131 | "env": { 132 | "type": [ 133 | "object", 134 | "string" 135 | ] 136 | }, 137 | "^env_\\S*$": { 138 | "type": [ 139 | "object", 140 | "string" 141 | ] 142 | }, 143 | "disable_logs" : { 144 | "type": "boolean" 145 | }, 146 | "log_date_format": { 147 | "type": "string" 148 | }, 149 | "min_uptime": { 150 | "type": [ 151 | "number", 152 | "string" 153 | ], 154 | "regex": "^\\d+(h|m|s)?$", 155 | "desc": "it should be a NUMBER - milliseconds, \"[NUMBER]h\"(hours), \"[NUMBER]m\"(minutes) or \"[NUMBER]s\"(seconds)", 156 | "min": 100, 157 | "ext_type": "stime" 158 | }, 159 | "max_restarts": { 160 | "type": "number", 161 | "min": 0 162 | }, 163 | "exec_mode": { 164 | "type": "string", 165 | "regex": "^(cluster|fork)(_mode)?$", 166 | "alias": "executeCommand", 167 | "desc": "it should be \"cluster\"(\"cluster_mode\") or \"fork\"(\"fork_mode\") only" 168 | }, 169 | "exec_interpreter": { 170 | "type": "string", 171 | "alias": "interpreter" 172 | }, 173 | "write": { 174 | "type": "boolean" 175 | }, 176 | "force": { 177 | "type": "boolean" 178 | }, 179 | "append_env_to_name": { 180 | "type": "boolean" 181 | }, 182 | "post_update": { 183 | "type": "array" 184 | }, 185 | "disable_trace": { 186 | "type": [ 187 | "boolean" 188 | ] 189 | }, 190 | "trace": { 191 | "type": [ 192 | "boolean" 193 | ] 194 | }, 195 | "v8": { 196 | "type": [ 197 | "boolean" 198 | ] 199 | }, 200 | "event_loop_inspector": { 201 | "type": [ 202 | "boolean" 203 | ] 204 | }, 205 | "deep_monitoring": { 206 | "type": [ 207 | "boolean" 208 | ] 209 | }, 210 | "increment_var": { 211 | "type": "string" 212 | }, 213 | "instance_var": { 214 | "type": "string", 215 | "default" : "NODE_APP_INSTANCE" 216 | }, 217 | "windowsHide": { 218 | "type": "boolean", 219 | "default" : true 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /pm2/lib/API/Deploy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 the PM2 project authors. All rights reserved. 3 | * Use of this source code is governed by a license that 4 | * can be found in the LICENSE file. 5 | */ 6 | 7 | var fs = require('fs'); 8 | var Deploy = require('pm2-deploy'); 9 | 10 | var cst = require('../../constants.js'); 11 | var Utility = require('../Utility.js'); 12 | var Common = require('../Common.js'); 13 | 14 | function deployHelper() { 15 | console.log(''); 16 | console.log('-----> Helper: Deployment with PM2'); 17 | console.log(''); 18 | console.log(' Generate a sample ecosystem.config.js with the command'); 19 | console.log(' $ pm2 ecosystem'); 20 | console.log(' Then edit the file depending on your needs'); 21 | console.log(''); 22 | console.log(' Commands:'); 23 | console.log(' setup run remote setup commands'); 24 | console.log(' update update deploy to the latest release'); 25 | console.log(' revert [n] revert to [n]th last deployment or 1'); 26 | console.log(' curr[ent] output current release commit'); 27 | console.log(' prev[ious] output previous release commit'); 28 | console.log(' exec|run execute the given '); 29 | console.log(' list list previous deploy commits'); 30 | console.log(' [ref] deploy to [ref], the "ref" setting, or latest tag'); 31 | console.log(''); 32 | console.log(''); 33 | console.log(' Basic Examples:'); 34 | console.log(''); 35 | console.log(' First initialize remote production host:'); 36 | console.log(' $ pm2 deploy ecosystem.config.js production setup'); 37 | console.log(''); 38 | console.log(' Then deploy new code:'); 39 | console.log(' $ pm2 deploy ecosystem.config.js production'); 40 | console.log(''); 41 | console.log(' If I want to revert to the previous commit:'); 42 | console.log(' $ pm2 deploy ecosystem.config.js production revert 1'); 43 | console.log(''); 44 | console.log(' Execute a command on remote server:'); 45 | console.log(' $ pm2 deploy ecosystem.config.js production exec "pm2 restart all"'); 46 | console.log(''); 47 | console.log(' PM2 will look by default to the ecosystem.config.js file so you dont need to give the file name:'); 48 | console.log(' $ pm2 deploy production'); 49 | console.log(' Else you have to tell PM2 the name of your ecosystem file'); 50 | console.log(''); 51 | console.log(' More examples in https://github.com/Unitech/pm2'); 52 | console.log(''); 53 | }; 54 | 55 | module.exports = function(CLI) { 56 | CLI.prototype.deploy = function(file, commands, cb) { 57 | var that = this; 58 | 59 | if (file == 'help') { 60 | deployHelper(); 61 | return cb ? cb() : that.exitCli(cst.SUCCESS_EXIT); 62 | } 63 | 64 | var args = commands.rawArgs; 65 | var env; 66 | 67 | args.splice(0, args.indexOf('deploy') + 1); 68 | 69 | // Find ecosystem file by default 70 | if (!Common.isConfigFile(file)) { 71 | env = args[0]; 72 | var defaultConfigNames = ['ecosystem.config.js', 'ecosystem.json', 'ecosystem.json5', 'package.json']; 73 | file = Utility.whichFileExists(defaultConfigNames); 74 | 75 | if (!file) { 76 | Common.printError('Not any default deployment file exists.'+ 77 | ' Allowed default config file names are: ' + defaultConfigNames.join(', ')); 78 | return cb ? cb('Not any default ecosystem file present') : that.exitCli(cst.ERROR_EXIT); 79 | } 80 | } 81 | else 82 | env = args[1]; 83 | 84 | var json_conf = null; 85 | 86 | try { 87 | json_conf = Common.parseConfig(fs.readFileSync(file), file); 88 | } catch (e) { 89 | Common.printError(e); 90 | return cb ? cb(e) : that.exitCli(cst.ERROR_EXIT); 91 | } 92 | 93 | if (!env) { 94 | deployHelper(); 95 | return cb ? cb() : that.exitCli(cst.SUCCESS_EXIT); 96 | } 97 | 98 | if (!json_conf.deploy || !json_conf.deploy[env]) { 99 | Common.printError('%s environment is not defined in %s file', env, file); 100 | return cb ? cb('%s environment is not defined in %s file') : that.exitCli(cst.ERROR_EXIT); 101 | } 102 | 103 | if (!json_conf.deploy[env]['post-deploy']) { 104 | json_conf.deploy[env]['post-deploy'] = 'pm2 startOrRestart ' + file + ' --env ' + env; 105 | } 106 | 107 | Deploy.deployForEnv(json_conf.deploy, env, args, function(err, data) { 108 | if (err) { 109 | Common.printError('Deploy failed'); 110 | return cb ? cb(err) : that.exitCli(cst.ERROR_EXIT); 111 | } 112 | Common.printOut('--> Success'); 113 | return cb ? cb(null, data) : that.exitCli(cst.SUCCESS_EXIT); 114 | }); 115 | }; 116 | 117 | }; 118 | -------------------------------------------------------------------------------- /pm2/lib/API/Keymetrics/kmapi.js: -------------------------------------------------------------------------------- 1 | var querystring = require('querystring'); 2 | var https = require('https'); 3 | var fs = require('fs'); 4 | var needle = require('needle'); 5 | var url = require('url'); 6 | var cst = require('../../../constants.js'); 7 | 8 | var KM = function() { 9 | this.BASE_URI = 'https://app.keymetrics.io'; 10 | this.CLIENT_ID = '938758711'; 11 | this.CB_URI = 'https://app.keymetrics.io'; 12 | this.ACCESS_TOKEN_FILE = cst.KM_ACCESS_TOKEN; 13 | this.access_token = null; 14 | } 15 | 16 | /** 17 | * @param user_info.username 18 | * @param user_info.password 19 | * @return promise 20 | */ 21 | KM.prototype.loginAndGetAccessToken = function (user_info, cb) { 22 | var that = this; 23 | var URL_AUTH = '/api/oauth/authorize?response_type=token&scope=all&client_id=' + 24 | that.CLIENT_ID + '&redirect_uri=' + that.CB_URI; 25 | 26 | needle.get(that.BASE_URI + URL_AUTH, function(err, res) { 27 | if (err) return cb(err); 28 | 29 | var cookie = res.cookies; 30 | 31 | needle.post(that.BASE_URI + '/api/oauth/login', user_info, { 32 | cookies : cookie 33 | }, function(err, resp, body) { 34 | if (err) return cb(err); 35 | if (body.indexOf('/api/oauth/login') > -1) return cb('Wrong credentials'); 36 | 37 | var location = resp.headers.location; 38 | var redirect = that.BASE_URI + location; 39 | 40 | needle.get(redirect, { 41 | cookies : cookie 42 | }, function(err, res) { 43 | if (err) return cb(err); 44 | var refresh_token = querystring.parse(url.parse(res.headers.location).query).access_token; 45 | 46 | needle.post(that.BASE_URI + '/api/oauth/token', { 47 | client_id : that.CLIENT_ID, 48 | grant_type : 'refresh_token', 49 | refresh_token : refresh_token, 50 | scope : 'all' 51 | }, function(err, res, body) { 52 | if (err) return cb(err); 53 | that.access_token = body.access_token; 54 | return cb(null, body.access_token); 55 | }) 56 | }); 57 | }); 58 | }); 59 | } 60 | 61 | KM.prototype.getLocalAccessToken = function(cb) { 62 | var that = this; 63 | 64 | fs.readFile(that.ACCESS_TOKEN_FILE, function(e, content) { 65 | if (e) return cb(e); 66 | cb(null, content.toString()); 67 | }); 68 | }; 69 | 70 | KM.prototype.saveLocalAccessToken = function(access_token, cb) { 71 | var that = this; 72 | fs.writeFile(that.ACCESS_TOKEN_FILE, access_token, function(e, content) { 73 | if (e) return cb(e); 74 | cb(); 75 | }); 76 | }; 77 | 78 | KM.prototype.getBuckets = function(cb) { 79 | var that = this; 80 | 81 | needle.get(that.BASE_URI + '/api/bucket', { 82 | headers : { 83 | 'Authorization' : 'Bearer ' + that.access_token 84 | }, 85 | json : true 86 | }, function(err, res, body) { 87 | if (err) return cb(err); 88 | return cb(null, body); 89 | }); 90 | } 91 | 92 | /** 93 | * @param user_info.username 94 | * @param user_info.password 95 | * @param user_info.email 96 | * @return promise 97 | */ 98 | KM.prototype.register = function(user_info, cb) { 99 | var that = this; 100 | 101 | needle.post(that.BASE_URI + '/api/oauth/register', user_info, { 102 | json: true, 103 | headers: { 104 | 'X-Register-Provider': 'pm2-register' 105 | } 106 | }, function (err, res, body) { 107 | if (err) return cb(err); 108 | if (body.email && body.email.message) return cb(body.email.message); 109 | if (body.username && body.username.message) return cb(body.username.message); 110 | 111 | cb(null, { 112 | token : body.access_token.token 113 | }) 114 | }); 115 | }; 116 | 117 | KM.prototype.defaultNode = function(cb) { 118 | var that = this; 119 | 120 | needle.get(that.BASE_URI + '/api/node/default', function(err, res, body) { 121 | if (err) return cb(err); 122 | cb(null, url.parse(body.endpoints.web).protocol + '//' + url.parse(body.endpoints.web).hostname); 123 | }); 124 | } 125 | 126 | 127 | KM.prototype.createBucket = function(default_node, bucket_name, cb) { 128 | var that = this; 129 | 130 | needle.post(default_node + '/api/bucket/create_classic', { 131 | name : bucket_name 132 | }, { 133 | json : true, 134 | headers : { 135 | 'Authorization' : 'Bearer ' + that.access_token 136 | } 137 | }, function(err, res, body) { 138 | if (err) return cb(err); 139 | cb(null, body); 140 | }); 141 | } 142 | 143 | KM.prototype.fullCreationFlow = function(user_info, cb) { 144 | var that = this; 145 | 146 | this.register(user_info, function(err, dt) { 147 | if (err) return cb(err); 148 | that.access_token = dt.token; 149 | that.defaultNode(function(err, default_node) { 150 | if (err) return cb(err); 151 | that.createBucket(default_node, 'Node Monitoring', function(err, packet) { 152 | if (err) return cb(err); 153 | return cb(null, { 154 | secret_id : packet.bucket.secret_id, 155 | public_id : packet.bucket.public_id 156 | }); 157 | }); 158 | }) 159 | }); 160 | } 161 | 162 | module.exports = new KM; 163 | -------------------------------------------------------------------------------- /pm2/lib/Worker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 the PM2 project authors. All rights reserved. 3 | * Use of this source code is governed by a license that 4 | * can be found in the LICENSE file. 5 | */ 6 | var vizion = require('vizion'); 7 | var cst = require('../constants.js'); 8 | var async = require('async'); 9 | var debug = require('debug')('pm2:worker'); 10 | var domain = require('domain'); 11 | var readLog = require('debug')('pm2-read'); 12 | /*** 13 | * worker脚本主要用来实时更新版本,内存的监控 14 | * @param God 15 | */ 16 | module.exports = function(God) { 17 | var timer = null; 18 | 19 | God.Worker = {}; 20 | God.Worker.is_running = false; 21 | 22 | var _getProcessById = function(pm_id) { 23 | var proc = God.clusters_db[pm_id]; 24 | return proc ? proc : null; 25 | }; 26 | 27 | var maxMemoryRestart = function(proc_key, cb) { 28 | var proc = _getProcessById(proc_key.pm2_env.pm_id); 29 | 30 | if (!(proc && 31 | proc.pm2_env && 32 | proc_key.monit)) 33 | return cb(); 34 | 35 | if (proc_key.monit.memory !== undefined && 36 | proc.pm2_env.max_memory_restart !== undefined && 37 | proc.pm2_env.max_memory_restart < proc_key.monit.memory && 38 | proc.pm2_env.axm_options && 39 | proc.pm2_env.axm_options.pid === undefined) { 40 | console.log('[PM2][WORKER] Process %s restarted because it exceeds --max-memory-restart value (current_memory=%s max_memory_limit=%s [octets])', proc.pm2_env.pm_id, proc_key.monit.memory, proc.pm2_env.max_memory_restart); 41 | // 如果设置了内存超过某个数值重启,则平滑重启 42 | God.softReloadProcessId({ 43 | id : proc.pm2_env.pm_id 44 | }, function(err, data) { 45 | if (err) 46 | console.error(err.stack || err); 47 | return cb(); 48 | }); 49 | } 50 | else { 51 | return cb(); 52 | } 53 | }; 54 | // 版本跟新,Vizion版本更新同步 55 | var versioningRefresh = function(proc_key, cb) { 56 | var proc = _getProcessById(proc_key.pm2_env.pm_id); 57 | if (!(proc && 58 | proc.pm2_env && 59 | (proc.pm2_env.vizion !== false && proc.pm2_env.vizion != "false") && 60 | proc.pm2_env.versioning && 61 | proc.pm2_env.versioning.repo_path)) { 62 | return cb(); 63 | } 64 | 65 | if (proc.pm2_env.vizion_running === true) 66 | { 67 | debug('Vizion is already running for proc id: %d, skipping this round', proc.pm2_env.pm_id); 68 | return cb(); 69 | } 70 | 71 | proc.pm2_env.vizion_running = true; 72 | var repo_path = proc.pm2_env.versioning.repo_path; 73 | console.log('proc.pm2_env.versioning.repo_path',proc.pm2_env.versioning.repo_path) 74 | vizion.analyze({ 75 | folder: proc.pm2_env.versioning.repo_path 76 | }, 77 | function(err, meta) { 78 | if (err != null) 79 | return cb(); 80 | 81 | proc = _getProcessById(proc_key.pm2_env.pm_id); 82 | 83 | if (!(proc && 84 | proc.pm2_env && 85 | proc.pm2_env.versioning && 86 | proc.pm2_env.versioning.repo_path)) { 87 | console.error('Proc not defined anymore or versioning unknown'); 88 | return cb(); 89 | } 90 | 91 | proc.pm2_env.vizion_running = false; 92 | meta.repo_path = repo_path; 93 | proc.pm2_env.versioning = meta; 94 | debug('[PM2][WORKER] %s parsed for versioning', proc.pm2_env.name); 95 | return cb(); 96 | }); 97 | }; 98 | 99 | // 这个任务主要检查版本和最大内存 100 | var tasks = function() { 101 | if (God.Worker.is_running === true) { 102 | debug('[PM2][WORKER] Worker is already running, skipping this round'); 103 | return false; 104 | } 105 | God.Worker.is_running = true; 106 | readLog('God自身调用getMonitorData') 107 | God.getMonitorData(null, function(err, data) { 108 | if (err || !data || typeof(data) !== 'object') { 109 | God.Worker.is_running = false; 110 | return console.error(err); 111 | } 112 | 113 | async.eachLimit(data, 1, function(proc_key, next) { 114 | if (!proc_key || 115 | !proc_key.pm2_env || 116 | proc_key.pm2_env.pm_id === undefined) 117 | return next(); 118 | 119 | debug('[PM2][WORKER] Processing proc id:', proc_key.pm2_env.pm_id); 120 | 121 | versioningRefresh(proc_key, function() { 122 | maxMemoryRestart(proc_key, function() { 123 | return next(); 124 | }); 125 | }); 126 | }, function(err) { 127 | God.Worker.is_running = false; 128 | readLog('[PM2][WORKER] My job here is done, next job in %d seconds', parseInt(cst.WORKER_INTERVAL / 1000)) 129 | debug('[PM2][WORKER] My job here is done, next job in %d seconds', parseInt(cst.WORKER_INTERVAL / 1000)); 130 | }); 131 | }); 132 | }; 133 | 134 | var wrappedTasks = function() { 135 | var d = domain.create(); 136 | 137 | d.once('error', function(err) { 138 | console.error('[PM2][WORKER] Error caught by domain:\n' + (err.stack || err)); 139 | God.Worker.is_running = false; 140 | }); 141 | 142 | d.run(function() { 143 | tasks(); 144 | }); 145 | }; 146 | 147 | 148 | God.Worker.start = function() { 149 | timer = setInterval(wrappedTasks, cst.WORKER_INTERVAL); 150 | }; 151 | 152 | God.Worker.stop = function() { 153 | if (timer !== null) 154 | clearInterval(timer); 155 | }; 156 | }; 157 | -------------------------------------------------------------------------------- /pm2/lib/API/Interaction.js: -------------------------------------------------------------------------------- 1 | 2 | var cst = require('../../constants.js'); 3 | var Common = require('../Common.js'); 4 | var UX = require('./CliUx'); 5 | var chalk = require('chalk'); 6 | var async = require('async'); 7 | var path = require('path'); 8 | var fs = require('fs'); 9 | var KMDaemon = require('../Interactor/InteractorDaemonizer'); 10 | 11 | module.exports = function(CLI) { 12 | 13 | var installServerMonit = function(CLI, cb) { 14 | if (process.env.NO_SERVER_MONIT || 15 | process.env.NODE_ENV == 'test' || 16 | cst.IS_WINDOWS == true) 17 | return cb(); 18 | 19 | CLI.Client.executeRemote('getMonitorData', {}, function(err, list) { 20 | var installed = list.some(function(app) { 21 | return app.name == 'pm2-server-monit'; 22 | }); 23 | if (installed == false) 24 | CLI.install('pm2-server-monit', cb); 25 | else cb(); 26 | }) 27 | }; 28 | 29 | /** 30 | * Launch interactor 31 | * For programmatic interaction 32 | * http://pm2.keymetrics.io/docs/usage/use-pm2-with-cloud-providers/ 33 | * @method interact 34 | * @param {string} secret_key 35 | * @param {string} public_key 36 | * @param {string} machine_name 37 | */ 38 | CLI.prototype.interact = function(secret_key, public_key, machine_name, cb) { 39 | var that = this; 40 | 41 | if (typeof(machine_name) == 'function') { 42 | cb = machine_name; 43 | machine_name = null; 44 | } 45 | KMDaemon.launchAndInteract(that._conf, { 46 | secret_key : secret_key || null, 47 | public_key : public_key || null, 48 | machine_name : machine_name || null 49 | }, function(err, dt) { 50 | if (err) { 51 | return cb ? cb(err) : that.exitCli(cst.ERROR_EXIT); 52 | } 53 | return cb ? cb(null, dt) : that.exitCli(cst.SUCCESS_EXIT); 54 | }); 55 | }; 56 | 57 | /** 58 | * Aliases 59 | */ 60 | CLI.prototype.link = CLI.prototype.interact; 61 | 62 | CLI.prototype.unlink = function(cb) { 63 | this._pre_interact('delete', cb); 64 | }; 65 | 66 | CLI.prototype.interactInfos = function(cb) { 67 | KMDaemon.getInteractInfo(this._conf, function(err, data) { 68 | if (err) 69 | return cb(Common.retErr(err)); 70 | return cb(null, data); 71 | }); 72 | }; 73 | 74 | // 75 | // Interact 76 | // 77 | CLI.prototype._pre_interact = function(cmd, public_key, machine, info_node) { 78 | var that = this; 79 | 80 | if (cmd == 'stop' || cmd == 'kill') { 81 | console.log(chalk.cyan('[Keymetrics.io]') + ' Stopping agent...'); 82 | that.killInteract(function() { 83 | console.log(chalk.cyan('[Keymetrics.io]') + ' Stopped'); 84 | return process.exit(cst.SUCCESS_EXIT); 85 | }); 86 | return false; 87 | } 88 | 89 | if (cmd == 'info') { 90 | console.log(chalk.cyan('[Keymetrics.io]') + ' Getting agent information...'); 91 | that.interactInfos(function(err, infos) { 92 | if (err) { 93 | console.error(err.message); 94 | return that.exitCli(cst.ERROR_EXIT); 95 | } 96 | console.log(infos); 97 | return that.exitCli(cst.SUCCESS_EXIT); 98 | }); 99 | return false; 100 | } 101 | 102 | if (cmd == 'delete') { 103 | that.killInteract(function() { 104 | try { 105 | fs.unlinkSync(cst.INTERACTION_CONF); 106 | } catch(e) { 107 | console.log(chalk.cyan('[Keymetrics.io]') + ' No interaction config file found'); 108 | return process.exit(cst.SUCCESS_EXIT); 109 | } 110 | console.log(chalk.cyan('[Keymetrics.io]') + ' Agent interaction ended'); 111 | return process.exit(cst.SUCCESS_EXIT); 112 | }); 113 | return false; 114 | } 115 | 116 | if (cmd == 'start' || cmd == 'restart') { 117 | KMDaemon.launchAndInteract(that._conf, { 118 | public_key : null, 119 | secret_key : null, 120 | machine_name : null, 121 | info_node : null 122 | }, function(err, dt) { 123 | if (err) { 124 | Common.printError(err); 125 | return that.exitCli(cst.ERROR_EXIT); 126 | } 127 | return that.exitCli(cst.SUCCESS_EXIT); 128 | }); 129 | } 130 | 131 | if (cmd && !public_key) { 132 | console.error(chalk.cyan('[Keymetrics.io]') + ' Command [%s] unknown or missing public key', cmd); 133 | return process.exit(cst.ERROR_EXIT); 134 | } 135 | 136 | var infos; 137 | 138 | if (!cmd) { 139 | infos = null; 140 | } 141 | else 142 | infos = { 143 | public_key : public_key, 144 | secret_key : cmd, 145 | machine_name : machine, 146 | info_node : info_node.infoNode || null 147 | } 148 | 149 | KMDaemon.launchAndInteract(that._conf, infos, function(err, dt) { 150 | if (err) 151 | return that.exitCli(cst.ERROR_EXIT); 152 | return that.exitCli(cst.SUCCESS_EXIT); 153 | }); 154 | }; 155 | 156 | /** 157 | * Kill interactor 158 | * @method killInteract 159 | */ 160 | CLI.prototype.killInteract = function(cb) { 161 | var that = this; 162 | KMDaemon.killInteractorDaemon(that._conf, function(err) { 163 | return cb ? cb(Common.retErr('Interactor not launched')) : that.exitCli(cst.SUCCESS_EXIT); 164 | }); 165 | }; 166 | 167 | }; 168 | -------------------------------------------------------------------------------- /pm2/lib/API/Modules/Modularizerv1.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 the PM2 project authors. All rights reserved. 3 | * Use of this source code is governed by a license that 4 | * can be found in the LICENSE file. 5 | */ 6 | var shelljs = require('shelljs'); 7 | var path = require('path'); 8 | var fs = require('fs'); 9 | var async = require('async'); 10 | var p = path; 11 | var spawn = require('child_process').spawn; 12 | var chalk = require('chalk'); 13 | var Configuration = require('../../Configuration.js'); 14 | var cst = require('../../../constants.js'); 15 | var Common = require('../../Common'); 16 | var Utility = require('../../Utility.js'); 17 | 18 | var MODULE_CONF_PREFIX = 'module-db'; 19 | 20 | var Modularizer = module.exports = {}; 21 | 22 | function startModule(CLI, opts, cb) { 23 | /** SCRIPT 24 | * Open file and make the script detection smart 25 | */ 26 | 27 | if (!opts.cmd) throw new Error('module package.json not defined'); 28 | if (!opts.development_mode) opts.development_mode = false; 29 | 30 | try { 31 | var package_json = require(opts.cmd); 32 | } catch(e) { 33 | Common.printError(e); 34 | return cb(); 35 | } 36 | 37 | /** 38 | * Script file detection 39 | * 1- *apps* field (default pm2 json configuration) 40 | * 2- *bin* field 41 | * 3- *main* field 42 | */ 43 | if (!package_json.apps) { 44 | package_json.apps = {}; 45 | 46 | if (package_json.bin) { 47 | var bin = Object.keys(package_json.bin)[0]; 48 | 49 | package_json.apps.script = package_json.bin[bin]; 50 | } 51 | else if (package_json.main) { 52 | package_json.apps.script = package_json.main; 53 | } 54 | } 55 | 56 | Common.extend(opts, { 57 | cwd : opts.proc_path, 58 | watch : opts.development_mode, 59 | force_name : package_json.name, 60 | started_as_module : true 61 | }); 62 | 63 | // Start the module 64 | CLI.start(package_json, opts, function(err, data) { 65 | if (err) return cb(err); 66 | return cb(null, data); 67 | }); 68 | }; 69 | 70 | Modularizer.launchModules = function(CLI, cb) { 71 | var module_folder = p.join(cst.PM2_ROOT_PATH, 'node_modules'); 72 | var modules = Configuration.getSync(MODULE_CONF_PREFIX); 73 | 74 | if (!modules) return cb(); 75 | 76 | async.eachLimit(Object.keys(modules), 1, function(module, next) { 77 | var pmod = p.join(module_folder, module, cst.DEFAULT_MODULE_JSON); 78 | 79 | Common.printOut(cst.PREFIX_MSG_MOD + 'Starting module ' + module); 80 | 81 | var opts = {}; 82 | 83 | if (modules[module] != true) { 84 | Common.extend(opts, modules[module]); 85 | } 86 | 87 | Common.extend(opts, { 88 | cmd : pmod, 89 | development_mode : false, 90 | proc_path : p.join(module_folder, module) 91 | }); 92 | 93 | startModule(CLI, opts, function(err, dt) { 94 | if (err) console.error(err); 95 | return next(); 96 | }); 97 | 98 | }, function() { 99 | return cb ? cb(null) : false; 100 | }); 101 | } 102 | 103 | Modularizer.installModule = function(CLI, module_name, opts, cb) { 104 | var proc_path = '', 105 | cmd = '', 106 | conf = {}, 107 | development_mode = false; 108 | 109 | var cli = { 110 | bin : 'npm', 111 | cmd : 'install' 112 | } 113 | 114 | Common.printOut(cst.PREFIX_MSG_MOD + 'Calling ' + chalk.bold.red('[' + cli.bin.toUpperCase() + ']') + ' to install ' + module_name + ' ...'); 115 | 116 | var install_instance = spawn(cst.IS_WINDOWS ? cli.bin + '.cmd' : cli.bin, [cli.cmd, module_name, '--loglevel=error'], { 117 | stdio : 'inherit', 118 | env: process.env, 119 | shell : true, 120 | cwd : cst.PM2_ROOT_PATH 121 | }); 122 | 123 | install_instance.on('close', finalize); 124 | 125 | install_instance.on('error', function (err) { 126 | console.error(err.stack || err); 127 | }); 128 | 129 | function finalize(code) { 130 | if (code != 0) { 131 | return cb(new Error("Installation failed")); 132 | } 133 | 134 | Common.printOut(cst.PREFIX_MSG_MOD + 'Module downloaded'); 135 | 136 | var canonic_module_name = Utility.getCanonicModuleName(module_name); 137 | 138 | proc_path = p.join(cst.PM2_ROOT_PATH, 'node_modules', canonic_module_name); 139 | 140 | cmd = p.join(proc_path, cst.DEFAULT_MODULE_JSON); 141 | 142 | /** 143 | * Append default configuration to module configuration 144 | */ 145 | try { 146 | var conf = JSON.parse(fs.readFileSync(path.join(proc_path, 'package.json')).toString()).config; 147 | if (conf) { 148 | Object.keys(conf).forEach(function(key) { 149 | Configuration.setSyncIfNotExist(canonic_module_name + ':' + key, conf[key]); 150 | }); 151 | } 152 | } catch(e) { 153 | Common.printError(e); 154 | } 155 | 156 | opts = Common.extend(opts, { 157 | cmd : cmd, 158 | development_mode : development_mode, 159 | proc_path : proc_path 160 | }); 161 | 162 | Configuration.set(MODULE_CONF_PREFIX + ':' + canonic_module_name, { 163 | uid : opts.uid, 164 | gid : opts.gid, 165 | version : 0 166 | }, function(err, data) { 167 | 168 | startModule(CLI, opts, function(err, dt) { 169 | if (err) return cb(err); 170 | 171 | if (process.env.PM2_PROGRAMMATIC === 'true') 172 | return cb(null, dt); 173 | 174 | CLI.conf(canonic_module_name, function() { 175 | Common.printOut(cst.PREFIX_MSG_MOD + 'Module successfully installed and launched'); 176 | Common.printOut(cst.PREFIX_MSG_MOD + 'Edit configuration via: `pm2 conf`'); 177 | return cb(null, dt); 178 | }); 179 | }); 180 | }); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /pm2/lib/binaries/DevCLI.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | process.env.PM2_NO_INTERACTION = 'true'; 5 | // Do not print banner 6 | process.env.PM2_DISCRETE_MODE = true; 7 | 8 | var commander = require('commander'); 9 | 10 | var debug = require('debug')('pm2:cli'); 11 | var PM2 = require('../..'); 12 | var Log = require('../API/Log'); 13 | var cst = require('../../constants.js'); 14 | var pkg = require('../../package.json'); 15 | var platform = require('os').platform(); 16 | var moment = require('moment'); 17 | var Common = require('../Common'); 18 | var chalk = require('chalk'); 19 | var path = require('path'); 20 | var fmt = require('../tools/fmt.js'); 21 | var exec = require('child_process').exec; 22 | var os = require('os'); 23 | 24 | commander.version(pkg.version) 25 | .description('pm2-dev monitor for any file changes and automatically restart it') 26 | .option('--raw', 'raw log output') 27 | .option('--timestamp', 'print timestamp') 28 | .option('--node-args ', 'space delimited arguments to pass to node in cluster mode - e.g. --node-args="--debug=7001 --trace-deprecation"') 29 | .option('--ignore [files]', 'files to ignore while watching') 30 | .option('--post-exec [cmd]', 'execute extra command after change detected') 31 | .option('--silent-exec', 'do not output result of post command', false) 32 | .option('--test-mode', 'debug mode for test suit') 33 | .option('--interpreter ', 'the interpreter pm2 should use for executing app (bash, python...)') 34 | .option('--env [name]', 'select env_[name] env variables in process config file') 35 | .option('--auto-exit', 'exit if all processes are errored/stopped or 0 apps launched') 36 | .usage('pm2-dev app.js'); 37 | 38 | var pm2 = new PM2.custom({ 39 | pm2_home : path.join(os.homedir ? os.homedir() : (process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE), '.pm2-dev') 40 | }); 41 | 42 | pm2.connect(function() { 43 | commander.parse(process.argv); 44 | }); 45 | 46 | function postExecCmd(command, cb) { 47 | var exec_cmd = exec(command); 48 | 49 | if (commander.silentExec !== true) { 50 | exec_cmd.stdout.on('data', function(data) { 51 | process.stdout.write(data); 52 | }); 53 | 54 | exec_cmd.stderr.on('data', function(data) { 55 | process.stderr.write(data); 56 | }); 57 | } 58 | 59 | exec_cmd.on('close', function done() { 60 | if (cb) cb(null); 61 | }); 62 | 63 | exec_cmd.on('error', function (err) { 64 | console.error(err.stack || err); 65 | }); 66 | }; 67 | 68 | function run(cmd, opts) { 69 | var timestamp = opts.timestamp; 70 | 71 | opts.watch = true; 72 | opts.autorestart = true; 73 | 74 | if (opts.autoExit) 75 | autoExit(); 76 | 77 | if (opts.ignore) { 78 | opts.ignore_watch = opts.ignore.split(',') 79 | opts.ignore_watch.push('node_modules'); 80 | } 81 | 82 | if (timestamp === true) 83 | timestamp = 'YYYY-MM-DD-HH:mm:ss'; 84 | 85 | pm2.start(cmd, opts, function(err, procs) { 86 | 87 | if (err) { 88 | console.error(err); 89 | pm2.destroy(function() { 90 | process.exit(0); 91 | }); 92 | return false; 93 | } 94 | 95 | if (opts.testMode) { 96 | return pm2.disconnect(function() { 97 | }); 98 | } 99 | 100 | fmt.sep(); 101 | fmt.title('PM2 development mode'); 102 | fmt.field('Apps started', procs.map(function(p) { return p.pm2_env.name } )); 103 | fmt.field('Processes started', chalk.bold(procs.length)); 104 | fmt.field('Watch and Restart', chalk.green('Enabled')); 105 | fmt.field('Ignored folder', opts.ignore_watch || 'node_modules'); 106 | if (opts.postExec) 107 | fmt.field('Post restart cmd', opts.postExec); 108 | fmt.sep(); 109 | 110 | setTimeout(function() { 111 | pm2.Client.launchBus(function(err, bus) { 112 | bus.on('process:event', function(packet) { 113 | if (packet.event == 'online') { 114 | if (opts.postExec) 115 | postExecCmd(opts.postExec); 116 | } 117 | }); 118 | }); 119 | }, 1000); 120 | 121 | Log.devStream(pm2.Client, 'all', opts.raw, timestamp, false); 122 | 123 | process.on('SIGINT', function() { 124 | console.log('>>>>> [PM2 DEV] Stopping current development session'); 125 | pm2.delete('all', function() { 126 | pm2.destroy(function() { 127 | process.exit(0); 128 | }); 129 | }); 130 | }); 131 | 132 | }); 133 | } 134 | 135 | commander.command('*') 136 | .action(function(cmd, opts){ 137 | run(cmd, commander); 138 | }); 139 | 140 | commander.command('start ') 141 | .description('start target config file/script in development mode') 142 | .action(function(cmd, opts) { 143 | run(cmd, commander); 144 | }); 145 | 146 | function exitPM2() { 147 | if (pm2 && pm2.connected == true) { 148 | console.log(chalk.green.bold('>>> Exiting PM2')); 149 | pm2.kill(function() { 150 | process.exit(0); 151 | }); 152 | } 153 | else 154 | process.exit(0); 155 | } 156 | 157 | function autoExit(final) { 158 | setTimeout(function() { 159 | pm2.list(function(err, apps) { 160 | if (err) console.error(err.stack || err); 161 | 162 | var online_count = 0; 163 | 164 | apps.forEach(function(app) { 165 | if (app.pm2_env.status == cst.ONLINE_STATUS || 166 | app.pm2_env.status == cst.LAUNCHING_STATUS) 167 | online_count++; 168 | }); 169 | 170 | if (online_count == 0) { 171 | console.log('0 application online, exiting'); 172 | if (final == true) 173 | process.exit(1); 174 | else 175 | autoExit(true); 176 | return false; 177 | } 178 | autoExit(false); 179 | }); 180 | }, 3000); 181 | } 182 | 183 | if (process.argv.length == 2) { 184 | commander.outputHelp(); 185 | exitPM2(); 186 | } 187 | -------------------------------------------------------------------------------- /pm2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_from": "pm2", 3 | "_id": "pm2@2.10.3", 4 | "_inBundle": false, 5 | "_integrity": "sha1-DzODSgmgYQ22WjoOEKKBwSNWdzI=", 6 | "_location": "/pm2", 7 | "_phantomChildren": {}, 8 | "_requested": { 9 | "type": "tag", 10 | "registry": true, 11 | "raw": "pm2", 12 | "name": "pm2", 13 | "escapedName": "pm2", 14 | "rawSpec": "", 15 | "saveSpec": null, 16 | "fetchSpec": "latest" 17 | }, 18 | "_requiredBy": [ 19 | "#USER", 20 | "/" 21 | ], 22 | "_resolved": "http://r.npm.sankuai.com/pm2/download/pm2-2.10.3.tgz", 23 | "_shasum": "0f33834a09a0610db65a3a0e10a281c123567732", 24 | "_spec": "pm2", 25 | "_where": "/Users/jianchen/WebstormProjects/nodeprocess", 26 | "author": { 27 | "name": "Strzelewicz Alexandre", 28 | "email": "alexandre@keymetrics.io", 29 | "url": "https://keymetrics.io" 30 | }, 31 | "bin": { 32 | "pm2": "./bin/pm2", 33 | "pm2-dev": "./bin/pm2-dev", 34 | "pm2-docker": "./bin/pm2-docker", 35 | "pm2-runtime": "./bin/pm2-runtime" 36 | }, 37 | "bugs": { 38 | "url": "https://github.com/Unitech/pm2/issues" 39 | }, 40 | "bundleDependencies": false, 41 | "contributors": [ 42 | { 43 | "name": "Alex Kocharin", 44 | "email": "alex@kocharin.ru" 45 | }, 46 | { 47 | "name": "Soyuka", 48 | "email": "soyuka@gmail.com" 49 | }, 50 | { 51 | "name": "Joni Shkurti", 52 | "email": "jonishkurti90@gmail.com" 53 | }, 54 | { 55 | "name": "James Ide" 56 | }, 57 | { 58 | "name": "Jun Tjatse", 59 | "email": "thisnamemeansnothing@gmail.com" 60 | }, 61 | { 62 | "name": "Xu Jingxin", 63 | "email": "sailxjx@gmail.com" 64 | }, 65 | { 66 | "name": "Ben Postlethwaite", 67 | "email": "post.ben.here@gmail.com" 68 | }, 69 | { 70 | "name": "Devo.ps", 71 | "email": "contact@devo.ps" 72 | }, 73 | { 74 | "name": "Bret Copeland", 75 | "email": "bret@atlantisflight.org" 76 | }, 77 | { 78 | "name": "John Hurliman", 79 | "email": "jhurliman@jhurliman.org" 80 | }, 81 | { 82 | "name": "TruongSinh Tran-Nguyen", 83 | "email": "i@truongsinh.pro" 84 | }, 85 | { 86 | "name": "Michael Hueuberger", 87 | "email": "michael.heuberger@binarykitchen.com" 88 | }, 89 | { 90 | "name": "Chris Wiggins", 91 | "email": "chris@chriswiggins.co.nz" 92 | } 93 | ], 94 | "dependencies": { 95 | "async": "^2.5", 96 | "blessed": "^0.1.81", 97 | "chalk": "^1.1", 98 | "chokidar": "^2", 99 | "cli-table-redemption": "^1.0.0", 100 | "commander": "2.13.0", 101 | "cron": "^1.3", 102 | "debug": "^3.0", 103 | "eventemitter2": "1.0.5", 104 | "fclone": "1.0.11", 105 | "gkt": "https://tgz.pm2.io/gkt-1.0.0.tgz", 106 | "mkdirp": "0.5.1", 107 | "moment": "^2.19", 108 | "needle": "^2.1.0", 109 | "nssocket": "0.6.0", 110 | "pidusage": "^1.2.0", 111 | "pm2-axon": "3.1.0", 112 | "pm2-axon-rpc": "^0.5.1", 113 | "pm2-deploy": "^0.3.9", 114 | "pm2-multimeter": "^0.1.2", 115 | "pmx": "^1.6", 116 | "promptly": "2.2.0", 117 | "semver": "^5.3", 118 | "shelljs": "0.7.8", 119 | "source-map-support": "^0.5", 120 | "sprintf-js": "1.1.1", 121 | "v8-compile-cache": "^1.1.0", 122 | "vizion": "^0.2", 123 | "yamljs": "^0.3.0" 124 | }, 125 | "deprecated": false, 126 | "description": "Production process manager for Node.JS applications with a built-in load balancer.", 127 | "devDependencies": { 128 | "mocha": "^3.5", 129 | "should": "^11" 130 | }, 131 | "directories": { 132 | "bin": "./bin", 133 | "lib": "./lib", 134 | "example": "./examples" 135 | }, 136 | "engines": { 137 | "node": ">=0.12" 138 | }, 139 | "homepage": "http://pm2.keymetrics.io/", 140 | "keywords": [ 141 | "cli", 142 | "fault tolerant", 143 | "sysadmin", 144 | "tools", 145 | "pm2", 146 | "logs", 147 | "log", 148 | "json", 149 | "express", 150 | "hapi", 151 | "kraken", 152 | "reload", 153 | "load balancer", 154 | "lb", 155 | "load-balancer", 156 | "kubernetes", 157 | "k8s", 158 | "pm2-docker", 159 | "runtime", 160 | "source maps", 161 | "graceful", 162 | "microservice", 163 | "programmatic", 164 | "harmony", 165 | "node-pm2", 166 | "production", 167 | "keymetrics", 168 | "node.js monitoring", 169 | "strong-pm", 170 | "deploy", 171 | "deployment", 172 | "daemon", 173 | "supervisor", 174 | "supervisord", 175 | "nodemon", 176 | "pm2.io", 177 | "ghost", 178 | "ghost production", 179 | "monitoring", 180 | "keymetrics", 181 | "process manager", 182 | "forever", 183 | "profiling", 184 | "probes", 185 | "apm", 186 | "container", 187 | "forever-monitor", 188 | "keep process alive", 189 | "process configuration", 190 | "clustering", 191 | "cluster cli", 192 | "cluster", 193 | "docker", 194 | "cron", 195 | "devops", 196 | "dev ops" 197 | ], 198 | "license": "AGPL-3.0", 199 | "main": "index.js", 200 | "maintainers": [ 201 | { 202 | "name": "tknew", 203 | "email": "strzelewicz.alexandre@gmail.com" 204 | }, 205 | { 206 | "name": "soyuka", 207 | "email": "soyuka@gmail.com" 208 | }, 209 | { 210 | "name": "wallet77", 211 | "email": "wallet77@gmail.com" 212 | }, 213 | { 214 | "name": "vmarchaud", 215 | "email": "contact@vmarchaud.fr" 216 | } 217 | ], 218 | "name": "pm2", 219 | "optionalDependencies": { 220 | "gkt": "https://tgz.pm2.io/gkt-1.0.0.tgz" 221 | }, 222 | "preferGlobal": true, 223 | "repository": { 224 | "type": "git", 225 | "url": "git://github.com/Unitech/pm2.git" 226 | }, 227 | "scripts": { 228 | "bench-pmx": "pm2 delete all; pm2 install pm2-probe; node examples/pmx/app.js; pm2 ls", 229 | "test": "NODE_ENV=test bash test/pm2_check_dependencies.sh && NODE_ENV=test bash test/pm2_programmatic_tests.sh && NODE_ENV=test bash test/pm2_behavior_tests.sh" 230 | }, 231 | "types": "types/index.d.ts", 232 | "version": "2.10.3" 233 | } 234 | -------------------------------------------------------------------------------- /pm2/lib/API/Modules/Modules.js: -------------------------------------------------------------------------------- 1 | 2 | /*************************** 3 | * 4 | * Module methods 5 | * 6 | **************************/ 7 | 8 | var cst = require('../../../constants.js'); 9 | var Common = require('../../Common.js'); 10 | var UX = require('../CliUx'); 11 | var chalk = require('chalk'); 12 | var async = require('async'); 13 | 14 | var shelljs = require('shelljs'); 15 | var path = require('path'); 16 | var fs = require('fs'); 17 | var p = path; 18 | var Configuration = require('../../Configuration.js'); 19 | var Utility = require('../../Utility.js'); 20 | 21 | var MODULE_CONF_PREFIX = 'module-db'; 22 | 23 | var Modularizer = require('./Modularizer.js'); 24 | var ModularizerV1 = require('./Modularizerv1.js'); 25 | 26 | // Special module with post display 27 | function postDisplay(app, cb) { 28 | var that = this; 29 | var retry = 0; 30 | 31 | UX.processing.start('Initializing module'); 32 | 33 | (function detectModuleInit() { 34 | retry++; 35 | if (retry > 12) { 36 | // Module init has timeouted 37 | return displayOrNot(null); 38 | } 39 | that.describe(app.pm_id, function(err, data) { 40 | 41 | if (data && data[0] && data[0].pm2_env && 42 | data[0].pm2_env.axm_options && 43 | data[0].pm2_env.axm_options.human_info) { 44 | return displayOrNot(data[0]); 45 | } 46 | setTimeout(function() { 47 | detectModuleInit(); 48 | }, 300); 49 | }); 50 | })(); 51 | 52 | function displayOrNot(app) { 53 | UX.processing.stop(); 54 | 55 | if (app) { 56 | var module_name = app.name; 57 | var human_info = app.pm2_env.axm_options.human_info; 58 | 59 | UX.postModuleInfos(module_name, human_info); 60 | Common.printOut(chalk.white.italic(' Use `pm2 show %s` to display this helper'), module_name); 61 | Common.printOut(chalk.white.italic(' Use `pm2 logs %s [--lines 1000]` to display logs'), module_name); 62 | Common.printOut(chalk.white.italic(' Use `pm2 monit` to monitor CPU and Memory usage'), module_name); 63 | return cb ? cb(null, app) : that.exitCli(cst.SUCCESS_EXIT); 64 | } 65 | 66 | return cb ? cb(null, { msg : 'Module started' }) : that.speedList(cst.SUCCESS_EXIT); 67 | } 68 | } 69 | 70 | module.exports = function(CLI) { 71 | /** 72 | * Install / Update a module 73 | */ 74 | CLI.prototype.install = function(module_name, opts, cb) { 75 | var that = this; 76 | 77 | if (typeof(opts) == 'function') { 78 | cb = opts; 79 | opts = {}; 80 | } 81 | 82 | // Because for test, allow to install module in V1 way 83 | if (opts.v1) { 84 | Common.printOut('Installing the V1 way...'); 85 | console.log(opts.uid, opts.gid, opts.v1); 86 | ModularizerV1.installModule(this, module_name, opts, function(err, data) { 87 | if (err) { 88 | Common.printError(cst.PREFIX_MSG_ERR + (err.message || err)); 89 | return cb ? cb(Common.retErr(err)) : that.speedList(cst.ERROR_EXIT); 90 | } 91 | 92 | // Check if special module with post_install display 93 | if (data && data[0] && data[0].pm2_env && data[0].pm2_env.PM2_EXTRA_DISPLAY) { 94 | return postDisplay.call(that, data[0].pm2_env, cb); 95 | } 96 | return cb ? cb(null, data) : that.speedList(cst.SUCCESS_EXIT); 97 | }); 98 | return false; 99 | } 100 | 101 | Modularizer.install(this, module_name, opts, function(err, data) { 102 | if (err) { 103 | Common.printError(cst.PREFIX_MSG_ERR + (err.message || err)); 104 | return cb ? cb(Common.retErr(err)) : that.speedList(cst.ERROR_EXIT); 105 | } 106 | 107 | // Check if special module with post_install display 108 | if (data && data[0] && data[0].pm2_env && data[0].pm2_env.PM2_EXTRA_DISPLAY) { 109 | return postDisplay.call(that, data[0].pm2_env, cb); 110 | } 111 | return cb ? cb(null, data) : that.speedList(cst.SUCCESS_EXIT); 112 | }); 113 | }; 114 | 115 | /** 116 | * Uninstall a module 117 | */ 118 | CLI.prototype.uninstall = function(module_name, cb) { 119 | var that = this; 120 | 121 | Modularizer.uninstall(this, module_name, function(err, data) { 122 | if (err) 123 | return cb ? cb(Common.retErr(err)) : that.speedList(cst.ERROR_EXIT); 124 | return cb ? cb(null, data) : that.speedList(cst.SUCCESS_EXIT); 125 | }); 126 | }; 127 | 128 | /** 129 | * Publish module on NPM + Git push 130 | */ 131 | CLI.prototype.publish = function(module_name, cb) { 132 | var that = this; 133 | 134 | Modularizer.publish(function(err, data) { 135 | if (err) 136 | return cb ? cb(Common.retErr(err)) : that.speedList(cst.ERROR_EXIT); 137 | return cb ? cb(null, data) : that.speedList(cst.SUCCESS_EXIT); 138 | }); 139 | }; 140 | 141 | /** 142 | * Publish module on NPM + Git push 143 | */ 144 | CLI.prototype.generateModuleSample = function(app_name, cb) { 145 | var that = this; 146 | 147 | Modularizer.generateSample(app_name, function(err, data) { 148 | if (err) 149 | return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT); 150 | return cb ? cb(null, data) : that.exitCli(cst.SUCCESS_EXIT); 151 | }); 152 | }; 153 | 154 | CLI.prototype.killAllModules = function(cb) { 155 | var that = this; 156 | 157 | this.Client.getAllModulesId(function(err, modules_id) { 158 | async.forEachLimit(modules_id, 1, function(id, next) { 159 | that._operate('deleteProcessId', id, next); 160 | }, function() { 161 | return cb ? cb() : false; 162 | }); 163 | }); 164 | }; 165 | 166 | CLI.prototype.deleteModule = function(module_name, cb) { 167 | var that = this; 168 | 169 | var found_proc = []; 170 | 171 | this.Client.getAllProcess(function(err, procs) { 172 | if (err) { 173 | Common.printError('Error retrieving process list: ' + err); 174 | return cb(Common.retErr(err)); 175 | } 176 | 177 | procs.forEach(function(proc) { 178 | if (proc.pm2_env.name == module_name && proc.pm2_env.pmx_module) { 179 | found_proc.push(proc.pm_id); 180 | } 181 | }); 182 | 183 | if (found_proc.length == 0) 184 | return cb(); 185 | 186 | that._operate('deleteProcessId', found_proc[0], function(err) { 187 | if (err) return cb(Common.retErr(err)); 188 | Common.printOut('In memory process deleted'); 189 | return cb(); 190 | }); 191 | }); 192 | }; 193 | }; 194 | -------------------------------------------------------------------------------- /pm2/lib/API/Monit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 the PM2 project authors. All rights reserved. 3 | * Use of this source code is governed by a license that 4 | * can be found in the LICENSE file. 5 | */ 6 | // pm2-htop 7 | // Library who interacts with PM2 to display processes resources in htop way 8 | // by Strzelewicz Alexandre 9 | 10 | var multimeter = require('pm2-multimeter'); 11 | var os = require('os'); 12 | var p = require('path'); 13 | var chalk = require('chalk'); 14 | 15 | var CliUx = require('./CliUx'); 16 | 17 | var debug = require('debug')('pm2:monit'); 18 | 19 | // Cst for light programs 20 | const RATIO_T1 = Math.floor(os.totalmem() / 500); 21 | // Cst for medium programs 22 | const RATIO_T2 = Math.floor(os.totalmem() / 50); 23 | // Cst for heavy programs 24 | const RATIO_T3 = Math.floor(os.totalmem() / 5); 25 | // Cst for heavy programs 26 | const RATIO_T4 = Math.floor(os.totalmem()); 27 | 28 | var Monit = {}; 29 | 30 | //helper to get bars.length (num bars printed) 31 | Object.size = function(obj) { 32 | var size = 0, key; 33 | for (key in obj) { 34 | if (obj.hasOwnProperty(key)) size++; 35 | } 36 | return size; 37 | }; 38 | 39 | /** 40 | * Reset the monitor through charm, basically \033c 41 | * @param String msg optional message to show 42 | * @return Monit 43 | */ 44 | Monit.reset = function(msg) { 45 | 46 | this.multi.charm.reset(); 47 | 48 | this.multi.write('\x1B[32m⌬ PM2 \x1B[39mmonitoring\x1B[96m (To go further check out https://app.keymetrics.io) \x1B[39m\n\n'); 49 | 50 | if(msg) { 51 | this.multi.write(msg); 52 | } 53 | 54 | this.bars = {}; 55 | 56 | return this; 57 | } 58 | 59 | /** 60 | * Synchronous Monitor init method 61 | * @method init 62 | * @return Monit 63 | */ 64 | Monit.init = function() { 65 | 66 | this.multi = multimeter(process); 67 | 68 | this.multi.on('^C', this.stop); 69 | 70 | this.reset(); 71 | 72 | return this; 73 | } 74 | 75 | /** 76 | * Stops monitor 77 | * @method stop 78 | */ 79 | Monit.stop = function() { 80 | this.multi.charm.destroy(); 81 | process.exit(0); 82 | } 83 | 84 | 85 | /** 86 | * Refresh monitor 87 | * @method refresh 88 | * @param {} processes 89 | * @return this 90 | */ 91 | Monit.refresh = function(processes) { 92 | debug('Monit refresh'); 93 | 94 | if(!processes) { 95 | processes = []; 96 | } 97 | 98 | var num = processes.length; 99 | this.num_bars = Object.size(this.bars); 100 | 101 | if(num !== this.num_bars) { 102 | debug('Monit addProcesses - actual: %s, new: %s', this.num_bars, num); 103 | return this.addProcesses(processes); 104 | } else { 105 | 106 | if(num === 0) { 107 | return; 108 | } 109 | 110 | debug('Monit refresh'); 111 | var proc; 112 | 113 | for(var i = 0; i < num; i++) { 114 | proc = processes[i]; 115 | 116 | //this is to avoid a print issue when the process is restarted for example 117 | //we might also check for the pid but restarted|restarting will be rendered bad 118 | if(this.bars[proc.pm_id] && proc.pm2_env.status !== this.bars[proc.pm_id].status) { 119 | debug('bars for %s does not exists', proc.pm_id); 120 | this.addProcesses(processes); 121 | break; 122 | } 123 | 124 | this.updateBars(proc); 125 | 126 | } 127 | } 128 | 129 | return this; 130 | } 131 | 132 | Monit.addProcess = function(proc, i) { 133 | if(proc.pm_id in this.bars) { 134 | return ; 135 | } 136 | 137 | if (proc.monit.error) 138 | throw new Error(JSON.stringify(proc.monit.error)); 139 | 140 | var process_name = proc.pm2_env.name || p.basename(proc.pm2_env.pm_exec_path); 141 | var status = proc.pm2_env.status == 'online' ? chalk.green.bold('●') : chalk.red.bold('●'); 142 | 143 | this.multi.write(' ' + status + ' ' + chalk.green.bold(process_name)); 144 | this.multi.write('\n'); 145 | this.multi.write('[' + proc.pm2_env.pm_id + '] [' + proc.pm2_env.exec_mode + ']\n'); 146 | 147 | var bar_cpu = this.multi(40, (i * 2) + 3 + i, { 148 | width: 30, 149 | solid: { 150 | text: '|', 151 | foreground: 'white', 152 | background: 'blue' 153 | }, 154 | empty: { 155 | text: ' ' 156 | } 157 | }); 158 | 159 | var bar_memory = this.multi(40, (i * 2) + 4 + i, { 160 | width: 30, 161 | solid: { 162 | text: '|', 163 | foreground: 'white', 164 | background: 'red' 165 | }, 166 | empty: { 167 | text: ' ' 168 | } 169 | }); 170 | 171 | this.bars[proc.pm_id] = { 172 | memory: bar_memory, 173 | cpu: bar_cpu, 174 | status: proc.pm2_env.status 175 | }; 176 | 177 | this.updateBars(proc); 178 | 179 | this.multi.write('\n'); 180 | 181 | return this; 182 | } 183 | 184 | Monit.addProcesses = function(processes) { 185 | 186 | if(!processes) { 187 | processes = []; 188 | } 189 | 190 | this.reset(); 191 | 192 | var num = processes.length; 193 | 194 | if(num > 0) { 195 | for(var i = 0; i < num; i++) { 196 | this.addProcess(processes[i], i); 197 | } 198 | } else { 199 | this.reset('No processes to monit'); 200 | } 201 | 202 | } 203 | 204 | // Draw memory bars 205 | /** 206 | * Description 207 | * @method drawRatio 208 | * @param {} bar_memory 209 | * @param {} memory 210 | * @return 211 | */ 212 | Monit.drawRatio = function(bar_memory, memory) { 213 | var scale = 0; 214 | 215 | if (memory < RATIO_T1) scale = RATIO_T1; 216 | else if (memory < RATIO_T2) scale = RATIO_T2; 217 | else if (memory < RATIO_T3) scale = RATIO_T3; 218 | else scale = RATIO_T4; 219 | 220 | bar_memory.ratio(memory, 221 | scale, 222 | CliUx.bytesToSize(memory, 3)); 223 | }; 224 | 225 | /** 226 | * Updates bars informations 227 | * @param {} proc proc object 228 | * @return this 229 | */ 230 | Monit.updateBars = function(proc) { 231 | if (this.bars[proc.pm_id]) { 232 | if (proc.pm2_env.status !== 'online' || proc.pm2_env.status !== this.bars[proc.pm_id].status) { 233 | this.bars[proc.pm_id].cpu.percent(0, chalk.red(proc.pm2_env.status)); 234 | this.drawRatio(this.bars[proc.pm_id].memory, 0, chalk.red(proc.pm2_env.status)); 235 | } else if (!proc.monit) { 236 | this.bars[proc.pm_id].cpu.percent(0, chalk.red('No data')); 237 | this.drawRatio(this.bars[proc.pm_id].memory, 0, chalk.red('No data')); 238 | } else { 239 | this.bars[proc.pm_id].cpu.percent(proc.monit.cpu); 240 | this.drawRatio(this.bars[proc.pm_id].memory, proc.monit.memory); 241 | } 242 | } 243 | 244 | return this; 245 | } 246 | 247 | module.exports = Monit; -------------------------------------------------------------------------------- /pm2/lib/God/Reload.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 the PM2 project authors. All rights reserved. 3 | * Use of this source code is governed by a license that 4 | * can be found in the LICENSE file. 5 | */ 6 | 'use strict'; 7 | 8 | /** 9 | * @file Reload functions related 10 | * @author Alexandre Strzelewicz 11 | * @project PM2 12 | */ 13 | 14 | var async = require('async'); 15 | var cst = require('../../constants.js'); 16 | var Utility = require('../Utility.js'); 17 | 18 | /** 19 | * softReload will wait permission from process to exit 20 | * @method softReload 21 | * @param {} God 22 | * @param {} id 23 | * @param {} cb 24 | * @return Literal 25 | */ 26 | function softReload(God, id, cb) { 27 | var t_key = '_old_' + id; 28 | 29 | // Move old worker to tmp id 30 | God.clusters_db[t_key] = God.clusters_db[id]; 31 | 32 | delete God.clusters_db[id]; 33 | 34 | var old_worker = God.clusters_db[t_key]; 35 | 36 | // Deep copy 37 | var new_env = Utility.clone(old_worker.pm2_env); 38 | new_env.restart_time += 1; 39 | 40 | // Reset created_at and unstable_restarts 41 | God.resetState(new_env); 42 | 43 | old_worker.pm2_env.pm_id = t_key; 44 | old_worker.pm_id = t_key; 45 | 46 | God.executeApp(new_env, function(err, new_worker) { 47 | if (err) return cb(err); 48 | 49 | var timer = null; 50 | 51 | var onListen = function () { 52 | clearTimeout(timer); 53 | softCleanDeleteProcess(); 54 | console.log('-softReload- New worker listening'); 55 | }; 56 | 57 | // Bind to know when the new process is up 58 | new_worker.once('listening', onListen); 59 | 60 | timer = setTimeout(function() { 61 | new_worker.removeListener('listening', onListen); 62 | softCleanDeleteProcess(); 63 | }, new_env.listen_timeout || cst.GRACEFUL_LISTEN_TIMEOUT); 64 | 65 | // Remove old worker properly 66 | var softCleanDeleteProcess = function () { 67 | var cleanUp = function () { 68 | clearTimeout(timer); 69 | console.log('-softReload- Old worker disconnected'); 70 | return God.deleteProcessId(t_key, cb); 71 | }; 72 | 73 | old_worker.once('disconnect', cleanUp); 74 | 75 | try { 76 | if (old_worker.state != 'dead' && old_worker.state != 'disconnected') 77 | old_worker.send && old_worker.send('shutdown'); 78 | else { 79 | clearTimeout(timer); 80 | console.error('Worker %d is already disconnected', old_worker.pm2_env.pm_id); 81 | return God.deleteProcessId(t_key, cb); 82 | } 83 | } catch(e) { 84 | clearTimeout(timer); 85 | console.error('Worker %d is already disconnected', old_worker.pm2_env.pm_id); 86 | return God.deleteProcessId(t_key, cb); 87 | } 88 | 89 | timer = setTimeout(function () { 90 | old_worker.removeListener('disconnect', cleanUp); 91 | return God.deleteProcessId(t_key, cb); 92 | }, cst.GRACEFUL_TIMEOUT); 93 | return false; 94 | }; 95 | return false; 96 | }); 97 | return false; 98 | }; 99 | 100 | /** 101 | * hardReload will reload without waiting permission from process 102 | * @method hardReload 103 | * @param {} God 104 | * @param {} id 105 | * @param {} cb 106 | * @return Literal 107 | */ 108 | function hardReload(God, id, wait_msg, cb) { 109 | var t_key = '_old_' + id; 110 | 111 | // Move old worker to tmp id 112 | God.clusters_db[t_key] = God.clusters_db[id]; 113 | delete God.clusters_db[id]; 114 | 115 | var old_worker = God.clusters_db[t_key]; 116 | // Deep copy 117 | var new_env = Utility.clone(old_worker.pm2_env); 118 | new_env.restart_time += 1; 119 | 120 | // Reset created_at and unstable_restarts 121 | God.resetState(new_env); 122 | 123 | old_worker.pm2_env.pm_id = t_key; 124 | old_worker.pm_id = t_key; 125 | 126 | new_env.wait_ready = false; 127 | 128 | God.executeApp(new_env, function(err, new_worker) { 129 | if (err) return cb(err); 130 | 131 | var timer = null; 132 | 133 | var onListen = function () { 134 | clearTimeout(timer); 135 | console.log('-reload- New worker listening'); 136 | return God.deleteProcessId(t_key, cb); 137 | }; 138 | 139 | // Bind to know when the new process is up 140 | if (wait_msg == 'listening') 141 | new_worker.once('listening', onListen); 142 | else { 143 | var listener = function (packet) { 144 | if (packet.raw === 'ready' && 145 | packet.process.name === new_worker.pm2_env.name && 146 | packet.process.pm_id === new_worker.pm2_env.pm_id) { 147 | God.bus.removeListener('process:msg', listener) 148 | return onListen(); 149 | } 150 | } 151 | God.bus.on('process:msg', listener); 152 | } 153 | 154 | timer = setTimeout(function() { 155 | if (wait_msg == 'listening') 156 | new_worker.removeListener(wait_msg, onListen); 157 | else 158 | God.bus.removeListener('process:msg', listener) 159 | 160 | return God.deleteProcessId(t_key, cb); 161 | }, new_env.listen_timeout || cst.GRACEFUL_LISTEN_TIMEOUT); 162 | 163 | return false; 164 | }); 165 | return false; 166 | }; 167 | 168 | /** 169 | * Description 170 | * @method exports 171 | * @param {} God 172 | * @return 173 | */ 174 | module.exports = function(God) { 175 | 176 | /** 177 | * GracefulReload 178 | * @method softReloadProcessId 179 | * @param {} id 180 | * @param {} cb 181 | * @return CallExpression 182 | */ 183 | God.softReloadProcessId = function(opts, cb) { 184 | var id = opts.id; 185 | var env = opts.env || {}; 186 | 187 | if (!(id in God.clusters_db)) 188 | return cb(new Error('PM ID unknown')); 189 | 190 | if (God.clusters_db[id].pm2_env.status == cst.ONLINE_STATUS && 191 | God.clusters_db[id].pm2_env.exec_mode == 'cluster_mode' && 192 | !God.clusters_db[id].pm2_env.wait_ready) { 193 | 194 | Utility.extendExtraConfig(God.clusters_db[id], opts); 195 | Utility.extend(God.clusters_db[id].pm2_env.env, opts.env); 196 | 197 | return softReload(God, id, cb); 198 | } 199 | else { 200 | console.log('Process %s in a stopped status, starting it', id); 201 | return God.restartProcessId(opts, cb); 202 | } 203 | }; 204 | 205 | /** 206 | * Reload 207 | * @method reloadProcessId 208 | * @param {} id 209 | * @param {} cb 210 | * @return CallExpression 211 | */ 212 | God.reloadProcessId = function(opts, cb) { 213 | var id = opts.id; 214 | var env = opts.env || {}; 215 | 216 | if (!(id in God.clusters_db)) 217 | return cb(new Error('PM2 ID unknown')); 218 | 219 | if (God.clusters_db[id].pm2_env.status == cst.ONLINE_STATUS && 220 | God.clusters_db[id].pm2_env.exec_mode == 'cluster_mode') { 221 | 222 | Utility.extendExtraConfig(God.clusters_db[id], opts); 223 | Utility.extend(God.clusters_db[id].pm2_env.env, opts.env); 224 | 225 | var wait_msg = God.clusters_db[id].pm2_env.wait_ready ? 'ready' : 'listening'; 226 | return hardReload(God, id, wait_msg, cb); 227 | } 228 | else { 229 | console.log('Process %s in a stopped status, starting it', id); 230 | return God.restartProcessId(opts, cb); 231 | } 232 | }; 233 | 234 | }; 235 | -------------------------------------------------------------------------------- /pm2/lib/binaries/Runtime4Docker.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Specialized PM2 CLI for Containers 5 | */ 6 | var commander = require('commander'); 7 | var debug = require('debug')('pm2:cli'); 8 | var PM2 = require('../..'); 9 | var Log = require('../../lib/API/Log'); 10 | var cst = require('../../constants.js'); 11 | var pkg = require('../../package.json'); 12 | var path = require('path'); 13 | var DEFAULT_FAIL_COUNT = 3; 14 | 15 | process.env.PM2_DISCRETE_MODE = true; 16 | 17 | commander.version(pkg.version) 18 | .description('pm2-runtime is a drop-in replacement Node.js binary for containers') 19 | .option('-i --instances ', 'launch [number] of processes automatically load-balanced. Increase overall performances and performance stability.') 20 | .option('--secret [key]', '[MONITORING] keymetrics secret key') 21 | .option('--no-autorestart', 'start an app without automatic restart') 22 | .option('--node-args ', 'space delimited arguments to pass to node in cluster mode - e.g. --node-args="--debug=7001 --trace-deprecation"') 23 | .option('-n --name ', 'set a for script') 24 | .option('--max-memory-restart ', 'specify max memory amount used to autorestart (in octet or use syntax like 100M)') 25 | .option('-c --cron ', 'restart a running process based on a cron pattern') 26 | .option('--interpreter ', 'the interpreter pm2 should use for executing app (bash, python...)') 27 | .option('--public [key]', '[MONITORING] keymetrics public key') 28 | .option('--machine-name [name]', '[MONITORING] keymetrics machine name') 29 | .option('--trace', 'enable transaction tracing with km') 30 | .option('--v8', 'enable v8 data collecting') 31 | .option('--format', 'output logs formated like key=val') 32 | .option('--raw', 'raw output (default mode)') 33 | .option('--formatted', 'formatted log output |id|app|log') 34 | .option('--json', 'output logs in json format') 35 | .option('--delay ', 'delay start of configuration file by ', 0) 36 | .option('--web [port]', 'launch process web api on [port] (default to 9615)') 37 | .option('--only ', 'only act on one application of configuration') 38 | .option('--no-auto-exit', 'do not exit if all processes are errored/stopped or 0 apps launched') 39 | .option('--env [name]', 'inject env_[name] env variables in process config file') 40 | .option('--watch', 'watch and restart application on file change') 41 | .option('--error ', 'error log file destination (default disabled)', '/dev/null') 42 | .option('--output ', 'output log file destination (default disabled)', '/dev/null') 43 | .option('--deep-monitoring', 'enable all monitoring tools (equivalent to --v8 --event-loop-inspector --trace)') 44 | .allowUnknownOption() 45 | .usage('app.js'); 46 | 47 | commander.command('*') 48 | .action(function(cmd){ 49 | Runtime.instanciate(cmd); 50 | }); 51 | 52 | commander.command('start ') 53 | .description('start an application or json ecosystem file') 54 | .action(function(cmd) { 55 | Runtime.instanciate(cmd); 56 | }); 57 | 58 | if (process.argv.length == 2) { 59 | commander.outputHelp(); 60 | process.exit(1); 61 | } 62 | 63 | var Runtime = { 64 | pm2 : null, 65 | instanciate : function(cmd) { 66 | this.pm2 = new PM2.custom({ 67 | pm2_home : process.env.PM2_HOME || path.join(process.env.HOME, '.pm2'), 68 | secret_key : process.env.KEYMETRICS_SECRET || commander.secret, 69 | public_key : process.env.KEYMETRICS_PUBLIC || commander.public, 70 | machine_name : process.env.INSTANCE_NAME || commander.machineName, 71 | daemon_mode : process.env.PM2_RUNTIME_DEBUG || false 72 | }); 73 | 74 | this.pm2.connect(function(err, pm2_meta) { 75 | if (pm2_meta.new_pm2_instance == false) { 76 | console.warn('[WARN] PM2 Daemon is already running') 77 | } 78 | 79 | process.on('SIGINT', function() { 80 | Runtime.exit(); 81 | }); 82 | 83 | process.on('SIGTERM', function() { 84 | Runtime.exit(); 85 | }); 86 | 87 | Runtime.startLogStreaming(); 88 | Runtime.startApp(cmd, function(err) { 89 | if (err) { 90 | console.error(err.message || err); 91 | return Runtime.exit(); 92 | } 93 | }); 94 | }); 95 | }, 96 | 97 | /** 98 | * Log Streaming Management 99 | */ 100 | startLogStreaming : function() { 101 | if (commander.json === true) 102 | Log.jsonStream(this.pm2.Client, 'all'); 103 | else if (commander.format === true) 104 | Log.formatStream(this.pm2.Client, 'all', false, 'YYYY-MM-DD-HH:mm:ssZZ'); 105 | else 106 | Log.stream(this.pm2.Client, 'all', !commander.formatted, commander.timestamp, true); 107 | }, 108 | 109 | /** 110 | * Application Startup 111 | */ 112 | startApp : function(cmd, cb) { 113 | function exec() { 114 | this.pm2.start(cmd, commander, function(err, obj) { 115 | if (err) 116 | return cb(err); 117 | if (obj && obj.length == 0) 118 | return cb(new Error('Failed to start application')) 119 | 120 | if (commander.web) { 121 | var port = commander.web === true ? cst.WEB_PORT : commander.web; 122 | Runtime.pm2.web(port); 123 | } 124 | 125 | if (commander.autoExit) { 126 | setTimeout(function() { 127 | Runtime.autoExitWorker(); 128 | }, 4000); 129 | } 130 | 131 | // For Testing purpose (allow to auto exit CLI) 132 | if (process.env.PM2_RUNTIME_DEBUG) 133 | Runtime.pm2.disconnect(function() {}); 134 | 135 | return cb(null, obj); 136 | }); 137 | } 138 | // via --delay option 139 | setTimeout(exec.bind(this), commander.delay * 1000); 140 | }, 141 | 142 | /** 143 | * Exit runtime mgmt 144 | */ 145 | exit : function(code) { 146 | if (!this.pm2) return process.exit(1); 147 | 148 | this.pm2.kill(function() { 149 | process.exit(code || 0); 150 | }); 151 | }, 152 | 153 | /** 154 | * Exit current PM2 instance if 0 app is online 155 | * function activated via --auto-exit 156 | */ 157 | autoExitWorker : function(fail_count) { 158 | var interval = 2000; 159 | 160 | if (typeof(fail_count) =='undefined') 161 | fail_count = DEFAULT_FAIL_COUNT; 162 | 163 | var timer = setTimeout(function () { 164 | Runtime.pm2.list(function (err, apps) { 165 | if (err) { 166 | console.error('Could not run pm2 list'); 167 | return Runtime.autoExitWorker(); 168 | } 169 | 170 | var appOnline = 0; 171 | 172 | apps.forEach(function (app) { 173 | if (app.pm2_env.status === cst.ONLINE_STATUS || 174 | app.pm2_env.status === cst.LAUNCHING_STATUS) { 175 | appOnline++; 176 | } 177 | }); 178 | 179 | if (appOnline === 0) { 180 | console.log('0 application online, retry =', fail_count); 181 | if (fail_count <= 0) 182 | return Runtime.exit(2); 183 | return Runtime.autoExitWorker(--fail_count); 184 | } 185 | 186 | Runtime.autoExitWorker(); 187 | }); 188 | }, interval); 189 | 190 | timer.unref(); 191 | } 192 | } 193 | 194 | commander.parse(process.argv); 195 | --------------------------------------------------------------------------------