Commit | Line | Data |
---|---|---|
a8bf3ea4 | 1 | var fs = require('fs'), |
f9ff7686 | 2 | _ = require('lodash'), |
2aeb721f | 3 | util = require('util'), |
1360a454 | 4 | WebListener = require('./weblistener.js'), |
1286229a | 5 | config = require('./configuration.js'), |
adefb6bd | 6 | modules = require('./modules.js'), |
4eba7612 | 7 | Identd = require('./identd.js'), |
9556a028 | 8 | Proxy = require('./proxy.js'), |
4eba7612 | 9 | ControlInterface = require('./controlinterface.js'); |
fd779420 | 10 | |
186531ed D |
11 | |
12 | ||
11dbb00f D |
13 | process.chdir(__dirname + '/../'); |
14 | config.loadConfig(); | |
15 | ||
16 | ||
17 | // If we're not running in the forground and we have a log file.. switch | |
18 | // console.log to output to a file | |
38ef7086 | 19 | if (process.argv.indexOf('-f') === -1 && global.config && global.config.log) { |
11dbb00f | 20 | (function () { |
b737610b | 21 | var log_file_name = global.config.log; |
11dbb00f D |
22 | |
23 | if (log_file_name[0] !== '/') { | |
24 | log_file_name = __dirname + '/../' + log_file_name; | |
25 | } | |
26 | ||
27 | ||
28 | ||
29 | console.log = function() { | |
30 | var logfile = fs.openSync(log_file_name, 'a'), | |
31 | out; | |
32 | ||
2aeb721f | 33 | out = util.format.apply(util, arguments); |
11dbb00f D |
34 | |
35 | // Make sure we out somthing to log and we have an open file | |
36 | if (!out || !logfile) return; | |
37 | ||
38 | out += '\n'; | |
39 | fs.writeSync(logfile, out, null); | |
40 | ||
41 | fs.closeSync(logfile); | |
42 | }; | |
43 | })(); | |
44 | } | |
4a30a583 | 45 | |
ab15f618 | 46 | |
ab15f618 | 47 | |
186531ed | 48 | // Make sure we have a valid config file and at least 1 server |
1f49a029 D |
49 | if (!global.config || Object.keys(global.config).length === 0) { |
50 | console.log('Couldn\'t find a valid config.js file (Did you copy the config.example.js file yet?)'); | |
a8bf3ea4 | 51 | process.exit(1); |
fd779420 D |
52 | } |
53 | ||
b737610b | 54 | if ((!global.config.servers) || (global.config.servers.length < 1)) { |
a8bf3ea4 JA |
55 | console.log('No servers defined in config file'); |
56 | process.exit(2); | |
fd779420 D |
57 | } |
58 | ||
59 | ||
186531ed D |
60 | |
61 | ||
1920a38e | 62 | // Create a plugin interface |
ae64bf07 | 63 | global.modules = new modules.Publisher(); |
1920a38e | 64 | |
874f28a5 | 65 | // Register as the active interface |
ae64bf07 | 66 | modules.registerPublisher(global.modules); |
1920a38e | 67 | |
991091b7 | 68 | // Load any modules in the config |
d8002ae0 D |
69 | if (global.config.module_dir) { |
70 | (global.config.modules || []).forEach(function (module_name) { | |
5a18896d | 71 | if (modules.load(module_name)) { |
d8002ae0 D |
72 | console.log('Module ' + module_name + ' loaded successfuly'); |
73 | } else { | |
74 | console.log('Module ' + module_name + ' failed to load'); | |
75 | } | |
76 | }); | |
77 | } | |
1920a38e D |
78 | |
79 | ||
80 | ||
186531ed | 81 | |
186531ed | 82 | // Holder for all the connected clients |
26322e8f D |
83 | global.clients = { |
84 | clients: Object.create(null), | |
c36ed4eb | 85 | addresses: Object.create(null), |
26322e8f | 86 | |
cc3c5d04 D |
87 | // Local and foriegn port pairs for identd lookups |
88 | // {'65483_6667': client_obj, '54356_6697': client_obj} | |
89 | port_pairs: {}, | |
90 | ||
c36ed4eb JA |
91 | add: function (client) { |
92 | this.clients[client.hash] = client; | |
93 | if (typeof this.addresses[client.real_address] === 'undefined') { | |
94 | this.addresses[client.real_address] = Object.create(null); | |
95 | } | |
96 | this.addresses[client.real_address][client.hash] = client; | |
97 | }, | |
26322e8f | 98 | |
c36ed4eb JA |
99 | remove: function (client) { |
100 | if (typeof this.clients[client.hash] !== 'undefined') { | |
101 | delete this.clients[client.hash]; | |
102 | delete this.addresses[client.real_address][client.hash]; | |
103 | if (Object.keys(this.addresses[client.real_address]).length < 1) { | |
104 | delete this.addresses[client.real_address]; | |
105 | } | |
106 | } | |
107 | }, | |
26322e8f | 108 | |
c36ed4eb JA |
109 | numOnAddress: function (addr) { |
110 | if (typeof this.addresses[addr] !== 'undefined') { | |
111 | return Object.keys(this.addresses[addr]).length; | |
112 | } else { | |
113 | return 0; | |
114 | } | |
09c26937 D |
115 | }, |
116 | ||
117 | broadcastKiwiCommand: function (command, data, callback) { | |
118 | var clients = []; | |
119 | ||
120 | // Get an array of clients for us to work with | |
121 | for (var client in global.clients.clients) { | |
122 | clients.push(global.clients.clients[client]); | |
123 | } | |
124 | ||
125 | ||
126 | // Sending of the command in batches | |
127 | var sendCommandBatch = function (list) { | |
128 | var batch_size = 100, | |
129 | cutoff; | |
130 | ||
131 | if (list.length >= batch_size) { | |
132 | // If we have more clients than our batch size, call ourself with the next batch | |
133 | setTimeout(function () { | |
134 | sendCommandBatch(list.slice(batch_size)); | |
135 | }, 200); | |
136 | ||
137 | cutoff = batch_size; | |
138 | ||
139 | } else { | |
140 | cutoff = list.length; | |
141 | } | |
142 | ||
143 | list.slice(0, cutoff).forEach(function (client) { | |
144 | if (!client.disposed) { | |
145 | client.sendKiwiCommand(command, data); | |
146 | } | |
147 | }); | |
148 | ||
149 | if (cutoff === list.length && typeof callback === 'function') { | |
150 | callback(); | |
151 | } | |
152 | }; | |
153 | ||
154 | sendCommandBatch(clients); | |
c36ed4eb JA |
155 | } |
156 | }; | |
186531ed | 157 | |
8838bdd6 JA |
158 | global.servers = { |
159 | servers: Object.create(null), | |
09c26937 | 160 | |
8838bdd6 JA |
161 | addConnection: function (connection) { |
162 | var host = connection.irc_host.hostname; | |
163 | if (!this.servers[host]) { | |
164 | this.servers[host] = []; | |
165 | } | |
166 | this.servers[host].push(connection); | |
167 | }, | |
09c26937 | 168 | |
8838bdd6 JA |
169 | removeConnection: function (connection) { |
170 | var host = connection.irc_host.hostname | |
171 | if (this.servers[host]) { | |
172 | this.servers[host] = _.without(this.servers[host], connection); | |
173 | if (this.servers[host].length === 0) { | |
174 | delete this.servers[host]; | |
175 | } | |
176 | } | |
177 | }, | |
09c26937 | 178 | |
8838bdd6 JA |
179 | numOnHost: function (host) { |
180 | if (this.servers[host]) { | |
181 | return this.servers[host].length; | |
182 | } else { | |
183 | return 0; | |
184 | } | |
185 | } | |
186 | }; | |
187 | ||
26322e8f D |
188 | |
189 | ||
09c26937 D |
190 | /** |
191 | * When a new config is loaded, send out an alert to the clients so | |
192 | * so they can reload it | |
193 | */ | |
645fe41b | 194 | config.on('loaded', function () { |
09c26937 | 195 | global.clients.broadcastKiwiCommand('reconfig'); |
645fe41b JA |
196 | }); |
197 | ||
198 | ||
26322e8f | 199 | |
adefb6bd D |
200 | /* |
201 | * Identd server | |
202 | */ | |
203 | if (global.config.identd && global.config.identd.enabled) { | |
cc3c5d04 D |
204 | var identd_resolve_user = function(port_here, port_there) { |
205 | var key = port_here.toString() + '_' + port_there.toString(); | |
206 | ||
207 | if (typeof global.clients.port_pairs[key] == 'undefined') { | |
208 | return; | |
209 | } | |
210 | ||
211 | return global.clients.port_pairs[key].username; | |
212 | }; | |
213 | ||
214 | var identd_server = new Identd({ | |
adefb6bd | 215 | bind_addr: global.config.identd.address, |
cc3c5d04 D |
216 | bind_port: global.config.identd.port, |
217 | user_id: identd_resolve_user | |
218 | }); | |
219 | ||
220 | identd_server.start(); | |
adefb6bd D |
221 | } |
222 | ||
223 | ||
224 | ||
225 | ||
26322e8f D |
226 | /* |
227 | * Web listeners | |
228 | */ | |
229 | ||
230 | ||
186531ed | 231 | // Start up a weblistener for each found in the config |
b737610b | 232 | _.each(global.config.servers, function (server) { |
9556a028 D |
233 | if (server.type == 'proxy') { |
234 | // Start up a kiwi proxy server | |
235 | var serv = new Proxy.ProxyServer(); | |
585d3189 | 236 | serv.listen(server.port, server.address, server); |
9556a028 D |
237 | |
238 | serv.on('listening', function() { | |
239 | console.log('Kiwi proxy listening on %s:%s %s SSL', server.address, server.port, (server.ssl ? 'with' : 'without')); | |
240 | }); | |
241 | ||
96ecb5e7 | 242 | serv.on('socket_connected', function(pipe) { |
7cc8c4ad D |
243 | // SSL connections have the raw socket as a property |
244 | var socket = pipe.irc_socket.socket ? | |
245 | pipe.irc_socket.socket : | |
246 | pipe.irc_socket; | |
247 | ||
248 | pipe.identd_pair = socket.localPort.toString() + '_' + socket.remotePort.toString(); | |
9556a028 D |
249 | global.clients.port_pairs[pipe.identd_pair] = pipe.meta; |
250 | }); | |
251 | ||
252 | serv.on('connection_close', function(pipe) { | |
9556a028 D |
253 | delete global.clients.port_pairs[pipe.identd_pair]; |
254 | }); | |
255 | ||
256 | } else { | |
257 | // Start up a kiwi web server | |
258 | var wl = new WebListener(server, global.config.transports); | |
259 | ||
260 | wl.on('connection', function (client) { | |
261 | clients.add(client); | |
262 | }); | |
263 | ||
264 | wl.on('client_dispose', function (client) { | |
265 | clients.remove(client); | |
266 | }); | |
267 | ||
268 | wl.on('listening', function () { | |
269 | console.log('Listening on %s:%s %s SSL', server.address, server.port, (server.ssl ? 'with' : 'without')); | |
270 | webListenerRunning(); | |
271 | }); | |
272 | ||
273 | wl.on('error', function (err) { | |
274 | console.log('Error listening on %s:%s: %s', server.address, server.port, err.code); | |
275 | // TODO: This should probably be refactored. ^JA | |
276 | webListenerRunning(); | |
277 | }); | |
278 | } | |
a8bf3ea4 | 279 | }); |
68ad40c6 | 280 | |
e380bc9e D |
281 | // Once all the listeners are listening, set the processes UID/GID |
282 | var num_listening = 0; | |
283 | function webListenerRunning() { | |
284 | num_listening++; | |
285 | if (num_listening === global.config.servers.length) { | |
286 | setProcessUid(); | |
287 | } | |
288 | } | |
b0ad9f0a | 289 | |
f52d8543 | 290 | |
186531ed D |
291 | |
292 | ||
293 | /* | |
294 | * Process settings | |
295 | */ | |
296 | ||
297 | // Set process title | |
298 | process.title = 'kiwiirc'; | |
299 | ||
300 | // Change UID/GID | |
e380bc9e D |
301 | function setProcessUid() { |
302 | if ((global.config.group) && (global.config.group !== '')) { | |
303 | process.setgid(global.config.group); | |
304 | } | |
305 | if ((global.config.user) && (global.config.user !== '')) { | |
306 | process.setuid(global.config.user); | |
307 | } | |
fd779420 | 308 | } |
709031df | 309 | |
186531ed | 310 | |
0bb21ab3 D |
311 | // Make sure Kiwi doesn't simply quit on an exception |
312 | process.on('uncaughtException', function (e) { | |
313 | console.log('[Uncaught exception] ' + e); | |
caff44fe | 314 | console.log(e.stack); |
0bb21ab3 D |
315 | }); |
316 | ||
317 | ||
88f14637 D |
318 | process.on('SIGUSR1', function() { |
319 | if (config.loadConfig()) { | |
320 | console.log('New config file loaded'); | |
321 | } else { | |
322 | console.log("No new config file was loaded"); | |
323 | } | |
324 | }); | |
325 | ||
326 | ||
5658034d D |
327 | process.on('SIGUSR2', function() { |
328 | console.log('Connected clients: ' + _.size(global.clients.clients).toString()); | |
329 | console.log('Num. remote hosts: ' + _.size(global.clients.addresses).toString()); | |
330 | }); | |
88f14637 | 331 | |
186531ed D |
332 | |
333 | /* | |
334 | * Listen for runtime commands | |
335 | */ | |
87a6abbe | 336 | process.stdin.resume(); |
4eba7612 | 337 | new ControlInterface(process.stdin, process.stdout, {prompt: ''}); |