versions update
[random_node_code.git] / lxc / init.js
1 #!/node/bin/node
2
3 // this next line is used by the parse_dist.php bit to create its version info
4 // VERSION_FOR_PARSER: 0.0.1
5
6 //var uid=$user
7 var fs = require("fs");
8 var asroot = false;
9 var debug_output = false;
10 var default_loop_time = 2000;
11
12 // set the global context
13 global.lxcnodejs = new Object();
14
15 // and begin...
16 if(process.getuid() == 0) {
17         asroot = true;
18 } else {
19         console.log("Not running as root, assuming debug mode, config file should be ./config");
20 }
21
22 if(asroot) var mnt = require("/node_modules/mount");
23
24 // perform mount
25 console.log("Mounting proc");
26 if(asroot) {
27         mnt.mount("proc", "/proc", "proc");
28 } else {
29         //console.log("fake mount");
30         console.log("(debug) pass /proc mount");
31 }
32
33 // get the global space setup
34
35
36 // read main and node config file and parse
37 if(asroot) {
38         console.log("Reading config file /config");
39         loadConfig("/config");
40         loadNodeConfig("/node_base/appconfig", "/node_base");
41 } else {
42         console.log("Reading config file ./config (debug mode)");
43         loadConfig("./config");
44         loadNodeConfig("./appconfig", ".");
45 }
46
47 // begin the main loop
48 if(asroot) {
49         startMainLoop();
50 } else {
51         if(debug_output) console.log("(debug) main loop now begins with process monitor");
52         startMainLoop();
53 }
54
55 /*
56  * 
57  * END OF MAIN ROUTINE
58  * 
59  */
60
61 function loadNodeConfig(file, basedir) {
62         var configFile = fs.readFileSync(file).toString();
63         var lines = configFile.split("\n");
64         var nodes = new Object();
65         
66         for(var i=0; i < lines.length; i++) {
67                 //console.log("line: ", lines[i].trim());
68                 var sks = lines[i].match(/^[^#][a-zA-Z0-9:]+/);
69                 var lks = null;
70                 if(sks != null) lks = lines[i].split(":");
71                 if(lks!=null) {
72                         stl = lines[i].split(":");
73                         //console.log("lks is: '%s', '%s'", stl[0], stl[1]);
74                         if(stl.length < 3) {
75                                 console.log("Invalid config line '%s', should be 'appname:directory:appfilepath[:options]'", lines[i].trim());
76                         } else {
77                                 var args = null;
78                                 if(stl[3] != null) args = stl[3];
79                                 nodes[stl[0]] = new Object();
80                                 nodes[stl[0]].directory = stl[1];
81                                 nodes[stl[0]].file = stl[2];
82                                 nodes[stl[0]].args = args;
83                                 console.log("Adding node config for '%s'", stl[0]);
84                         }
85                 }
86         }
87         
88         // check our config against the current global
89         for(key in global.lxcnodejs) {
90                 if(typeof nodes[key] == "undefined") {
91                         global.lxcnodejs[key].stop_and_kill = true;
92                 } else {
93                         if(global.lxcnodejs[key].directory != nodes[key].directory) {
94                                 if(debug_output) console.log("(debug) marking '%s' for restart on config change (directory)", key);
95                                 global.lxcnodejs[key].directory = nodes[key].directory;
96                                 global.lxcnodejs[key].restart = true;
97                         }
98                         if(global.lxcnodejs[key].file != nodes[key].file) {
99                                 if(debug_output) console.log("(debug) marking '%s' for restart on config change (file)", key);
100                                 global.lxcnodejs[key].file = nodes[key].file;
101                                 global.lxcnodejs[key].restart = true;
102                         }
103                         if(global.lxcnodejs[key].args != nodes[key].args) {
104                                 if(debug_output) console.log("(debug) marking '%s' for restart on config change (options)", key);
105                                 global.lxcnodejs[key].args = nodes[key].args;
106                                 global.lxcnodejs[key].restart = true;
107                         }
108                 }
109         }
110         
111         // check for new configs and load them
112         for(key in nodes) {
113                 if(typeof global.lxcnodejs[key] == "undefined") {
114                         if(debug_output) console.log("(debug) application '%s' is a new config, creating", key);
115                         global.lxcnodejs[key] = new Object();
116                         global.lxcnodejs[key].directory = nodes[key].directory;
117                         global.lxcnodejs[key].file = nodes[key].file;
118                         global.lxcnodejs[key].args = nodes[key].args;
119                         global.lxcnodejs[key].pid = -1;
120                 }
121         }
122 }
123
124 function loadConfig(file) {
125         var configFile = fs.readFileSync(file).toString();
126         
127         var lines = configFile.split("\n");
128         
129         for(var i=0; i < lines.length; i++) {
130                 //console.log("line: ", lines[i].trim());
131                 var lks = lines[i].match(/^[^#][a-zA-Z0-9]+\=.*/);
132                 if(lks!=null) {
133                         stl = lines[i].split("=");
134                         //console.log("lks is: '%s', '%s'", stl[0], stl[1]);
135                         if(stl.length !=2) {
136                                 console.log("Invalid config line '%s', should be name=value format", lines[i].trim());
137                         } else {
138                                 parseConfigLine(stl[0].trim(), stl[1].trim());
139                         }
140                 }
141         }
142 }
143
144 function parseConfigLine(key, value) {
145         //console.log("parsing config '%s' = '%s'", key, value);
146         switch(key) {
147                 case "ip4address":
148                         var lxon = value.split(":");
149                         var iface = "";
150                         var addrs = "";
151                         if(lxon.length!=2) {
152                                 console.log("Address config isnt correct, expect 'interface:address/mask'");
153                         } else {
154                                 iface = lxon[0];
155                                 addrs = lxon[1];                                
156                                 console.log("IPv4 address for '%s' is '%s'", iface, addrs);
157                                 if(asroot) {
158                                                 var nodespawn = require("child_process").spawn,
159                                                         nodeproc = nodespawn("/sbin/ip", ["link", "set", iface, "address", addrs, "up"], opts);                                 
160                                 } else {
161                                         if(debug_output) console.log("(debug) would run /sbin/ip link set %s address %s up", iface, addrs);
162                                 }
163                         }
164                         break;
165                 case "ip6address":
166                         var lxon = value.split(":");
167                         var iface = "";
168                         var addrs = "";
169                         if(lxon.length!=2) {
170                                 console.log("Address config isnt correct, expect 'interface:address/mask'");
171                         } else {
172                                 iface = lxon[0];
173                                 addrs = lxon[1];                                
174                                 console.log("IPv4 address for '%s' is '%s'", iface, addrs);
175                         }
176                         break;
177                 case "ip4gateway":
178                         console.log("Default IPv4 gateway is: '%s'", value);
179                         if(asroot) {
180                                 var nodespawn = require("child_process").spawn,
181                                 nodeproc = nodespawn("/sbin/ip", ["route", "add", "default", "via", value], opts);                                      
182                         } else {
183                                 if(debug_output) console.log("(debug) would run /sbin/ip route add default via %s", value);
184                         }
185                         break;
186                 case "checktimer":
187                         console.log("Setting health check timer to %s ms", value);
188                         default_loop_time = parseInt(value);
189                         if(default_loop_time < 100) {
190                                 console.log("(warning) health check timer must be larger then 100, setting to 100, was %s", value);
191                                 default_loop_time = 100;
192                         }
193                         break;
194                 case "ip6gateway":
195                         console.log("Default IPv6 gateway is: '%s'", value);
196                         break;
197                 case "dnsserver":
198                         console.log("DNS address set to: '%s'", value);
199                         break;
200                 case "hostname":
201                         console.log("Hostname set to: '%s'", value);
202                         break;
203                 case "debug":
204                         if(value == "true") {
205                                 debug_output = true;
206                                 console.log("(debug) Turning debugging on");
207                         }
208                 default:
209                         console.log("Unknown config line: '%s' = '%s'", key, value);
210         }
211 }
212
213 function startApp(appname, directory, file, uid) {
214         
215     var opts = { uid: uid, env: process.env, cwd: directory };
216     var sleeplen = Math.floor(Math.random()*60);
217     var nodespawn = require("child_process").spawn;
218     var nodeproc = null;
219     
220     if(asroot) {
221         nodeproc = nodespawn("/node/bin/node", ["/node_code/nc.js"], opts);
222     } else {
223         if(debug_output) console.log("(debug) using sleep to emulator process '%s'", appname);
224         nodeproc = nodespawn("sleep", [sleeplen], opts);
225     }
226     
227     nodeproc.procname = appname;
228     global.lxcnodejs[appname].pid = nodeproc.pid;
229     
230     if(debug_output) console.log("(debug) application started with pid of %d", nodeproc.pid);
231     
232     nodeproc.stdout.on("data", function(data) {
233             console.log("data from nodespawn: '%s', '%s'", data, nodeproc.procname);
234     });
235     nodeproc.stderr.on("data", function(data) {
236             console.log("stderrdata from nodespawn: '%s', '%s'", data, nodeproc.procname);
237     });
238     nodeproc.on("exit", function(data) {
239             console.log("nodespawn died for '%s'", nodeproc.procname);
240             global.lxcnodejs[nodeproc.procname].pid = 0;
241     });
242 }
243
244 // main loop does all starting/restarting of applications
245 function startMainLoop() {
246         // this loop kicks around every 5 seconds and just checks on the health of the processes or whatever
247         for(key in global.lxcnodejs) {
248                 if(debug_output) console.log("(debug) checking state of '%s'", key);
249                 if(typeof global.lxcnodejs[key].pid == "undefined") {
250                         // start it.
251                         console.log("Starting application '%s' (no pid was defined)", key);
252                         startApp(key, global.lxcnodejs[key].directory, global.lxcnodejs[key].file, null);
253                 } else if(global.lxcnodejs[key].pid == -1) {
254                         // start it.
255                         console.log("Starting application '%s' (initial start)", key);
256                         startApp(key, global.lxcnodejs[key].directory, global.lxcnodejs[key].file, null);
257                 } else if(global.lxcnodejs[key].pid == 0) {
258                         // start it.
259                         console.log("Starting application '%s' (pid 0, possible application crash?)", key);
260                         startApp(key, global.lxcnodejs[key].directory, global.lxcnodejs[key].file, null);
261                 } else if(typeof global.lxcnodejs[key].restart != "undefined") {
262                         // restart it
263                         console.log("Restarting application '%s' (restart defined)", key);
264                         startApp(key, global.lxcnodejs[key].directory, global.lxcnodejs[key].file, null);
265                 } else if(typeof global.lxcnodejs[key].stop_and_kill != "undefined") {
266                         // kill it
267                         console.log("Restarting application '%s'", key);
268                 } else {
269                         if(debug_output) console.log("(debug) state of '%s' is healthy", key);
270                 }
271                 
272                 // we also need to check the control directory and see
273                 // if theres any control files
274         }
275
276         // and restart the loop
277         setTimeout(startMainLoop, default_loop_time);
278 }