GNU Octave 11.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
octave-popen2.c
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 2016-2026 The Octave Project Developers
4//
5// See the file COPYRIGHT.md in the top-level directory of this
6// distribution or <https://octave.org/copyright/>.
7//
8// This file is part of Octave.
9//
10// Octave is free software: you can redistribute it and/or modify it
11// under the terms of the GNU General Public License as published by
12// the Free Software Foundation, either version 3 of the License, or
13// (at your option) any later version.
14//
15// Octave is distributed in the hope that it will be useful, but
16// WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU General Public License for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with Octave; see the file COPYING. If not, see
22// <https://www.gnu.org/licenses/>.
23//
24////////////////////////////////////////////////////////////////////////
25
26// It makes more sense to define octave_popen2 with the wrapper
27// functions than it does to try to provide wrappers for all the
28// individual functions and macros that it uses and that may be provided
29// by gnulib. We don't include gnulib headers directly in Octave's C++
30// source files to avoid problems that may be caused by the way that
31// gnulib overrides standard library functions.
32
33#if defined (HAVE_CONFIG_H)
34# include "config.h"
35#endif
36
37#include <stdbool.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41
42#if defined (__WIN32__) && ! defined (__CYGWIN__)
43# include <fcntl.h>
44# include <io.h>
45# define WIN32_LEAN_AND_MEAN 1
46# include <windows.h>
47
48# include "uniconv-wrappers.h"
49#else
50# include <errno.h>
51# include <fcntl.h>
52# include <sys/types.h>
53# include <sys/wait.h>
54# include <unistd.h>
55#endif
56
57#include "octave-popen2.h"
58
59#if defined (__WIN32__) && ! defined (__CYGWIN__)
60
61static char *
62make_command_string (const char *cmd, char *const *args)
63{
64 char *const *argp;
65 size_t cmd_len;
66 char *command;
67 size_t arg_len;
68
69 // Count command length, quotes, and terminating NUL character.
70 cmd_len = strlen (cmd) + 3;
71
72 // Count argument length, space, and quotes.
73 // Ignore first arg as it is the command.
74 argp = args;
75 while (*++argp)
76 cmd_len += strlen (*argp) + 3;
77
78 command = (char *) malloc (cmd_len);
79
80 // double-quote command
81 sprintf (command, "\"%s\"", cmd);
82
83 argp = args;
84 cmd_len = strlen (cmd) + 2;
85 while (*++argp)
86 {
87 // append double-quoted argument separated by space to command buffer
88 command[cmd_len++] = ' ';
89 command[cmd_len++] = '"';
90 arg_len = strlen (*argp);
91 memcpy (command + cmd_len, *argp, arg_len);
92 cmd_len += arg_len;
93 command[cmd_len++] = '"';
94 }
95 command[cmd_len] = 0;
96
97 return command;
98}
99
100pid_t
101octave_popen2 (const char *cmd, char *const *args, bool sync_mode,
102 int *fildes, const char **errmsg)
103{
104 pid_t pid;
105
106 char *command;
107 bool status;
108
109 PROCESS_INFORMATION pi;
110 STARTUPINFO si;
111
112 HANDLE hProcess = GetCurrentProcess ();
113 HANDLE childRead, childWrite, parentRead, parentWrite;
114 DWORD pipeMode;
115
116 ZeroMemory (&pi, sizeof (pi));
117 ZeroMemory (&si, sizeof (si));
118 si.cb = sizeof (si);
119
120 if (! CreatePipe (&childRead, &parentWrite, 0, 0)
121 || ! DuplicateHandle (hProcess, childRead, hProcess, &childRead,
122 0, TRUE,
123 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
124 {
125 *errmsg = "popen2: pipe creation failed";
126 return -1;
127 }
128
129 if (! CreatePipe (&parentRead, &childWrite, 0, 0)
130 || ! DuplicateHandle (hProcess, childWrite, hProcess, &childWrite,
131 0, TRUE,
132 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
133 {
134 *errmsg = "popen2: pipe creation failed";
135 return -1;
136 }
137
138 if (! sync_mode)
139 {
140 pipeMode = PIPE_NOWAIT;
141 SetNamedPipeHandleState (parentRead, &pipeMode, 0, 0);
142 }
143
144 fildes[1] = _open_osfhandle ((intptr_t) parentRead, _O_RDONLY | _O_BINARY);
145 fildes[0] = _open_osfhandle ((intptr_t) parentWrite, _O_WRONLY | _O_BINARY);
146
147 si.dwFlags |= STARTF_USESTDHANDLES;
148
149 si.hStdInput = childRead;
150 si.hStdOutput = childWrite;
151 si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
152
153 command = make_command_string (cmd, args);
154
155 wchar_t *wcmd = u8_to_wchar (command);
156
157 free (command);
158
159 status = CreateProcessW (NULL, wcmd, NULL, NULL, TRUE, CREATE_NO_WINDOW,
160 NULL, NULL, &si, &pi);
161
162 free (wcmd);
163
164 if (! status)
165 {
166 *errmsg = "popen2: process creation failed";
167 return -1;
168 }
169
170 pid = pi.dwProcessId;
171
172 CloseHandle (childRead);
173 CloseHandle (childWrite);
174
175 CloseHandle (pi.hProcess);
176 CloseHandle (pi.hThread);
177
178 return pid;
179}
180
181#else
182
183// Magic value used to indicate child failed back to parent process
184#define OCTAVE_CHILD_FAILURE 129
185
186pid_t
187octave_popen2 (const char *cmd, char *const *args, bool sync_mode,
188 int *fildes, const char **errmsg)
189{
190 pid_t pid;
191
192 int child_stdin[2], child_stdout[2];
193
194 if (pipe (child_stdin) < 0)
195 {
196 *errmsg = strerror (errno);
197 return -1;
198 }
199
200 if (pipe (child_stdout) < 0)
201 {
202 close (child_stdin[0]);
203 close (child_stdin[1]);
204
205 *errmsg = strerror (errno);
206 return -1;
207 }
208
209 pid = fork ();
210
211 if (pid == 0)
212 {
213 // Child process
214
215 close (child_stdin[1]);
216 close (child_stdout[0]);
217
218 if (dup2 (child_stdin[0], STDIN_FILENO) >= 0)
219 {
220 close (child_stdin[0]);
221
222 if (dup2 (child_stdout[1], STDOUT_FILENO) >= 0)
223 {
224 close (child_stdout[1]);
225
226 if (execvp (cmd, args) < 0)
227 perror ("error: popen2 (child)");
228 }
229 else
230 perror ("error: popen2 (child)");
231 }
232 else
233 perror ("error: popen2 (child)");
234
235 _Exit (OCTAVE_CHILD_FAILURE);
236 }
237 else if (pid > 0)
238 {
239 // Parent process
240
241 close (child_stdin[0]);
242 close (child_stdout[1]);
243
244#if defined (F_SETFL) && defined (O_NONBLOCK)
245 if (! sync_mode && fcntl (child_stdout[0], F_SETFL, O_NONBLOCK) < 0)
246 {
247 *errmsg = strerror (errno);
248 return -1;
249 }
250 else
251#endif
252 {
253 fildes[0] = child_stdin[1];
254 fildes[1] = child_stdout[0];
255
256 // Sanity check that child process hasn't already aborted.
257 // Note: Maybe use nanosleep() as usleep() is deprecated/removed
258 // after 2008?
259 usleep (5e3); // wait for 5 milliseconds
260 int wstatus;
261 pid_t tst_pid = waitpid (pid, &wstatus, WNOHANG);
262 if (tst_pid == -1)
263 {
264 // Error with waitpid ()
265 *errmsg = strerror (errno);
266 return -1;
267 }
268 else if (tst_pid != 0)
269 {
270 // Child has already changed state, probably bad, but verify
271 if (WIFEXITED(wstatus)
272 && WEXITSTATUS(wstatus) == OCTAVE_CHILD_FAILURE)
273 {
274 *errmsg = "popen2 (parent): failure in child process";
275 return -1;
276 }
277 }
278
279 return pid;
280 }
281 }
282
283 // Reaching this code means fork() was unsuccessful
284 *errmsg = strerror (errno);
285 return -1;
286}
287
288#endif
T::size_type strlen(const typename T::value_type *str)
Definition oct-string.cc:95
int pipe(int *fildes)
pid_t fork(std::string &msg)
int execvp(const std::string &file, const string_vector &argv)
pid_t waitpid(pid_t pid, int *status, int options)
int fcntl(int fd, int cmd, long arg)
int dup2(int old_fd, int new_fd)
void * malloc(unsigned)
void free(void *)
#define OCTAVE_CHILD_FAILURE
pid_t octave_popen2(const char *cmd, char *const *args, bool sync_mode, int *fildes, const char **errmsg)
#define STDIN_FILENO
Definition sysdep.cc:92
wchar_t * u8_to_wchar(const char *u8)
#define WNOHANG