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