]> git.proxmox.com Git - pve-xtermjs.git/blame - xterm.js/src/main.js
split upstream and our native into different source packages
[pve-xtermjs.git] / xterm.js / src / main.js
CommitLineData
dcf3d43b
DC
1console.log('xtermjs: starting');
2
3var states = {
4 start: 1,
5 connecting: 2,
6 connected: 3,
7 disconnecting: 4,
8 disconnected: 5,
291e1a24 9 reconnecting: 6,
dcf3d43b
DC
10};
11
12var term,
13 protocol,
14 socketURL,
15 socket,
16 ticket,
dcf3d43b
DC
17 resize,
18 ping,
291e1a24
DC
19 state = states.start,
20 starttime = new Date();
21
22var type = getQueryParameter('console');
23var vmid = getQueryParameter('vmid');
24var vmname = getQueryParameter('vmname');
25var nodename = getQueryParameter('node');
6c7c792d 26var cmd = getQueryParameter('cmd');
1d3d2919 27var cmdOpts = getQueryParameter('cmd-opts');
dcf3d43b 28
93c96ac5 29function updateState(newState, msg, code) {
dcf3d43b
DC
30 var timeout, severity, message;
31 switch (newState) {
32 case states.connecting:
33 message = "Connecting...";
34 timeout = 0;
35 severity = severities.warning;
36 break;
37 case states.connected:
e435736a 38 window.onbeforeunload = windowUnload;
dcf3d43b
DC
39 message = "Connected";
40 break;
41 case states.disconnecting:
f7fd260c 42 window.onbeforeunload = undefined;
dcf3d43b
DC
43 message = "Disconnecting...";
44 timeout = 0;
45 severity = severities.warning;
46 break;
291e1a24 47 case states.reconnecting:
f7fd260c 48 window.onbeforeunload = undefined;
291e1a24
DC
49 message = "Reconnecting...";
50 timeout = 0;
51 severity = severities.warning;
52 break;
dcf3d43b 53 case states.disconnected:
e435736a 54 window.onbeforeunload = undefined;
dcf3d43b
DC
55 switch (state) {
56 case states.start:
57 case states.connecting:
291e1a24 58 case states.reconnecting:
dcf3d43b
DC
59 message = "Connection failed";
60 timeout = 0;
61 severity = severities.error;
62 break;
63 case states.connected:
64 case states.disconnecting:
291e1a24
DC
65 var time_since_started = new Date() - starttime;
66 timeout = 5000;
3645b3f4 67 if (time_since_started > 5*1000 || type === 'shell') {
291e1a24
DC
68 message = "Connection closed";
69 } else {
70 message = "Connection failed";
71 severity = severities.error;
72 }
dcf3d43b
DC
73 break;
74 case states.disconnected:
75 // no state change
76 break;
77 default:
78 throw "unknown state";
79 }
80 break;
81 default:
82 throw "unknown state";
83 }
93c96ac5 84 let msgArr = [];
dcf3d43b 85 if (msg) {
93c96ac5
DC
86 msgArr.push(msg);
87 }
88 if (code !== undefined) {
89 msgArr.push(`Code: ${code}`);
90 }
91 if (msgArr.length > 0) {
92 message += ` (${msgArr.join(', ')})`;
dcf3d43b
DC
93 }
94 state = newState;
95 showMsg(message, timeout, severity);
96}
97
98var terminalContainer = document.getElementById('terminal-container');
99document.getElementById('status_bar').addEventListener('click', hideMsg);
9871a3b8 100document.getElementById('connect_btn').addEventListener('click', startGuest);
b9bbd688 101const fitAddon = new FitAddon.FitAddon();
154b65d5 102const webglAddon = new WebglAddon.WebglAddon();
dcf3d43b
DC
103
104createTerminal();
105
9871a3b8
DC
106function startConnection(url, params, term) {
107 API2Request({
108 method: 'POST',
109 params: params,
110 url: url + '/termproxy',
111 success: function(result) {
112 var port = encodeURIComponent(result.data.port);
113 ticket = result.data.ticket;
114 socketURL = protocol + location.hostname + ((location.port) ? (':' + location.port) : '') + '/api2/json' + url + '/vncwebsocket?port=' + port + '&vncticket=' + encodeURIComponent(ticket);
115
9871a3b8
DC
116 socket = new WebSocket(socketURL, 'binary');
117 socket.binaryType = 'arraybuffer';
118 socket.onopen = runTerminal;
119 socket.onclose = tryReconnect;
120 socket.onerror = tryReconnect;
121 updateState(states.connecting);
122 },
123 failure: function(msg) {
124 updateState(states.disconnected,msg);
125 }
126 });
127}
128
129function startGuest() {
130 let api_type = type === 'kvm' ? 'qemu' : 'lxc';
131 API2Request({
132 method: 'POST',
133 url: `/nodes/${nodename}/${api_type}/${vmid}/status/start`,
134 success: function(result) {
135 showMsg('Guest started successfully', 0);
136 setTimeout(function() {
137 location.reload();
138 }, 1000);
139 },
140 failure: function(msg) {
141 if (msg.match(/already running/)) {
142 showMsg('Guest started successfully', 0);
143 setTimeout(function() {
144 location.reload();
145 }, 1000);
146 } else {
147 updateState(states.disconnected,msg);
148 }
149 }
150 });
151}
152
dcf3d43b 153function createTerminal() {
37445374 154 term = new Terminal(getTerminalSettings());
154b65d5 155 term.open(terminalContainer);
b9bbd688 156 term.loadAddon(fitAddon);
154b65d5
DC
157 try {
158 term.loadAddon(webglAddon);
159 } catch (_e) {
160 console.warn("webgl-addon loading failed, falling back to regular dom renderer");
161 }
dcf3d43b 162
b9bbd688 163 term.onResize(function (size) {
dcf3d43b
DC
164 if (state === states.connected) {
165 socket.send("1:" + size.cols + ":" + size.rows + ":");
166 }
167 });
168
169 protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://';
170
171 var params = {};
dcf3d43b
DC
172 var url = '/nodes/' + nodename;
173 switch (type) {
174 case 'kvm':
175 url += '/qemu/' + vmid;
dcf3d43b
DC
176 break;
177 case 'lxc':
178 url += '/lxc/' + vmid;
dcf3d43b
DC
179 break;
180 case 'upgrade':
af39a6f0 181 params.cmd = 'upgrade';
dcf3d43b 182 break;
6c7c792d
TM
183 case 'cmd':
184 params.cmd = decodeURI(cmd);
bc2ce72a 185 if (cmdOpts !== undefined && cmdOpts !== null && cmdOpts !== "") {
1d3d2919
TL
186 params['cmd-opts'] = decodeURI(cmdOpts);
187 }
6c7c792d 188 break;
dcf3d43b 189 }
9871a3b8
DC
190 if (type === 'kvm' || type === 'lxc') {
191 API2Request({
192 method: 'GET',
193 url: `${url}/status/current`,
194 success: function(result) {
195 if (result.data.status === 'running') {
196 startConnection(url, params, term);
197 } else {
198 document.getElementById('connect_dlg').classList.add('pve_open');
199 }
200 },
201 failure: function(msg) {
202 updateState(states.disconnected, msg);
203 },
204 });
205 } else {
206 startConnection(url, params, term);
207 }
dcf3d43b
DC
208}
209
210function runTerminal() {
211 socket.onmessage = function(event) {
920ae86d 212 var answer = new Uint8Array(event.data);
dcf3d43b
DC
213 if (state === states.connected) {
214 term.write(answer);
215 } else if(state === states.connecting) {
920ae86d 216 if (answer[0] === 79 && answer[1] === 75) { // "OK"
dcf3d43b
DC
217 updateState(states.connected);
218 term.write(answer.slice(2));
219 } else {
220 socket.close();
221 }
222 }
223 };
224
b9bbd688 225 term.onData(function(data) {
dcf3d43b
DC
226 if (state === states.connected) {
227 socket.send("0:" + unescape(encodeURIComponent(data)).length.toString() + ":" + data);
228 }
229 });
230
231 ping = setInterval(function() {
232 socket.send("2");
233 }, 30*1000);
234
235 window.addEventListener('resize', function() {
236 clearTimeout(resize);
237 resize = setTimeout(function() {
238 // done resizing
b9bbd688 239 fitAddon.fit();
dcf3d43b
DC
240 }, 250);
241 });
242
5e91985c 243 socket.send(PVE.UserName + ':' + ticket + "\n");
dcf3d43b 244
1553e6ef
DC
245 // initial focus and resize
246 setTimeout(function() {
247 term.focus();
b9bbd688 248 fitAddon.fit();
1553e6ef 249 }, 250);
dcf3d43b
DC
250}
251
291e1a24
DC
252function getLxcStatus(callback) {
253 API2Request({
254 method: 'GET',
255 url: '/nodes/' + nodename + '/lxc/' + vmid + '/status/current',
256 success: function(result) {
257 if (typeof callback === 'function') {
258 callback(true, result);
259 }
260 },
261 failure: function(msg) {
262 if (typeof callback === 'function') {
263 callback(false, msg);
264 }
265 }
266 });
267}
268
269function checkMigration() {
270 var apitype = type;
271 if (apitype === 'kvm') {
272 apitype = 'qemu';
273 }
274 API2Request({
275 method: 'GET',
276 params: {
277 type: 'vm'
278 },
279 url: '/cluster/resources',
280 success: function(result) {
281 // if not yet migrated , wait and try again
282 // if not migrating and stopped, cancel
283 // if started, connect
284 result.data.forEach(function(entity) {
285 if (entity.id === (apitype + '/' + vmid)) {
286 var started = entity.status === 'running';
287 var migrated = entity.node !== nodename;
288 if (migrated) {
289 if (started) {
290 // goto different node
291 location.href = '?console=' + type +
292 '&xtermjs=1&vmid=' + vmid + '&vmname=' +
293 vmname + '&node=' + entity.node;
294 } else {
295 // wait again
296 updateState(states.reconnecting, 'waiting for migration to finish...');
297 setTimeout(checkMigration, 5000);
298 }
299 } else {
300 if (type === 'lxc') {
301 // we have to check the status of the
302 // container to know if it has the
303 // migration lock
304 getLxcStatus(function(success, result) {
305 if (success) {
306 if (result.data.lock === 'migrate') {
307 // still waiting
308 updateState(states.reconnecting, 'waiting for migration to finish...');
309 setTimeout(checkMigration, 5000);
8f2ac472
DC
310 } else if (started) {
311 // container was rebooted
312 location.reload();
291e1a24
DC
313 } else {
314 stopTerminal();
315 }
316 } else {
317 // probably the status call failed because
318 // the ct is already somewhere else, so retry
319 setTimeout(checkMigration, 1000);
320 }
321 });
322 } else if (started) {
323 // this happens if we have old data in
324 // /cluster/resources, or the connection
325 // disconnected, so simply try to reload here
326 location.reload();
327 } else if (type === 'kvm') {
328 // it seems the guest simply stopped
329 stopTerminal();
330 }
331 }
332
333 return;
334 }
335 });
336 },
337 failure: function(msg) {
338 errorTerminal({msg: msg});
339 }
340 });
341}
342
93c96ac5 343function tryReconnect(event) {
291e1a24
DC
344 var time_since_started = new Date() - starttime;
345 var type = getQueryParameter('console');
6c7c792d 346 if (time_since_started < 5*1000 || type === 'shell' || type === 'cmd') { // 5 seconds
93c96ac5 347 stopTerminal(event);
291e1a24 348 return;
291e1a24
DC
349 }
350
351 updateState(states.disconnecting, 'Detecting migration...');
352 setTimeout(checkMigration, 5000);
353}
354
b9bbd688
DC
355function clearEvents() {
356 term.onResize(() => {});
357 term.onData(() => {});
358}
359
e435736a
DC
360function windowUnload(e) {
361 let message = "Are you sure you want to leave this page?";
362
363 e = e || window.event;
364 if (e) {
365 e.returnValue = message;
366 }
367
368 return message;
369}
370
dcf3d43b 371function stopTerminal(event) {
8c502b81 372 event = event || {};
b9bbd688 373 clearEvents();
dcf3d43b
DC
374 clearInterval(ping);
375 socket.close();
93c96ac5 376 updateState(states.disconnected, event.reason, event.code);
dcf3d43b
DC
377}
378
379function errorTerminal(event) {
34d796a1 380 event = event || {};
b9bbd688 381 clearEvents();
dcf3d43b
DC
382 clearInterval(ping);
383 socket.close();
f21a83b7 384 term.dispose();
93c96ac5 385 updateState(states.disconnected, event.msg, event.code);
dcf3d43b 386}