Mailing-List: contact cygwin-help AT sourceware DOT cygnus DOT com; run by ezmlm List-Unsubscribe: List-Archive: List-Post: List-Help: , Sender: cygwin-owner AT sourceware DOT cygnus DOT com Delivered-To: mailing list cygwin AT sourceware DOT cygnus DOT com Message-ID: <1B8439EA4B41D21196740000F8BDCBD66E0556@zmerd009.ca.nortel.com> From: "Ken Bowler" To: "'cygwin AT sourceware DOT cygnus DOT com'" Subject: B20.1: Problem in exec (NT 4.0) Date: Wed, 11 Aug 1999 16:33:45 -0400 MIME-Version: 1.0 X-Mailer: Internet Mail Service (5.5.2448.0) Content-Type: text/plain; charset="iso-8859-1" X-Orig: I'm having a problem with pipelines form sh.exe when the sh.exe is started via CreateProcess from a non cygwin process which sets stdin, stdout and stderr for the cygwin process (sh.exe) to one end of a connected TCP/IP socket pair. A command pipeline like "/bin/ls . | /bin/cat" results in cat failing with the message "cat: write error: Bad file number". By replacing /bin/cat with a program that I wrote, I determined that cat was started with the stdout fd set to -1. I produced two small programs to demonstrate the problem. The first is a non-cygwin program that creates the socket pair and spawns the second program. It then reads the output from the spawned program from the socket not passed as stdin, stdout and stderr to the child process. This program should be compiled using MSVC (e.g. cl prog1.c -link wsock32.lib). The second is a cygwin program that emulates "sh" for the pipeline described above. It produces messages showing what is occurring as it proceeds. This should be compiled using gcc (e.g. gcc -o prog2.exe prog2.c) To demonstrate the problem issue the command: prog1 .\prog2.exe The output will be as below: prog1: Port assigned is 4977 prog2: Here we are about to create the pipeline prog2: First we spawn the '/bin/cat' prog2: Now we spawn '/bin/ls .' prog2: Child prog=/bin/cat, stdin=0 (duped from 4), stdout=1 (same as parent), stderr=2 (same as parent) prog2: Child prog=/bin/ls, stdin=0 (same as parent), stdout=1 (duped from 5), stderr=2 (same as parent) cat: write error: Bad file number prog2: Pipeline returns with exit code 0 (assuming prog1.exe and prog2.exe are in the same directory) If you invoke prog2 directly from the command line it works correctly. After the fork, and before the exec, the child process in prog2 has the stdout fd with a value of 1 Additional info: System: Dell Opt1Plex GX1 with a 400MZ Pentium II and 128 MB of Memory OS: Windows/NT 4.0 with SP5 installed on D: Cygwin: Beta 20.1 with the binaries installed in d:\bin Program 1: ----------------------------------------------------------------------cut here------------------------------------------------------------------------ ----------- #include #include #include static char errmsg[1024]; static char progname[64]; char *GetErrorMsg(int errnum) { if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errnum, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)errmsg, sizeof(errmsg), NULL) == 0) { sprintf(errmsg, "Win32 error number %ld", errnum); } else { errmsg[strlen(errmsg)-2] = '\0'; /* remove the cr-lf */ } return errmsg; } static int sockpair(SOCKET sv[2]) { struct sockaddr_in local_addr, bound_addr; int addrlen; SOCKET s1, s2, s3; int origOpenType, syncOpenType; int optLen; int rc; int saverrno; unsigned long loop_addr; optLen = sizeof(origOpenType); /* get current socket open type setting */ if ((rc = getsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&origOpenType, &optLen)) != NO_ERROR) { saverrno = WSAGetLastError(); fprintf(stderr, "%s: Unable to obtain current socket opentype options: Winsock error number %d.\n", progname, saverrno); return FALSE; } /* Now set open type so we get synchroinous socket handles suitable for stdin etc. */ syncOpenType = SO_SYNCHRONOUS_NONALERT; if ((rc = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&syncOpenType, sizeof(syncOpenType))) != NO_ERROR) { saverrno = WSAGetLastError(); fprintf(stderr, "%s: Unable to set synchronous socket opentype options: Winsock error number %d.\n", progname, saverrno); return FALSE; } if ((s1 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { saverrno = WSAGetLastError(); fprintf(stderr, "%s: Unable to create a socket: Winsock error number %d.\n", progname, saverrno); goto sckpr_error; } if ((s2 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { saverrno = WSAGetLastError(); fprintf(stderr, "%s: Unable to create a socket: Winsock error number %d.\n", progname, saverrno); closesocket(s1); goto sckpr_error; } loop_addr = inet_addr("127.0.0.1"); memset(&local_addr, '\0', sizeof(local_addr)); local_addr.sin_family = AF_INET; local_addr.sin_port = 0; memcpy(&local_addr.sin_addr, &loop_addr, sizeof(loop_addr)); if (bind(s2, (LPSOCKADDR)&local_addr, sizeof(local_addr)) != 0) { saverrno = WSAGetLastError(); fprintf(stderr, "%s: Unable to bind socket to loopback address: Winsock error number %d.\n", progname, saverrno); closesocket(s1); closesocket(s2); goto sckpr_error; } /* determine what address and port got assigned */ addrlen = sizeof(bound_addr); if (getsockname(s2, (LPSOCKADDR)&bound_addr, &addrlen) != 0) { saverrno = WSAGetLastError(); fprintf(stderr, "%s: Unable to determine port assigned by bind: Winsock error number %d.\n", progname, saverrno); closesocket(s1); closesocket(s2); goto sckpr_error; } local_addr.sin_port = bound_addr.sin_port; printf("%s: Port assigned is %d\n", progname, ntohs(local_addr.sin_port)); listen(s2, 1); /* set up to receive connection */ /* * Since the socket s1 is not non-blocking, the following connect should block * but it doesn't (probably because of a bug). */ if (connect(s1, (struct sockaddr *)&bound_addr, sizeof(bound_addr)) < 0) { saverrno = errno; fprintf(stderr, "%s: Unable to connect the two sockets: Winsock error number %d.\n", progname, saverrno); closesocket(s1); closesocket(s2); goto sckpr_error; } if ((s3 = accept(s2, (struct sockaddr *)&local_addr, (size_t *)&addrlen)) < 0) { saverrno = WSAGetLastError(); fprintf(stderr, "%s: Unable to accept connection: Winsock error number %d.\n", progname, saverrno); closesocket(s1); closesocket(s2); goto sckpr_error; } Sleep(1000); closesocket(s2); setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&origOpenType, sizeof(origOpenType)); sv[0] = s1; sv[1] = s3; return TRUE; sckpr_error: setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&origOpenType, sizeof(origOpenType)); return FALSE; } static SOCKET Spawn_Process(char *cmdline) { STARTUPINFO st_info; PROCESS_INFORMATION proc_info; BOOL success; int saverrno; SOCKET sv[2]; SOCKET ni_sock; if (!sockpair(sv)) /* create a pair of connected sockets */ return FALSE; /* Make one of thses sockets non-inheritable so the chile process does not see our end of the connection */ if (!DuplicateHandle(GetCurrentProcess(), (HANDLE)sv[0], GetCurrentProcess(), (HANDLE *)&ni_sock, 0, FALSE, /* This one not inherited */ DUPLICATE_SAME_ACCESS)) { saverrno = GetLastError(); fprintf(stderr, "%s: Unable to duplicate a non-inheritable server socket: %s\n", progname, GetErrorMsg(saverrno)); return FALSE; } closesocket(sv[0]); /* Close the inheritable copy */ st_info.cb = sizeof(STARTUPINFO); st_info.lpReserved = NULL; st_info.lpReserved2 = NULL; st_info.cbReserved2 = 0; st_info.lpDesktop = NULL; st_info.lpTitle = NULL; st_info.dwFlags = STARTF_USESTDHANDLES; st_info.hStdInput = (HANDLE)sv[1]; st_info.hStdOutput = (HANDLE)sv[1]; st_info.hStdError = (HANDLE)sv[1]; success = CreateProcess(NULL, /* executable path */ cmdline, /* command line */ NULL, /* default security attributes for process */ NULL, /* default security attributes for primary thread */ TRUE, /* proc inherits handles */ 0, /* creation flags */ NULL, /* process environment */ NULL, /* working directory to use */ &st_info, &proc_info); if (!success) { saverrno = GetLastError(); fprintf(stderr, "%s: Unable to spawn process: %s\n", progname, GetErrorMsg(saverrno)); closesocket(ni_sock); closesocket(sv[1]); return INVALID_SOCKET; } CloseHandle(proc_info.hThread); /* we don't need this at all */ closesocket(sv[1]); return ni_sock; } int main(int argc, char *argv[]) { SOCKET sock; int len; char buff[256]; WORD wsck_version; WSADATA wsck_data; int exit_code = 1; int saverrno; char *cp; if ((cp = strrchr(argv[0], '/')) == NULL) { if ((cp = strrchr(argv[0], '\\')) == NULL) cp = argv[0]; else cp++; } else cp++; strcpy(progname, cp); len = strlen(cp); if ((len > 4) && (strcmp(&progname[len-4], ".exe") == 0)) progname[len-4] = '\0'; if (argc < 2) { fprintf(stderr, "Usage: %s path_of_executable\n", progname); ExitProcess(exit_code); } wsck_version = MAKEWORD(1, 0); if (WSAStartup(wsck_version, &wsck_data) != 0) { fprintf(stderr, "%s: InitializeWinsock: The winsock DLL fails to initialize as vers 1.0\n", progname); goto done; } if ((sock = Spawn_Process(argv[1])) == INVALID_SOCKET) goto done; for (;;) { len = recv(sock, buff, sizeof(buff), 0); if (len > 0) write(1, buff, len); else if (len == 0) break; else { saverrno = WSAGetLastError(); fprintf(stderr, "%s: Error reading process socket: Winsock error number %d.\n", progname, saverrno); goto done; } } exit_code = 0; done: WSACleanup(); ExitProcess(exit_code); } ----------------------------------------------------------------------cut here------------------------------------------------------------------------ ----------- Program 2: ----------------------------------------------------------------------cut here------------------------------------------------------------------------ ----------- #include #include #include #include #include static char progname[64]; static FILE *errmsg; static int spawnit(int stdin_fd, int stdout_fd, int stderr_fd, char *prog, char *args[]) { int pid; int saverrno; int fd; char ch_stdin[32], ch_stdout[32], ch_stderr[32]; fflush(stderr); fflush(stdout); if ((pid = fork()) > 0) return pid; else if (pid == 0) { if (stderr_fd != 2) { close(2); if (stderr_fd != -1) { if ((fd = dup(stderr_fd)) != 2) { saverrno = errno; if (fd < 0) fprintf(errmsg, "%s: Dup for stderr fails: %s\n", progname, strerror(saverrno)); else { fprintf(errmsg, "%s: Dup for stderr produces wrong fd, %d instead of 2\n", progname, fd); close(fd); } strcpy(ch_stderr, "-1 (dup failed)"); } else sprintf(ch_stderr, "2 (duped from %d)", stderr_fd); close(stderr_fd); } else strcpy(ch_stderr, "-1 (requested)"); } else sprintf(ch_stderr, "2 (same as parent)"); if (stdout_fd != 1) { close(1); if (stdout_fd != -1) { if ((fd = dup(stdout_fd)) != 1) { saverrno = errno; if (fd < 0) fprintf(errmsg, "%s: Dup for stdout fails: %s\n", progname, strerror(saverrno)); else { fprintf(errmsg, "%s: Dup for stdout produces wrong fd, %d instead of 1\n", progname, fd); close(fd); } strcpy(ch_stdout, "-1 (dup failed)"); } else sprintf(ch_stdout, "1 (duped from %d)", stdout_fd); close(stdout_fd); } else strcpy(ch_stdout, "1 (requested)"); } else strcpy(ch_stdout, "1 (same as parent)"); if (stdin_fd != 0) { close(0); if (stdin_fd != -1) { if ((fd = dup(stdin_fd)) != 0) { saverrno = errno; if (fd < 0) fprintf(errmsg, "%s: Dup for stdin fails: %s\n", progname, strerror(saverrno)); else { fprintf(errmsg, "%s: Dup for stdin produces wrong fd, %d instead of 0\n", progname, fd); close(fd); } strcpy(ch_stdin, "-1 (dup failed)"); } else sprintf(ch_stdin, "0 (duped from %d)", stdin_fd); close(stdin_fd); } else strcpy(ch_stdin, "0 (requested)"); } else strcpy(ch_stdin, "0 (same as parent)"); fprintf(errmsg, "%s: Child prog=%s, stdin=%s, stdout=%s, stderr=%s\n", progname, prog, ch_stdin, ch_stdout, ch_stderr); fflush(errmsg); execv(prog, args); saverrno = errno; fprintf(errmsg, "%s: Execv of %s fails: %s\n", progname, prog, strerror(saverrno)); fflush(errmsg); exit(1); } else return -1; } int main(int argc, char *argv[]) { int errfd; int pipes[2]; char *args[10]; int pid; int status; char *cp; int len; int saverrno; if ((cp = strrchr(argv[0], '/')) == NULL) { if ((cp = strrchr(argv[0], '\\')) == NULL) cp = argv[0]; else cp++; } else cp++; strcpy(progname, cp); len = strlen(cp); if ((len > 4) && (strcmp(&progname[len-4], ".exe") == 0)) progname[len-4] = '\0'; errfd = dup(2); errmsg = fdopen(errfd, "w"); printf("%s: Here we are about to create the pipeline\n", progname); printf("%s: First we spawn the '/bin/cat'\n", progname); if (pipe(pipes) != 0) { saverrno = errno; fprintf(stderr, "%s: Pipe creation fails: %s\n", progname, strerror(saverrno)); goto errdone; } args[0] = "cat"; args[1] = NULL; if (spawnit(pipes[0], 1, 2, "/bin/cat", args) < 0) { saverrno = errno; fprintf(stderr, "%s: Spawn of '/bin/cat' fails: %s\n", progname, strerror(saverrno)); close(pipes[0]); close(pipes[1]); goto errdone; } close(pipes[0]); args[0] = "ls"; args[1] = "."; args[2] = NULL; printf("%s: Now we spawn '/bin/ls .'\n", progname); if ((pid = spawnit(0, pipes[1], 2, "/bin/ls", args)) < 0) { saverrno = errno; fprintf(stderr, "%s: Spawn of '/bin/ls' fails: %s\n", progname, strerror(saverrno)); close(pipes[1]); goto errdone; } close(pipes[1]); waitpid(pid, &status, 0); sleep(5); /* Just so the follwoing message usually follows output from /bin/cat */ printf("%s: Pipeline returns with exit code %d\n", progname, WEXITSTATUS(status)); exit(WEXITSTATUS(status)); errdone: fprintf(stderr, "%s: Test fails\n", progname); exit(-1); } ----------------------------------------------------------------------cut here------------------------------------------------------------------------ ----------- -- Want to unsubscribe from this list? Send a message to cygwin-unsubscribe AT sourceware DOT cygnus DOT com