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