├── 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 | 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