]> git.proxmox.com Git - mirror_smartmontools-debian.git/blob - os_win32/popen_win32.cpp
import smartmontools 7.0
[mirror_smartmontools-debian.git] / os_win32 / popen_win32.cpp
1 /*
2 * os_win32/popen_win32.cpp
3 *
4 * Home page of code is: https://www.smartmontools.org
5 *
6 * Copyright (C) 2018 Christian Franke
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #include "popen.h"
12
13 const char * popen_win32_cpp_cvsid = "$Id: popen_win32.cpp 4818 2018-10-17 05:32:17Z chrfranke $"
14 POPEN_H_CVSID;
15
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <io.h> // _open_osfhandle()
19 #include <signal.h> // SIGSEGV
20 #include <stdlib.h>
21 #include <string.h>
22
23 #define WIN32_LEAN_AND_MEAN
24 #include <windows.h>
25
26 static FILE * s_popen_file;
27 static HANDLE s_popen_process;
28
29 extern "C"
30 FILE * popen(const char * command, const char * mode)
31 {
32 // Fail if previous run is still in progress
33 if (s_popen_file) {
34 errno = EEXIST;
35 return (FILE *)0;
36 }
37
38 // mode "w" is not implemented
39 if (!(mode[0] == 'r' && (!mode[1] || !mode[2]))) {
40 errno = EINVAL;
41 return (FILE *)0;
42 }
43
44 // Set flags for text or binary mode
45 // Note: _open_osfhandle() ignores _fmode and defaults to O_BINARY
46 int oflags; const char * fomode;
47 switch (mode[1]) {
48 case 0:
49 case 't':
50 oflags = O_RDONLY|O_TEXT;
51 fomode = "rt";
52 break;
53 case 'b':
54 oflags = O_RDONLY|O_BINARY;
55 fomode = "rb";
56 break;
57 default:
58 errno = EINVAL;
59 return (FILE *)0;
60 }
61
62 // Create stdout pipe with inheritable write end
63 HANDLE pipe_out_r, pipe_out_w;
64 if (!CreatePipe(&pipe_out_r, &pipe_out_w, (SECURITY_ATTRIBUTES *)0, 1024)) {
65 errno = EMFILE;
66 return (FILE *)0;
67 }
68 if (!SetHandleInformation(pipe_out_w, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) {
69 CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
70 errno = EMFILE;
71 return (FILE *)0;
72 }
73
74 // Connect pipe read end to new FD
75 int fd = _open_osfhandle((intptr_t)pipe_out_r, oflags);
76 if (fd < 0) {
77 CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
78 return (FILE *)0;
79 }
80
81 // Connect FD to new FILE
82 FILE * f = fdopen(fd, fomode);
83 if (!f) {
84 int err = errno;
85 close(fd); // CloseHandle(pipe_out_r)
86 CloseHandle(pipe_out_w);
87 errno = err;
88 return (FILE *)0;
89 }
90
91 // Build command line "cmd /c COMMAND"
92 int cmdlen = strlen(command);
93 char * shellcmd = (char *)malloc(7 + cmdlen + 1);
94 if (!shellcmd) {
95 fclose(f); // CloseHandle(pipe_out_r)
96 CloseHandle(pipe_out_w);
97 errno = ENOMEM;
98 return (FILE *)0;
99 }
100 memcpy(shellcmd, "cmd /c ", 7);
101 memcpy(shellcmd + 7, command, cmdlen + 1);
102
103 // Redirect stdin stderr to null device
104 // Don't inherit parent's stdin, script may hang if parent has no console.
105 SECURITY_ATTRIBUTES sa_inherit = { sizeof(sa_inherit), (SECURITY_DESCRIPTOR *)0, TRUE };
106 HANDLE null_in = CreateFile("nul", GENERIC_READ , 0, &sa_inherit, OPEN_EXISTING, 0, (HANDLE)0);
107 HANDLE null_err = CreateFile("nul", GENERIC_WRITE, 0, &sa_inherit, OPEN_EXISTING, 0, (HANDLE)0);
108
109 // Set stdio handles
110 STARTUPINFO si; memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
111 si.hStdInput = null_in;
112 si.hStdOutput = pipe_out_w;
113 si.hStdError = null_err;
114 si.dwFlags = STARTF_USESTDHANDLES;
115
116 // Create process
117 PROCESS_INFORMATION pi;
118 BOOL ok = CreateProcessA(
119 getenv("COMSPEC"), // "C:\WINDOWS\system32\cmd.exe" or nullptr
120 shellcmd, // "cmd /c COMMAND" ("cmd" searched in PATH if COMSPEC not set)
121 (SECURITY_ATTRIBUTES *)0, (SECURITY_ATTRIBUTES *)0,
122 TRUE, // inherit
123 CREATE_NO_WINDOW, // DETACHED_PROCESS would open new console(s)
124 (void *)0, (char *)0, &si, &pi
125 );
126 free(shellcmd);
127
128 // Close inherited handles
129 CloseHandle(null_err);
130 CloseHandle(null_in);
131 CloseHandle(pipe_out_w);
132
133 if (!ok) {
134 fclose(f); // CloseHandle(pipe_out_r)
135 errno = ENOENT;
136 return (FILE *)0;
137 }
138
139 // Store process and FILE for pclose()
140 CloseHandle(pi.hThread);
141 s_popen_process = pi.hProcess;
142 s_popen_file = f;
143
144 return f;
145 }
146
147 extern "C"
148 int pclose(FILE * f)
149 {
150 if (f != s_popen_file) {
151 errno = EBADF;
152 return -1;
153 }
154
155 fclose(f);
156 s_popen_file = 0;
157
158 // Wait for process exitcode
159 DWORD exitcode = 42;
160 bool ok = ( WaitForSingleObject(s_popen_process, INFINITE) == WAIT_OBJECT_0
161 && GetExitCodeProcess(s_popen_process, &exitcode));
162
163 CloseHandle(s_popen_process);
164 s_popen_process = 0;
165
166 if (!ok) {
167 errno = ECHILD;
168 return -1;
169 }
170
171 // Modify exitcode for wait(3) macros
172 if (exitcode >> 23)
173 return ((exitcode << 9) >> 1) | SIGSEGV;
174 else
175 return exitcode << 8;
176 }