X-Recipient: archive-cygwin AT delorie DOT com X-Spam-Check-By: sourceware.org Date: Mon, 26 Nov 2012 17:04:46 +0100 From: Corinna Vinschen To: cygwin AT cygwin DOT com Subject: Re: isatty gives wrong result via ssh Message-ID: <20121126160446.GB3800@calimero.vinschen.de> Reply-To: cygwin AT cygwin DOT com Mail-Followup-To: cygwin AT cygwin DOT com References: <50B385F9 DOT 80103 AT kouptsov DOT com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="PNTmBPCT7hxwcZjr" Content-Disposition: inline In-Reply-To: <50B385F9.80103@kouptsov.com> User-Agent: Mutt/1.5.21 (2010-09-15) Mailing-List: contact cygwin-help AT cygwin DOT com; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: cygwin-owner AT cygwin DOT com Mail-Followup-To: cygwin AT cygwin DOT com Delivered-To: mailing list cygwin AT cygwin DOT com --PNTmBPCT7hxwcZjr Content-Type: text/plain; charset=utf-8 Content-Disposition: inline On Nov 26 10:08, Konstantin Kouptsov wrote: > On Windows, if I compile it using a Microsoft compiler: > > C: > cl /out:checktty.exe checktty.c > > the program behaves correctly when I run it from a DOS prompt or from a Cywin's bash prompt. However, if I connect to the Windows computer running Cygwin's sshd service from another Linux or Windows computer, it always gives the same result: > > $ ./checktty.exe > not a tty > > $ ./checktty.exe < checktty.c > not a tty > > (When I compile with Cygwin's gcc, everything is fine) > > What happens here? Cygwin pseudo ttys are implemented as pipes, and the msvcrt runtime has no idea about that. It sees a pipe and that's no tty from its POV. > Given that I must compile the program using Microsoft's compiler on Windows, how this can be worked around? The workaround is to implement your own isatty function which recognizes Cygwin pseudo ttys as well. I had a customer asking this question, too, at one point and I sent them example code. I attached it to this mail. HTH, Corinna -- Corinna Vinschen Please, send mails regarding Cygwin to Cygwin Project Co-Leader cygwin AT cygwin DOT com Red Hat --PNTmBPCT7hxwcZjr Content-Type: text/plain; charset=utf-8 Content-Disposition: attachment; filename="isatty-for-native-apps.c" #include #include #include #include #include #include #ifndef __MINGW64_VERSION_MAJOR /* MS winternl.h defines FILE_INFORMATION_CLASS, but with only a different single member. */ enum FILE_INFORMATION_CLASSX { FileNameInformation = 9 }; typedef struct _FILE_NAME_INFORMATION { ULONG FileNameLength; WCHAR FileName[1]; } FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION; NTSTATUS (NTAPI *pNtQueryInformationFile) (HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASSX); #else NTSTATUS (NTAPI *pNtQueryInformationFile) (HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS); #endif int isatty (int fd) { HANDLE fh; NTSTATUS status; IO_STATUS_BLOCK io; long buf[66]; /* NAME_MAX + 1 + sizeof ULONG */ PFILE_NAME_INFORMATION pfni = (PFILE_NAME_INFORMATION) buf; PWCHAR cp; /* First check using _isatty. Note that this returns the wrong result for NUL, for instance! Workaround is not to use _isatty at all, but rather GetFileType plus object name checking. */ if (_isatty (fd)) return 1; /* Now fetch the underlying HANDLE. */ fh = (HANDLE) _get_osfhandle (fd); if (!fh || fh == INVALID_HANDLE_VALUE) { errno = EBADF; return 0; } /* Must be a pipe. */ if (GetFileType (fh) != FILE_TYPE_PIPE) goto no_tty; /* Calling the native NT function NtQueryInformationFile is required to support pre-Vista systems. If that's of no concern, Vista introduced the GetFileInformationByHandleEx call with the FileNameInfo info class, which can be used instead. */ if (!pNtQueryInformationFile) { pNtQueryInformationFile = (NTSTATUS (NTAPI *)(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS)) GetProcAddress (GetModuleHandle ("ntdll.dll"), "NtQueryInformationFile"); if (!pNtQueryInformationFile) goto no_tty; } if (!NT_SUCCESS (pNtQueryInformationFile (fh, &io, pfni, sizeof buf, FileNameInformation))) goto no_tty; /* The filename is not guaranteed to be NUL-terminated. */ pfni->FileName[pfni->FileNameLength / sizeof (WCHAR)] = L'\0'; /* Now check the name pattern. The filename of a Cygwin pseudo tty pipe looks like this: \cygwin-%16llx-pty%d-{to,from}-master %16llx is the hash of the Cygwin installation, (to support multiple parallel installations), %d id the pseudo tty number, "to" or "from" differs the pipe direction. "from" is a stdin, "to" a stdout-like pipe. */ cp = pfni->FileName; if (!wcsncmp (cp, L"\\cygwin-", 8) && !wcsncmp (cp + 24, L"-pty", 4)) { cp = wcschr (cp + 28, '-'); if (!cp) goto no_tty; if (!wcscmp (cp, L"-from-master") || !wcscmp (cp, L"-to-master")) return 1; } no_tty: errno = EINVAL; return 0; } int main () { if (isatty(0)) printf("tty\n"); else printf("not a tty\n"); return 0; } --PNTmBPCT7hxwcZjr Content-Type: text/plain; charset=us-ascii -- Problem reports: http://cygwin.com/problems.html FAQ: http://cygwin.com/faq/ Documentation: http://cygwin.com/docs.html Unsubscribe info: http://cygwin.com/ml/#unsubscribe-simple --PNTmBPCT7hxwcZjr--