1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 | "use strict";
|
23 |
|
24 | const fs = require('fs');
|
25 | const net = require('net');
|
26 | const isEmpty = require('lodash.isempty');
|
27 | const includes = require('lodash.includes');
|
28 | const tail = require('./lib/tail');
|
29 | const residue_crypt = require('./lib/residue_crypt');
|
30 | const proc = require('./lib/option_parser');
|
31 |
|
32 | proc.parse(process.argv);
|
33 |
|
34 | if (proc.config === false) {
|
35 | console.error('ERR: No config file provided\nUsage: resitail --config <config>\n');
|
36 | process.exit();
|
37 | }
|
38 |
|
39 | const config = JSON.parse(fs.readFileSync(proc.config));
|
40 |
|
41 | if (!config.residue_config) {
|
42 | console.error('Invalid configuration. Missing: residue_config');
|
43 | process.exit();
|
44 | }
|
45 |
|
46 | const hooks = [];
|
47 |
|
48 | config.hooks.forEach((h) => {
|
49 | const hook = require(h.path);
|
50 | try {
|
51 | if (h.enabled) {
|
52 | const hookObj = hook(h.config);
|
53 | hooks.push(hookObj);
|
54 | hookObj.name = h.name;
|
55 | console.log(`Hooked [${h.name}]`);
|
56 | }
|
57 | } catch (e) {
|
58 | console.error(`Error while loading hook ${h.name}: ${e}`);
|
59 | process.exit();
|
60 | }
|
61 | });
|
62 |
|
63 | if (isEmpty(hooks)) {
|
64 | console.error('No hooks enabled');
|
65 | process.exit();
|
66 | }
|
67 |
|
68 | const residue_config = JSON.parse(fs.readFileSync(config.residue_config));
|
69 | const crypt = residue_crypt(residue_config);
|
70 | const packet_delimiter = '\r\n\r\n';
|
71 |
|
72 | const sendData = (evt, type, line, controller) => {
|
73 | if (controller) {
|
74 | hooks.forEach((hook) => {
|
75 | if (hook.config.channels.to_logger) {
|
76 | if (!includes(hook.config.loggers_ignore_list, controller.logger_id)) {
|
77 | hook.send({
|
78 | line,
|
79 | 'channel': 'logger',
|
80 | 'channel_name': controller.logger_id,
|
81 | 'client_id': controller.client_id
|
82 | });
|
83 | }
|
84 | }
|
85 | if (hook.config.channels.to_client) {
|
86 | if (!includes(hook.config.clients_ignore_list, controller.client_id)) {
|
87 | hook.send({
|
88 | line,
|
89 | 'channel': 'client',
|
90 | 'channel_name': controller.client_id,
|
91 | 'logger_id': controller.logger_id
|
92 | });
|
93 | }
|
94 | }
|
95 | });
|
96 | }
|
97 | }
|
98 |
|
99 | if (isEmpty(residue_config.known_clients)) {
|
100 | console.error('ERR: No known clients specified in residue config');
|
101 | process.exit();
|
102 | }
|
103 |
|
104 | const admin_socket = new net.Socket();
|
105 | admin_socket.connect(residue_config.admin_port, '127.0.0.1');
|
106 |
|
107 | const active_processes = [];
|
108 |
|
109 | const startTail = (clientId) => {
|
110 | console.log(`Start client [${clientId}]`);
|
111 | const request = {
|
112 | _t: parseInt((new Date()).getTime() / 1000, 10),
|
113 | type: 5,
|
114 | client_id: clientId,
|
115 | };
|
116 |
|
117 | const encryptedRequest = crypt.encrypt(request);
|
118 | admin_socket.write(encryptedRequest, 'utf-8');
|
119 | }
|
120 |
|
121 | const processResponse = (response) => {
|
122 | if (response.trim().length === 0) {
|
123 | return;
|
124 | }
|
125 | let decrypted = '<failed>';
|
126 | try {
|
127 | decrypted = crypt.decrypt(response);
|
128 | const resp = JSON.parse(decrypted);
|
129 | for (let i = 0; i < resp.length; ++i) {
|
130 | const list = resp[i].files;
|
131 |
|
132 | const controller = {
|
133 | logger_id: resp[i].logger_id,
|
134 | client_id: resp[i].client_id,
|
135 | };
|
136 |
|
137 | const files = [];
|
138 |
|
139 | for (let j = 0; j < list.length; ++j) {
|
140 | if (fs.existsSync(list[j])) {
|
141 | files.push(list[j]);
|
142 | }
|
143 | }
|
144 |
|
145 | const tail_process = tail(files, {
|
146 | buffer: 0,
|
147 | });
|
148 |
|
149 | tail_process.on('line', txt => {
|
150 | sendData('resitail:line', 'log', txt, controller);
|
151 | });
|
152 |
|
153 | tail_process.on('info', txt => {
|
154 | sendData('resitail:line', 'info', txt, controller);
|
155 | });
|
156 |
|
157 | tail_process.on('error', error => {
|
158 | sendData('resitail:err', 'err', error, controller);
|
159 | });
|
160 |
|
161 | if (!active_processes[controller.client_id]) {
|
162 | active_processes[controller.client_id] = [];
|
163 | }
|
164 |
|
165 | active_processes[controller.client_id].push(tail_process);
|
166 | }
|
167 | } catch (err) {
|
168 | sendData('resitail:err', 'err', `error occurred, details: ${decrypted}`);
|
169 | console.log(err);
|
170 | }
|
171 | }
|
172 |
|
173 | admin_socket.on('data', (data, cb) => {
|
174 | const responses = data.toString().split(packet_delimiter);
|
175 | for (let i = 0; i < responses.length; ++i) {
|
176 | processResponse(responses[i] + packet_delimiter);
|
177 | }
|
178 | });
|
179 |
|
180 |
|
181 | for (let i = 0; i < residue_config.known_clients.length; ++i) {
|
182 | startTail(residue_config.known_clients[i].client_id);
|
183 | }
|