GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
octave-popen2.c
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2016-2024 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 <unistd.h>
54 #endif
55 
56 #include "octave-popen2.h"
57 
58 #if defined (__WIN32__) && ! defined (__CYGWIN__)
59 
60 static char *
61 make_command_string (const char *cmd, char *const *args)
62 {
63  char *const *argp;
64  size_t cmd_len;
65  char *command;
66 
67  // Count Command length, quotes, and terminating NUL character.
68  cmd_len = strlen (cmd) + 3;
69 
70  // Count argument length, space, and quotes.
71  // Ignore first arg as it is the command.
72  argp = args;
73  while (*++argp)
74  cmd_len += strlen (*argp) + 3;
75 
76  command = (char *) malloc (cmd_len);
77 
78  sprintf (command, "\"%s\"", cmd);
79 
80  argp = args;
81  while (*++argp)
82  sprintf (command, "%s \"%s\"", command, *argp);
83 
84  return command;
85 }
86 
87 pid_t
88 octave_popen2 (const char *cmd, char *const *args, bool sync_mode,
89  int *fildes, const char **errmsg)
90 {
91  pid_t pid;
92 
93  char *command;
94  bool status;
95 
96  PROCESS_INFORMATION pi;
97  STARTUPINFO si;
98 
99  HANDLE hProcess = GetCurrentProcess ();
100  HANDLE childRead, childWrite, parentRead, parentWrite;
101  DWORD pipeMode;
102 
103  ZeroMemory (&pi, sizeof (pi));
104  ZeroMemory (&si, sizeof (si));
105  si.cb = sizeof (si);
106 
107  if (! CreatePipe (&childRead, &parentWrite, 0, 0)
108  || ! DuplicateHandle (hProcess, childRead, hProcess, &childRead,
109  0, TRUE,
110  DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
111  {
112  *errmsg = "popen2: pipe creation failed";
113  return -1;
114  }
115 
116  if (! CreatePipe (&parentRead, &childWrite, 0, 0)
117  || ! DuplicateHandle (hProcess, childWrite, hProcess, &childWrite,
118  0, TRUE,
119  DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
120  {
121  *errmsg = "popen2: pipe creation failed";
122  return -1;
123  }
124 
125  if (! sync_mode)
126  {
127  pipeMode = PIPE_NOWAIT;
128  SetNamedPipeHandleState (parentRead, &pipeMode, 0, 0);
129  }
130 
131  fildes[1] = _open_osfhandle ((intptr_t) parentRead, _O_RDONLY | _O_BINARY);
132  fildes[0] = _open_osfhandle ((intptr_t) parentWrite, _O_WRONLY | _O_BINARY);
133 
134  si.dwFlags |= STARTF_USESTDHANDLES;
135 
136  si.hStdInput = childRead;
137  si.hStdOutput = childWrite;
138  si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
139 
140  command = make_command_string (cmd, args);
141 
142  wchar_t *wcmd = u8_to_wchar (command);
143 
144  free (command);
145 
146  status = CreateProcessW (NULL, wcmd, NULL, NULL, TRUE, CREATE_NO_WINDOW,
147  NULL, NULL, &si, &pi);
148 
149  free (wcmd);
150 
151  if (! status)
152  {
153  *errmsg = "popen2: process creation failed";
154  return -1;
155  }
156 
157  pid = pi.dwProcessId;
158 
159  CloseHandle (childRead);
160  CloseHandle (childWrite);
161 
162  CloseHandle (pi.hProcess);
163  CloseHandle (pi.hThread);
164 
165  return pid;
166 }
167 
168 #else
169 
170 pid_t
171 octave_popen2 (const char *cmd, char *const *args, bool sync_mode,
172  int *fildes, const char **errmsg)
173 {
174  pid_t pid;
175 
176  int child_stdin[2], child_stdout[2];
177 
178  if (pipe (child_stdin) < 0)
179  {
180  *errmsg = strerror (errno);
181  return -1;
182  }
183 
184  if (pipe (child_stdout) < 0)
185  {
186  close (child_stdin[0]);
187  close (child_stdin[1]);
188 
189  *errmsg = strerror (errno);
190  return -1;
191  }
192 
193  pid = fork ();
194 
195  if (pid == 0)
196  {
197  // Child process
198 
199  close (child_stdin[1]);
200  close (child_stdout[0]);
201 
202  if (dup2 (child_stdin[0], STDIN_FILENO) >= 0)
203  {
204  close (child_stdin[0]);
205 
206  if (dup2 (child_stdout[1], STDOUT_FILENO) >= 0)
207  {
208  close (child_stdout[1]);
209 
210  if (execvp (cmd, args) < 0)
211  perror ("popen2 (child)");
212  }
213  else
214  perror ("popen2 (child)");
215  }
216  else
217  perror ("popen2 (child)");
218 
219  _exit (127);
220  }
221  else if (pid > 0)
222  {
223  // Parent process
224 
225  close (child_stdin[0]);
226  close (child_stdout[1]);
227 
228 #if defined (F_SETFL) && defined (O_NONBLOCK)
229  if (! sync_mode && fcntl (child_stdout[0], F_SETFL, O_NONBLOCK) < 0)
230  {
231  *errmsg = strerror (errno);
232  return -1;
233  }
234  else
235 #endif
236  {
237  fildes[0] = child_stdin[1];
238  fildes[1] = child_stdout[0];
239 
240  return pid;
241  }
242  }
243 
244  *errmsg = "foobar!";
245  *errmsg = strerror (errno);
246  return pid;
247 }
248 
249 #endif
T::size_type strlen(const typename T::value_type *str)
Definition: oct-string.cc:88
int pipe(int *fildes)
pid_t fork(std::string &msg)
Definition: oct-syscalls.cc:99
int execvp(const std::string &file, const string_vector &argv)
Definition: oct-syscalls.cc:74
int fcntl(int fd, int cmd, long arg)
int dup2(int old_fd, int new_fd)
Definition: oct-syscalls.cc:52
void * malloc(unsigned)
void free(void *)
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)