Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 9782765e0e627511a2154505166b04251d1f03ac (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
/*******************************************************************************
 * Copyright (c) 2002, 2010 QNX Software Systems and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     QNX Software Systems - Initial API and implementation
 *     Wind River Systems, Inc. - bug 248071
 *******************************************************************************/
package org.eclipse.cdt.utils.pty;

import java.io.IOException;

import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.utils.spawner.Spawner;
import org.eclipse.core.runtime.Platform;

/**
 * PTY - pseudo terminal support.
 */
public class PTY {

	/**
	 * The pty modes.
	 * @since 5.6
	 */
	public enum Mode {
		/** This mode is for use with an Eclipse console. */
		CONSOLE,
		/** This mode is for use with a terminal emulator. */
		TERMINAL
	}

	final boolean console;
	final String slave;
	final PTYInputStream in;
	final PTYOutputStream out;

	/**
	 * NOTE: Field is accessed by the native layer. Do not refactor!
	 */
	int master;

	private static boolean hasPTY;
	private static boolean isWinPTY;
	private static boolean isConsoleModeSupported;
	private static boolean setTerminalSizeErrorAlreadyLogged;

	/**
	 * The master fd is used on two streams. We need to wrap the fd
	 * so that when stream.close() is called the other stream is disabled.
	 */
	public class MasterFD {

		public int getFD() {
			return master;
		}

		void setFD(int fd) {
			master = fd;
		}
	}

	/**
	 * @return whether PTY support for console mode is available on this platform
	 */
	public static boolean isSupported() {
		return isSupported(Mode.CONSOLE);
	}

	/**
	 * @return whether PTY support for given mode is available on this platform
	 * @since 5.6
	 */
	public static boolean isSupported(Mode mode ) {
		return hasPTY && (isConsoleModeSupported || mode == Mode.TERMINAL);
	}
	

	/**
	 * Create PTY for use with Eclipse console.
	 * Identical to {@link PTY#PTY(boolean) PTY(Mode.CONSOLE)}.
	 */
	public PTY() throws IOException {
		this(Mode.CONSOLE);
	}

	/**
	 * Create PTY for given mode.
	 * 
	 * <p>
	 * The provided mode indicates whether the pseudo terminal is used with the interactive
	 * Eclipse console or a terminal emulation:
	 * <ul>
	 * <li><code>CONSOLE</code> - the terminal is configured with no echo and stderr is
	 * redirected to a pipe instead of the PTY. This mode is not supported on windows</li>
	 * <li><code>TERMINAL</code> - the terminal is configured with echo and stderr is
	 * connected to the PTY. This mode is best suited for use with a proper terminal emulation.
	 * Note that this mode might not be supported on all platforms.
	 * Known platforms which support this mode are:
	 * <code>linux-x86</code>, <code>linux-x86_64</code>, <code>solaris-sparc</code>, <code>macosx</code>.
	 * </li>
	 * </ul>
	 * </p>
	 * @param mode  the desired mode of operation
	 * @throws IOException  if the PTY could not be created
	 * @since 5.6
	 */
	public PTY(Mode mode) throws IOException {
		this(mode == Mode.CONSOLE);
	}

	/**
	 * Create pseudo terminal.
	 * 
	 * <p>
	 * The provided flag indicates whether the pseudo terminal is used with the interactive
	 * Eclipse console:
	 * <ul>
	 * <li>If <code>true</code> the terminal is configured with no echo and stderr is
	 * redirected to a pipe instead of the PTY. This mode is not supported on windows</li>
	 * <li>If <code>false</code> the terminal is configured with echo and stderr is
	 * connected to the PTY. This mode is best suited for use with a proper terminal emulation.
	 * Note that this mode might not be supported on all platforms.
	 * Known platforms which support this mode are:
	 * <code>linux-x86</code>, <code>linux-x86_64</code>, <code>solaris-sparc</code>, <code>macosx</code>.
	 * </li>
	 * </ul>
	 * </p>
	 * 
	 * @param console  whether terminal is used with Eclipse console
	 * @throws IOException  if the PTY could not be created
	 * @deprecated Use {@link #PTY(Mode)} instead
	 * @since 5.2
	 */
	@Deprecated
	public PTY(boolean console) throws IOException {
		this.console = console;
		if (console && !isConsoleModeSupported) {
			throw new IOException(CCorePlugin.getResourceString("Util.exception.cannotCreatePty")); //$NON-NLS-1$
		}
		slave= hasPTY ? openMaster(console) : null;

		if (slave == null) {
			throw new IOException(CCorePlugin.getResourceString("Util.exception.cannotCreatePty")); //$NON-NLS-1$
		}

		in = new PTYInputStream(new MasterFD());
		out = new PTYOutputStream(new MasterFD());
	}

	/**
	 * Test whether the slave name can be used as a tty device by external processes (e.g. gdb).
	 * If the slave name is not valid an IOException is thrown.
	 * @throws IOException  if the slave name is not valid
	 * @since 5.6
	 */
	public void validateSlaveName() throws IOException {
		// on windows the slave name is just an internal identifier
		// and does not represent a real device
		if (isWinPTY)
			throw new IOException("Slave name is not valid"); //$NON-NLS-1$
	}

	public String getSlaveName() {
		return slave;
	}

	public MasterFD getMasterFD() {
		return new MasterFD();
	}

	/**
	 * @return whether this pseudo terminal is for use with the Eclipse console.
	 * 
	 * @since 5.2
	 */
	public final boolean isConsole() {
		return console;
	}
	
	public PTYOutputStream getOutputStream() {
		return out;
	}
	
	public PTYInputStream getInputStream() {
		return in;
	}

	/**
	 * Change terminal window size to given width and height.
	 * <p>
	 * This should only be used when the pseudo terminal is configured
	 * for use with a terminal emulation, i.e. when {@link #isConsole()} 
	 * returns <code>false</code>.
	 * </p>
	 * <p>
	 * <strong>Note:</strong> This method may not be supported on all platforms.
	 * Known platforms which support this method are:
	 * <code>linux-x86</code>, <code>linux-x86_64</code>, <code>solaris-sparc</code>, <code>macosx</code>.
	 * </p>
	 * 
	 * @since 5.2
	 */
	public final void setTerminalSize(int width, int height) {
		try {
			change_window_size(master, width, height);
		} catch (UnsatisfiedLinkError ule) {
			if (!setTerminalSizeErrorAlreadyLogged) {
				setTerminalSizeErrorAlreadyLogged = true;
				CCorePlugin.log(CCorePlugin.getResourceString("Util.exception.cannotSetTerminalSize"), ule); //$NON-NLS-1$
			}
		}
	}

	/**
	 * @noreference This method is not intended to be referenced by clients.
	 * @since 5.6
	 */
	public int exec_pty(Spawner spawner, String[] cmdarray, String[] envp, String dir, int[] chan) throws IOException {
		if (isWinPTY) {
			return exec2(cmdarray, envp, dir, chan, slave, master, console);
		} else {
			return spawner.exec2(cmdarray, envp, dir, chan, slave, master, console);
		}
	}

	/**
	 * @noreference This method is not intended to be referenced by clients.
	 * @since 5.6
	 */
	public int waitFor(Spawner spawner, int pid) {
		if (isWinPTY) {
			return waitFor(master, pid);
		} else {
			return spawner.waitFor(pid);
		}
	}

	native String openMaster(boolean console);

	native int change_window_size(int fdm, int width, int height);

	/**
	 * Native method when executing with a terminal emulation (winpty only).
	 */
	native int exec2(String[] cmdarray, String[] envp, String dir, int[] chan, String slaveName, int masterFD, boolean console) throws IOException;

	/**
	 * Native method to wait for process to terminate (winpty only).
	 */
	native int waitFor(int masterFD, int processID);

	static {
		try {
			System.loadLibrary("pty"); //$NON-NLS-1$
			hasPTY = true;
			isWinPTY = Platform.OS_WIN32.equals(Platform.getOS());
			// on windows console mode is not supported except for experimental use
			// to enable it, set system property org.eclipse.cdt.core.winpty_console_mode=true
			isConsoleModeSupported = !isWinPTY || Boolean.getBoolean("org.eclipse.cdt.core.winpty_console_mode"); //$NON-NLS-1$
		} catch (SecurityException e) {
			// Comment out it worries the users too much
			//CCorePlugin.log(e);
		} catch (UnsatisfiedLinkError e) {
			// Comment out it worries the users too much
			//CCorePlugin.log(e);
		}
	}
	
}

Back to the top