summaryrefslogtreecommitdiffstats
path: root/lib/windows-spawn.h
blob: b218181803fdffc63e317c7d43fb1fd9c96221b1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/* Auxiliary functions for the creation of subprocesses.  Native Windows API.
   Copyright (C) 2001, 2003-2023 Free Software Foundation, Inc.
   Written by Bruno Haible <bruno@clisp.org>, 2003.

   This file is free software: you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation; either version 2.1 of the
   License, or (at your option) any later version.

   This file is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */

#ifndef _WINDOWS_SPAWN_H
#define _WINDOWS_SPAWN_H

/* This file uses _GL_ATTRIBUTE_MALLOC.  */
#if !_GL_CONFIG_H_INCLUDED
 #error "Please include config.h first."
#endif

#include <stdint.h>
#include <stdlib.h>

/* Get declarations of the native Windows API functions.  */
#define WIN32_LEAN_AND_MEAN
#include <windows.h>


/* Prepares an argument vector before calling spawn().

   Note that spawn() does not by itself call the command interpreter
     (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") :
      ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
         GetVersionEx(&v);
         v.dwPlatformId == VER_PLATFORM_WIN32_NT;
      }) ? "cmd.exe" : "command.com").
   Instead it simply concatenates the arguments, separated by ' ', and calls
   CreateProcess().  We must quote the arguments since Windows CreateProcess()
   interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a
   special way:
   - Space and tab are interpreted as delimiters. They are not treated as
     delimiters if they are surrounded by double quotes: "...".
   - Unescaped double quotes are removed from the input. Their only effect is
     that within double quotes, space and tab are treated like normal
     characters.
   - Backslashes not followed by double quotes are not special.
   - But 2*n+1 backslashes followed by a double quote become
     n backslashes followed by a double quote (n >= 0):
       \" -> "
       \\\" -> \"
       \\\\\" -> \\"
   - '*', '?' characters may get expanded through wildcard expansion in the
     callee: By default, in the callee, the initialization code before main()
     takes the result of GetCommandLine(), wildcard-expands it, and passes it
     to main(). The exceptions to this rule are:
       - programs that inspect GetCommandLine() and ignore argv,
       - mingw programs that have a global variable 'int _CRT_glob = 0;',
       - Cygwin programs, when invoked from a Cygwin program.

   prepare_spawn creates and returns a new argument vector, where the arguments
   are appropriately quoted and an additional argument "sh.exe" has been added
   at the beginning.  The new argument vector is freshly allocated.  The memory
   for all its elements is allocated within *MEM_TO_FREE, which is freshly
   allocated as well.  In case of memory allocation failure, NULL is returned,
   with errno set.
 */
extern const char ** prepare_spawn (const char * const *argv,
                                    char **mem_to_free);

/* Composes the command to be passed to CreateProcess().
   ARGV must contain appropriately quoted arguments, as returned by
   prepare_spawn.
   Returns a freshly allocated string.  In case of memory allocation failure,
   NULL is returned, with errno set.  */
extern char * compose_command (const char * const *argv)
  _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_DEALLOC_FREE;

/* Composes the block of memory that contains the environment variables.
   ENVP must contain an environment (a NULL-terminated array of string of the
   form VARIABLE=VALUE).
   Returns a freshly allocated block of memory.  In case of memory allocation
   failure, NULL is returned, with errno set.  */
extern char * compose_envblock (const char * const *envp)
  _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_DEALLOC_FREE;


