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