├── config.json
├── .idea
├── encodings.xml
├── jsLibraryMappings.xml
├── razzmaster.iml
└── libraries
│ └── razzmaster_node_modules.xml
├── .gitignore
├── ping.js
├── package.json
├── LICENSE
├── info.js
├── blink.js
├── common.js
├── main.js
├── arp.js
├── install.js
├── find.js
└── README.md
/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "packages":[
3 | "git",
4 | "curl"
5 | ]
6 | }
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/jsLibraryMappings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/razzmaster.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/razzmaster_node_modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # node-waf configuration
20 | .lock-wscript
21 |
22 | # Compiled binary addons (http://nodejs.org/api/addons.html)
23 | build/Release
24 |
25 | # Dependency directory
26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
27 | node_modules
28 |
--------------------------------------------------------------------------------
/ping.js:
--------------------------------------------------------------------------------
1 | /* copied from adafruit
2 | * Created by josh on 6/22/15.
3 | */
4 |
5 | var spawn = require('child_process').spawn,
6 | os = require('os').platform();
7 |
8 | function ping(address, cb) {
9 |
10 | var p;
11 |
12 | if (os.indexOf('linux') === 0) {
13 | p = spawn('/bin/ping', ['-o', '-w 1', '-c 1', address]);
14 | } else if (os.indexOf('win') === 0) {
15 | p = spawn('C:/windows/system32/ping.exe', ['-w', '1000', address]);
16 | } else if (os.indexOf('darwin') === 0) {
17 | p = spawn('/sbin/ping', ['-o', '-t 1', '-c 1', address]);
18 | }
19 |
20 | if(!p) return cb();
21 |
22 | p.once('exit', function(code) {
23 | cb();
24 | });
25 |
26 | }
27 |
28 | exports = module.exports = ping;
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "razzmaster",
3 | "version": "0.0.9",
4 | "description": "Find and set up new Raspberry Pis on your network",
5 | "bin":"./main.js",
6 | "main": "find.js",
7 | "scripts": {
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/joshmarinacci/razzmaster.git"
13 | },
14 | "author": {
15 | "name": "Josh Marinacci",
16 | "email": "joshua@marinacci.org"
17 | },
18 | "license": "ISC",
19 | "bugs": {
20 | "url": "https://github.com/joshmarinacci/razzmaster/issues"
21 | },
22 | "homepage": "https://github.com/joshmarinacci/razzmaster#readme",
23 | "dependencies": {
24 | "ip": "^0.3.3",
25 | "q": "^1.4.1",
26 | "ssh2": "^0.4.9"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Josh Marinacci
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/info.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by josh on 1/14/16.
3 | */
4 | var common = require('./common');
5 | var Client = require('ssh2').Client;
6 | var args = common.splitArgs();
7 |
8 | if(!common.checkArgs(args)) return printHelp();
9 |
10 | function printHelp() {
11 | console.log("usage");
12 | console.log("razzmaster info --host 192.168.1.3");
13 | console.log(" --username pi");
14 | console.log(" --password mypass");
15 | }
16 |
17 |
18 |
19 | if(!args.port) args.port = 22;
20 | if(!args.username) {
21 | console.log("using default username: 'pi'");
22 | args.username = 'pi';
23 | }
24 | if(!args.password) {
25 | console.log("using default password: 'raspberry'");
26 | args.password = 'raspberry';
27 | }
28 |
29 |
30 | var conn = new Client();
31 |
32 |
33 | var get_model = 'cat /sys/firmware/devicetree/base/model; echo \n';
34 | var get_version = 'cat /etc/os-release; echo \n'
35 | var uname = 'uname -a; echo \n';
36 |
37 | function execRemote(args) {
38 | return function() {
39 | return common.execRemote(conn,args);
40 | }
41 | }
42 |
43 |
44 | conn.on('ready', function() {
45 | common.chain([
46 | execRemote(get_model),
47 | execRemote(get_version),
48 | execRemote(uname)
49 | ]).done(function(){
50 | conn.end();
51 | });
52 | }).connect(args);
53 |
54 | conn.on('end',function(){
55 | process.exit();
56 | });
57 |
--------------------------------------------------------------------------------
/blink.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by josh on 7/2/15.
3 | */
4 | var common = require('./common');
5 | var Client = require('ssh2').Client;
6 | var args = common.splitArgs();
7 |
8 | if(!common.checkArgs(args)) return printHelp();
9 | if(!args.port) args.port = 22;
10 | if(!args.username) {
11 | console.log("using default username: 'pi'");
12 | args.username = 'pi';
13 | }
14 | if(!args.password) {
15 | console.log("using default password: 'raspberry'");
16 | args.password = 'raspberry';
17 | }
18 |
19 | function printHelp() {
20 | console.log("usage");
21 | console.log("razzmaster blink --host 192.168.1.3");
22 | console.log(" --username myusername");
23 | console.log(" --password mypass");
24 | }
25 |
26 |
27 |
28 | var conn = new Client();
29 |
30 | process.on( 'SIGINT', function() {
31 | console.log( "stopping the green blinking" );
32 | common.execRemote(conn, 'echo "echo none > /sys/devices/platform/soc/soc\:leds/leds/led0/trigger" | sudo sh').then(function(){
33 | conn.end();
34 | });
35 | });
36 |
37 | conn.on('ready', function() {
38 | common.execRemote(conn, 'echo "echo heartbeat > /sys/devices/platform/soc/soc\:leds/leds/led0/trigger" | sudo sh')
39 | .then(function() {
40 | console.log("Your raspberry pi should be blinking green.\n"
41 | +"press control C to stop blinking and quit");
42 | });
43 | }).connect(args);
44 |
45 | conn.on('end',function(){
46 | process.exit();
47 | });
48 |
--------------------------------------------------------------------------------
/common.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by josh on 7/2/15.
3 | */
4 |
5 | var Q = require('q');
6 |
7 | exports.splitArgs = function() {
8 | var argv = process.argv.slice(2);
9 | //console.log('argv = ', argv);
10 | var obj = {};
11 | for(var i=0; i= 2) {
41 | var chunks = lines[1].split(' ').filter(String);
42 | return cb(null, chunks[2]);
43 | }
44 |
45 | cb(null, false);
46 |
47 | });
48 |
49 | };
50 |
51 | function windows(ip, cb) {
52 |
53 | var arp = spawn('arp', ['-a', ip]),
54 | buffer = '';
55 |
56 | if(! arp)
57 | return cb('arp failed');
58 |
59 | arp.stdout.on('data', function(data) {
60 | buffer += data;
61 | });
62 |
63 | arp.on('close', function(code) {
64 |
65 | var lines = buffer.split('\r\n');
66 |
67 | if (code !== 0) {
68 | return cb('arp failed');
69 | }
70 |
71 | for(var i = 3; i < lines.length; i++) {
72 |
73 | var chunks = lines[i].split(' ').filter(String);
74 |
75 | if (chunks[0] === ip) {
76 | return cb(null, chunks[1].replace(/-/g, ':'));
77 | }
78 |
79 | }
80 |
81 | cb(null, false);
82 |
83 | });
84 |
85 | };
86 |
87 | function mac(ip, cb) {
88 |
89 | var arp = spawn('arp', ['-n', ip]),
90 | buffer = '';
91 |
92 | if(! arp)
93 | return cb('arp failed');
94 |
95 | arp.stdout.on('data', function(data) {
96 | buffer += data;
97 | });
98 |
99 | arp.on('close', function(code) {
100 |
101 | var chunks = buffer.split(' ').filter(String);
102 |
103 | if (code !== 0) {
104 | return cb('arp failed');
105 | }
106 |
107 | if (chunks[3] !== 'no') {
108 | return cb(null, chunks[3]);
109 | }
110 |
111 | cb(null, false);
112 |
113 | });
114 |
115 | };
116 |
117 | exports = module.exports = arp;
118 |
--------------------------------------------------------------------------------
/install.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 | var Q = require('q');
3 | var Client = require('ssh2').Client;
4 | var common = require('./common');
5 | var args = common.splitArgs();
6 |
7 | if(!common.checkArgs(args)) return printHelp();
8 |
9 | function printHelp() {
10 | console.log("usage");
11 | console.log("razzmaster install --config configfile.json --host 192.168.1.3");
12 | console.log(" --username pi");
13 | console.log(" --password mypass");
14 | }
15 |
16 | if(!args.config) return console.log("missing --config option");
17 | if(!args.port) args.port = 22;
18 | if(!args.username) {
19 | console.log("using default username: 'pi'");
20 | args.username = 'pi';
21 | }
22 | if(!args.password) {
23 | console.log("using default password: 'raspberry'");
24 | args.password = 'raspberry';
25 | }
26 |
27 |
28 | var config = JSON.parse(fs.readFileSync(args.config));
29 |
30 |
31 | console.log("args = ", args);
32 | console.log("config",config);
33 |
34 | var conn = new Client();
35 | var funcs = [];
36 |
37 |
38 | function execRemote(conn, args) {
39 | return function() {
40 | return common.execRemote(conn,args);
41 | }
42 | }
43 |
44 | function processInstalls(config) {
45 | //update apt
46 | funcs.push(execRemote(conn, 'sudo apt-get -y update'));
47 | funcs.push(execRemote(conn, 'sudo apt-get -y upgrade'));
48 |
49 | // install packages
50 | if(config.packages) {
51 | config.packages.forEach(function (pak) {
52 | if (pak == 'nodejs') {
53 | //install node
54 | funcs.push(execRemote(conn, 'curl -sL https://deb.nodesource.com/setup_6.x | sudo bash -'));
55 | funcs.push(execRemote(conn, 'sudo apt-get install -y nodejs'));
56 | funcs.push(execRemote(conn, 'node --version'));
57 | funcs.push(execRemote(conn, 'npm --version'));
58 | } else {
59 | funcs.push(execRemote(conn, 'sudo apt-get install -y ' + pak));
60 | }
61 | });
62 | }
63 |
64 | // install global npms
65 | if(config.git) {
66 | config.git.forEach(function(repo){
67 | funcs.push(execRemote(conn,"cd $HOME"));
68 | funcs.push(execRemote(conn,'git clone '+repo));
69 | })
70 | }
71 |
72 | if(config.npm) {
73 | config.npm.forEach(function(mod){
74 | funcs.push(execRemote(conn, "npm install -g "+mod));
75 | });
76 | }
77 | }
78 |
79 | function processWifi(config) {
80 | if(!config.wifi) return;
81 | if(!config.wifi.ssid) return console.log("ssid missing!");
82 | if(!config.wifi.password) return console.log("password missing!");
83 | var ssid = config.wifi.ssid;
84 | var pass = config.wifi.password;
85 | var line1 = 'ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev';
86 | var line2 = 'update_config=1';
87 | var line3 = 'network={\n ssid="'+ssid+'"\n psk="'+pass+'" \n}';
88 | var str = [line1,line2,line3].join("\n");
89 | str = str.replace(/\"/g,"\\\"");
90 | //console.log('string is ',str);
91 |
92 | var filename = '/etc/wpa_supplicant/wpa_supplicant.conf';
93 | funcs.push(execRemote(conn,'sudo echo "'+str+'" > tmp.txt'));
94 | funcs.push(execRemote(conn,'sudo cp tmp.txt '+filename));
95 | funcs.push(execRemote(conn,'sudo ifdown wlan0'));
96 | funcs.push(execRemote(conn,'sudo ifup wlan0'));
97 |
98 | }
99 | processWifi(config);
100 | processInstalls(config);
101 | conn.on('ready', function() {
102 | common.chain(funcs).done(function(){
103 | conn.end();
104 | });
105 | }).connect(args);
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/find.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by josh on 6/22/15.
3 | */
4 | console.log("finding a raspberry pi");
5 |
6 | var ip = require('ip'),
7 | os = require('os'),
8 | ping= require('./ping'),
9 | arp = require('./arp'),
10 | Q = require('q');
11 | ;
12 | /*
13 | var addr = ip.address();
14 | // grab the first three octects of the IP
15 | var subnet = addr.substr(0, addr.lastIndexOf('.')) || false;
16 | console.log("my ip = ", ip);
17 | console.log("my subnet = ", subnet);
18 | */
19 |
20 | function setupPolyFill() {
21 | if (![].includes) {
22 | Array.prototype.includes = function(searchElement /*, fromIndex*/ ) {
23 | 'use strict';
24 | var O = Object(this);
25 | var len = parseInt(O.length) || 0;
26 | if (len === 0) {
27 | return false;
28 | }
29 | var n = parseInt(arguments[1]) || 0;
30 | var k;
31 | if (n >= 0) {
32 | k = n;
33 | } else {
34 | k = len + n;
35 | if (k < 0) {k = 0;}
36 | }
37 | var currentElement;
38 | while (k < len) {
39 | currentElement = O[k];
40 | if (searchElement === currentElement ||
41 | (searchElement !== searchElement && currentElement !== currentElement)) {
42 | return true;
43 | }
44 | k++;
45 | }
46 | return false;
47 | };
48 | }
49 |
50 | }
51 |
52 | setupPolyFill();
53 |
54 | var whitelist = [
55 | 'b8:27:eb', // rasberry pi ethernet
56 | '00:e0:4c', // realtek
57 | '00:14:78', // tp-link
58 | '00:0c:43', // ralink
59 | '00:0f:60', // canakit/ralink
60 | 'e0:91:53', //cirago wifi/bt combo http://cirago.com/bta7300.php
61 | ];
62 |
63 | var interfaces = os.networkInterfaces();
64 | var subnets = [];
65 |
66 | for(var name in interfaces) {
67 | var ifc = interfaces[name];
68 | for(var i=0; i