/* An inheritable handle with some additional information.  */
struct IHANDLE
{
  /* Either INVALID_HANDLE_VALUE or an inheritable handle.  */
  HANDLE handle;
  /* Only relevant if handle != INVALID_HANDLE_VALUE.
     It is a bit mask consisting of:
       - 32 for O_APPEND.
       - KEEP_OPEN_IN_CHILD if the handle is scheduled to be preserved in the
         child process.
       - KEEP_OPEN_IN_PARENT if the handle is shared with (and needs to be kept
         open in) the parent process.
       - DELAYED_DUP2_OLDFD if there is a delayed dup2 (oldfd, newfd) and
         this IHANDLE is at position oldfd.
       - DELAYED_DUP2_NEWFD if there is a delayed dup2 (oldfd, newfd) and
         this IHANDLE is at position newfd.
     Note that DELAYED_DUP2_OLDFD and DELAYED_DUP2_NEWFD cannot be set in the
     same IHANDLE.  */
  unsigned short flags;
  #define KEEP_OPEN_IN_CHILD 0x100
  #define KEEP_OPEN_IN_PARENT 0x200
  #define DELAYED_DUP2_OLDFD 0x400
  #define DELAYED_DUP2_NEWFD 0x800
  /* Only relevant if handle != INVALID_HANDLE_VALUE and flags contains
     either DELAYED_DUP2_OLDFD or DELAYED_DUP2_NEWFD.
     It is the other fd of the delayed dup2 (oldfd, newfd), i.e.
       - for DELAYED_DUP2_OLDFD, the newfd,
       - for DELAYED_DUP2_NEWFD, the oldfd.  */
  int linked_fd;
};

/* This struct keeps track of which handles to potentially pass to a subprocess,
   and with which flags.  All of the handles here are inheritable.
   Regarding handle inheritance, see
   <https://docs.microsoft.com/en-us/windows/win32/sysinfo/handle-inheritance>.
   Whether a handle is actually scheduled for being preserved in the child
   process is determined by the KEEP_OPEN_IN_CHILD bit in the flags.  */
struct inheritable_handles
{
  /* The number of occupied entries in the two arrays below.
     3 <= count <= allocated.  */
  size_t count;
  /* The number of allocated entries in the two arrays below.  */
  size_t allocated;
  /* ih[0..count-1] are the occupied entries.  */
  struct IHANDLE *ih;
};

/* Initializes a set of inheritable handles, filling in all or part of the
   file descriptors of the current process.
   If DUPLICATE is true, the handles stored are those of all file descriptors,
   and we use DuplicateHandle to make sure that they are all inheritable.
   If DUPLICATE is false, the handles stored are only the inheritables ones;
   this is a shortcut for spawnpvech().
   Returns 0 upon success.  In case of failure, -1 is returned, with errno set.
 */
extern int init_inheritable_handles (struct inheritable_handles *inh_handles,
                                     bool duplicate);

/* Fills a set of inheritable handles into a STARTUPINFO for CreateProcess().
   Returns 0 upon success.  In case of failure, -1 is returned, with errno set.
 */
extern int compose_handles_block (const struct inheritable_handles *inh_handles,
                                  STARTUPINFOA *sinfo);

/* Frees the memory held by a set of inheritable handles.  */
extern void free_inheritable_handles (struct inheritable_handles *inh_handles);


/* Converts a CreateProcess() error code (retrieved through GetLastError()) to
   an errno value.  */
extern int convert_CreateProcess_error (DWORD error);


/* Creates a subprocess.
   MODE is either P_WAIT or P_NOWAIT.
   PROGNAME is the program to invoke.
   ARGV is the NULL-terminated array of arguments, ARGV[0] being PROGNAME by
   convention.
   ENVP is the NULL-terminated set of environment variable assignments, or NULL
   to inherit the initial environ variable assignments from the caller and
   ignore all calls to putenv(), setenv(), unsetenv() done in the caller.
   CURRDIR is the directory in which to start the program, or NULL to inherit
   the working directory from the caller.
   STDIN_HANDLE, STDOUT_HANDLE, STDERR_HANDLE are the handles to use for the
   first three file descriptors in the callee process.
   Returns
     - 0 for success (if MODE is P_WAIT), or
     - a handle that be passed to _cwait (on Windows) or waitpid (on OS/2), or
     - -1 upon error, with errno set.
 */
extern intptr_t spawnpvech (int mode,
                            const char *progname, const char * const *argv,
                            const char * const *envp,
                            const char *currdir,
                            HANDLE stdin_handle, HANDLE stdout_handle,
                            HANDLE stderr_handle);

#endif /* _WINDOWS_SPAWN_H */