Rizin
unix-like reverse engineering framework and cli tools
process.c
Go to the documentation of this file.
1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to
5  * deal in the Software without restriction, including without limitation the
6  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7  * sell copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19  * IN THE SOFTWARE.
20  */
21 
22 #include <assert.h>
23 #include <io.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <signal.h>
27 #include <limits.h>
28 #include <wchar.h>
29 #include <malloc.h> /* alloca */
30 
31 #include "uv.h"
32 #include "internal.h"
33 #include "handle-inl.h"
34 #include "req-inl.h"
35 
36 
37 #define SIGKILL 9
38 
39 
40 typedef struct env_var {
41  const WCHAR* const wide;
42  const WCHAR* const wide_eq;
43  const size_t len; /* including null or '=' */
45 
46 #define E_V(str) { L##str, L##str L"=", sizeof(str) }
47 
48 static const env_var_t required_vars[] = { /* keep me sorted */
49  E_V("HOMEDRIVE"),
50  E_V("HOMEPATH"),
51  E_V("LOGONSERVER"),
52  E_V("PATH"),
53  E_V("SYSTEMDRIVE"),
54  E_V("SYSTEMROOT"),
55  E_V("TEMP"),
56  E_V("USERDOMAIN"),
57  E_V("USERNAME"),
58  E_V("USERPROFILE"),
59  E_V("WINDIR"),
60 };
61 
62 
65 
66 
67 static void uv__init_global_job_handle(void) {
68  /* Create a job object and set it up to kill all contained processes when
69  * it's closed. Since this handle is made non-inheritable and we're not
70  * giving it to anyone, we're the only process holding a reference to it.
71  * That means that if this process exits it is closed and all the processes
72  * it contains are killed. All processes created with uv_spawn that are not
73  * spawned with the UV_PROCESS_DETACHED flag are assigned to this job.
74  *
75  * We're setting the JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK flag so only the
76  * processes that we explicitly add are affected, and *their* subprocesses
77  * are not. This ensures that our child processes are not limited in their
78  * ability to use job control on Windows versions that don't deal with
79  * nested jobs (prior to Windows 8 / Server 2012). It also lets our child
80  * processes created detached processes without explicitly breaking away
81  * from job control (which uv_spawn doesn't, either).
82  */
83  SECURITY_ATTRIBUTES attr;
84  JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
85 
86  memset(&attr, 0, sizeof attr);
87  attr.bInheritHandle = FALSE;
88 
89  memset(&info, 0, sizeof info);
90  info.BasicLimitInformation.LimitFlags =
95 
96  uv_global_job_handle_ = CreateJobObjectW(&attr, NULL);
98  uv_fatal_error(GetLastError(), "CreateJobObjectW");
99 
100  if (!SetInformationJobObject(uv_global_job_handle_,
101  JobObjectExtendedLimitInformation,
102  &info,
103  sizeof info))
104  uv_fatal_error(GetLastError(), "SetInformationJobObject");
105 }
106 
107 
108 static int uv_utf8_to_utf16_alloc(const char* s, WCHAR** ws_ptr) {
109  int ws_len, r;
110  WCHAR* ws;
111 
112  ws_len = MultiByteToWideChar(CP_UTF8,
113  0,
114  s,
115  -1,
116  NULL,
117  0);
118  if (ws_len <= 0) {
119  return GetLastError();
120  }
121 
122  ws = (WCHAR*) uv__malloc(ws_len * sizeof(WCHAR));
123  if (ws == NULL) {
124  return ERROR_OUTOFMEMORY;
125  }
126 
127  r = MultiByteToWideChar(CP_UTF8,
128  0,
129  s,
130  -1,
131  ws,
132  ws_len);
133  assert(r == ws_len);
134 
135  *ws_ptr = ws;
136  return 0;
137 }
138 
139 
141  uv__handle_init(loop, (uv_handle_t*) handle, UV_PROCESS);
142  handle->exit_cb = NULL;
143  handle->pid = 0;
144  handle->exit_signal = 0;
145  handle->wait_handle = INVALID_HANDLE_VALUE;
146  handle->process_handle = INVALID_HANDLE_VALUE;
147  handle->child_stdio_buffer = NULL;
148  handle->exit_cb_pending = 0;
149 
150  UV_REQ_INIT(&handle->exit_req, UV_PROCESS_EXIT);
151  handle->exit_req.data = handle;
152 }
153 
154 
155 /*
156  * Path search functions
157  */
158 
159 /*
160  * Helper function for search_path
161  */
162 static WCHAR* search_path_join_test(const WCHAR* dir,
163  size_t dir_len,
164  const WCHAR* name,
165  size_t name_len,
166  const WCHAR* ext,
167  size_t ext_len,
168  const WCHAR* cwd,
169  size_t cwd_len) {
170  WCHAR *result, *result_pos;
171  DWORD attrs;
172  if (dir_len > 2 && dir[0] == L'\\' && dir[1] == L'\\') {
173  /* It's a UNC path so ignore cwd */
174  cwd_len = 0;
175  } else if (dir_len >= 1 && (dir[0] == L'/' || dir[0] == L'\\')) {
176  /* It's a full path without drive letter, use cwd's drive letter only */
177  cwd_len = 2;
178  } else if (dir_len >= 2 && dir[1] == L':' &&
179  (dir_len < 3 || (dir[2] != L'/' && dir[2] != L'\\'))) {
180  /* It's a relative path with drive letter (ext.g. D:../some/file)
181  * Replace drive letter in dir by full cwd if it points to the same drive,
182  * otherwise use the dir only.
183  */
184  if (cwd_len < 2 || _wcsnicmp(cwd, dir, 2) != 0) {
185  cwd_len = 0;
186  } else {
187  dir += 2;
188  dir_len -= 2;
189  }
190  } else if (dir_len > 2 && dir[1] == L':') {
191  /* It's an absolute path with drive letter
192  * Don't use the cwd at all
193  */
194  cwd_len = 0;
195  }
196 
197  /* Allocate buffer for output */
198  result = result_pos = (WCHAR*)uv__malloc(sizeof(WCHAR) *
199  (cwd_len + 1 + dir_len + 1 + name_len + 1 + ext_len + 1));
200 
201  /* Copy cwd */
202  wcsncpy(result_pos, cwd, cwd_len);
203  result_pos += cwd_len;
204 
205  /* Add a path separator if cwd didn't end with one */
206  if (cwd_len && wcsrchr(L"\\/:", result_pos[-1]) == NULL) {
207  result_pos[0] = L'\\';
208  result_pos++;
209  }
210 
211  /* Copy dir */
212  wcsncpy(result_pos, dir, dir_len);
213  result_pos += dir_len;
214 
215  /* Add a separator if the dir didn't end with one */
216  if (dir_len && wcsrchr(L"\\/:", result_pos[-1]) == NULL) {
217  result_pos[0] = L'\\';
218  result_pos++;
219  }
220 
221  /* Copy filename */
222  wcsncpy(result_pos, name, name_len);
223  result_pos += name_len;
224 
225  if (ext_len) {
226  /* Add a dot if the filename didn't end with one */
227  if (name_len && result_pos[-1] != '.') {
228  result_pos[0] = L'.';
229  result_pos++;
230  }
231 
232  /* Copy extension */
233  wcsncpy(result_pos, ext, ext_len);
234  result_pos += ext_len;
235  }
236 
237  /* Null terminator */
238  result_pos[0] = L'\0';
239 
240  attrs = GetFileAttributesW(result);
241 
242  if (attrs != INVALID_FILE_ATTRIBUTES &&
243  !(attrs & FILE_ATTRIBUTE_DIRECTORY)) {
244  return result;
245  }
246 
247  uv__free(result);
248  return NULL;
249 }
250 
251 
252 /*
253  * Helper function for search_path
254  */
255 static WCHAR* path_search_walk_ext(const WCHAR *dir,
256  size_t dir_len,
257  const WCHAR *name,
258  size_t name_len,
259  WCHAR *cwd,
260  size_t cwd_len,
261  int name_has_ext) {
262  WCHAR* result;
263 
264  /* If the name itself has a nonempty extension, try this extension first */
265  if (name_has_ext) {
266  result = search_path_join_test(dir, dir_len,
267  name, name_len,
268  L"", 0,
269  cwd, cwd_len);
270  if (result != NULL) {
271  return result;
272  }
273  }
274 
275  /* Try .com extension */
276  result = search_path_join_test(dir, dir_len,
277  name, name_len,
278  L"com", 3,
279  cwd, cwd_len);
280  if (result != NULL) {
281  return result;
282  }
283 
284  /* Try .exe extension */
285  result = search_path_join_test(dir, dir_len,
286  name, name_len,
287  L"exe", 3,
288  cwd, cwd_len);
289  if (result != NULL) {
290  return result;
291  }
292 
293  return NULL;
294 }
295 
296 
297 /*
298  * search_path searches the system path for an executable filename -
299  * the windows API doesn't provide this as a standalone function nor as an
300  * option to CreateProcess.
301  *
302  * It tries to return an absolute filename.
303  *
304  * Furthermore, it tries to follow the semantics that cmd.exe, with this
305  * exception that PATHEXT environment variable isn't used. Since CreateProcess
306  * can start only .com and .exe files, only those extensions are tried. This
307  * behavior equals that of msvcrt's spawn functions.
308  *
309  * - Do not search the path if the filename already contains a path (either
310  * relative or absolute).
311  *
312  * - If there's really only a filename, check the current directory for file,
313  * then search all path directories.
314  *
315  * - If filename specified has *any* extension, search for the file with the
316  * specified extension first.
317  *
318  * - If the literal filename is not found in a directory, try *appending*
319  * (not replacing) .com first and then .exe.
320  *
321  * - The path variable may contain relative paths; relative paths are relative
322  * to the cwd.
323  *
324  * - Directories in path may or may not end with a trailing backslash.
325  *
326  * - CMD does not trim leading/trailing whitespace from path/pathex entries
327  * nor from the environment variables as a whole.
328  *
329  * - When cmd.exe cannot read a directory, it will just skip it and go on
330  * searching. However, unlike posix-y systems, it will happily try to run a
331  * file that is not readable/executable; if the spawn fails it will not
332  * continue searching.
333  *
334  * UNC path support: we are dealing with UNC paths in both the path and the
335  * filename. This is a deviation from what cmd.exe does (it does not let you
336  * start a program by specifying an UNC path on the command line) but this is
337  * really a pointless restriction.
338  *
339  */
340 static WCHAR* search_path(const WCHAR *file,
341  WCHAR *cwd,
342  const WCHAR *path) {
343  int file_has_dir;
344  WCHAR* result = NULL;
345  WCHAR *file_name_start;
346  WCHAR *dot;
347  const WCHAR *dir_start, *dir_end, *dir_path;
348  size_t dir_len;
349  int name_has_ext;
350 
351  size_t file_len = wcslen(file);
352  size_t cwd_len = wcslen(cwd);
353 
354  /* If the caller supplies an empty filename,
355  * we're not gonna return c:\windows\.exe -- GFY!
356  */
357  if (file_len == 0
358  || (file_len == 1 && file[0] == L'.')) {
359  return NULL;
360  }
361 
362  /* Find the start of the filename so we can split the directory from the
363  * name. */
364  for (file_name_start = (WCHAR*)file + file_len;
365  file_name_start > file
366  && file_name_start[-1] != L'\\'
367  && file_name_start[-1] != L'/'
368  && file_name_start[-1] != L':';
369  file_name_start--);
370 
371  file_has_dir = file_name_start != file;
372 
373  /* Check if the filename includes an extension */
374  dot = wcschr(file_name_start, L'.');
375  name_has_ext = (dot != NULL && dot[1] != L'\0');
376 
377  if (file_has_dir) {
378  /* The file has a path inside, don't use path */
379  result = path_search_walk_ext(
380  file, file_name_start - file,
381  file_name_start, file_len - (file_name_start - file),
382  cwd, cwd_len,
383  name_has_ext);
384 
385  } else {
386  dir_end = path;
387 
388  /* The file is really only a name; look in cwd first, then scan path */
389  result = path_search_walk_ext(L"", 0,
390  file, file_len,
391  cwd, cwd_len,
392  name_has_ext);
393 
394  while (result == NULL) {
395  if (*dir_end == L'\0') {
396  break;
397  }
398 
399  /* Skip the separator that dir_end now points to */
400  if (dir_end != path || *path == L';') {
401  dir_end++;
402  }
403 
404  /* Next slice starts just after where the previous one ended */
405  dir_start = dir_end;
406 
407  /* If path is quoted, find quote end */
408  if (*dir_start == L'"' || *dir_start == L'\'') {
409  dir_end = wcschr(dir_start + 1, *dir_start);
410  if (dir_end == NULL) {
411  dir_end = wcschr(dir_start, L'\0');
412  }
413  }
414  /* Slice until the next ; or \0 is found */
415  dir_end = wcschr(dir_end, L';');
416  if (dir_end == NULL) {
417  dir_end = wcschr(dir_start, L'\0');
418  }
419 
420  /* If the slice is zero-length, don't bother */
421  if (dir_end - dir_start == 0) {
422  continue;
423  }
424 
425  dir_path = dir_start;
426  dir_len = dir_end - dir_start;
427 
428  /* Adjust if the path is quoted. */
429  if (dir_path[0] == '"' || dir_path[0] == '\'') {
430  ++dir_path;
431  --dir_len;
432  }
433 
434  if (dir_path[dir_len - 1] == '"' || dir_path[dir_len - 1] == '\'') {
435  --dir_len;
436  }
437 
438  result = path_search_walk_ext(dir_path, dir_len,
439  file, file_len,
440  cwd, cwd_len,
441  name_has_ext);
442  }
443  }
444 
445  return result;
446 }
447 
448 
449 /*
450  * Quotes command line arguments
451  * Returns a pointer to the end (next char to be written) of the buffer
452  */
453 WCHAR* quote_cmd_arg(const WCHAR *source, WCHAR *target) {
454  size_t len = wcslen(source);
455  size_t i;
456  int quote_hit;
457  WCHAR* start;
458 
459  if (len == 0) {
460  /* Need double quotation for empty argument */
461  *(target++) = L'"';
462  *(target++) = L'"';
463  return target;
464  }
465 
466  if (NULL == wcspbrk(source, L" \t\"")) {
467  /* No quotation needed */
468  wcsncpy(target, source, len);
469  target += len;
470  return target;
471  }
472 
473  if (NULL == wcspbrk(source, L"\"\\")) {
474  /*
475  * No embedded double quotes or backlashes, so I can just wrap
476  * quote marks around the whole thing.
477  */
478  *(target++) = L'"';
479  wcsncpy(target, source, len);
480  target += len;
481  *(target++) = L'"';
482  return target;
483  }
484 
485  /*
486  * Expected input/output:
487  * input : hello"world
488  * output: "hello\"world"
489  * input : hello""world
490  * output: "hello\"\"world"
491  * input : hello\world
492  * output: hello\world
493  * input : hello\\world
494  * output: hello\\world
495  * input : hello\"world
496  * output: "hello\\\"world"
497  * input : hello\\"world
498  * output: "hello\\\\\"world"
499  * input : hello world\
500  * output: "hello world\\"
501  */
502 
503  *(target++) = L'"';
504  start = target;
505  quote_hit = 1;
506 
507  for (i = len; i > 0; --i) {
508  *(target++) = source[i - 1];
509 
510  if (quote_hit && source[i - 1] == L'\\') {
511  *(target++) = L'\\';
512  } else if(source[i - 1] == L'"') {
513  quote_hit = 1;
514  *(target++) = L'\\';
515  } else {
516  quote_hit = 0;
517  }
518  }
519  target[0] = L'\0';
520  wcsrev(start);
521  *(target++) = L'"';
522  return target;
523 }
524 
525 
526 int make_program_args(char** args, int verbatim_arguments, WCHAR** dst_ptr) {
527  char** arg;
528  WCHAR* dst = NULL;
529  WCHAR* temp_buffer = NULL;
530  size_t dst_len = 0;
531  size_t temp_buffer_len = 0;
532  WCHAR* pos;
533  int arg_count = 0;
534  int err = 0;
535 
536  /* Count the required size. */
537  for (arg = args; *arg; arg++) {
538  DWORD arg_len;
539 
540  arg_len = MultiByteToWideChar(CP_UTF8,
541  0,
542  *arg,
543  -1,
544  NULL,
545  0);
546  if (arg_len == 0) {
547  return GetLastError();
548  }
549 
550  dst_len += arg_len;
551 
552  if (arg_len > temp_buffer_len)
553  temp_buffer_len = arg_len;
554 
555  arg_count++;
556  }
557 
558  /* Adjust for potential quotes. Also assume the worst-case scenario that
559  * every character needs escaping, so we need twice as much space. */
560  dst_len = dst_len * 2 + arg_count * 2;
561 
562  /* Allocate buffer for the final command line. */
563  dst = (WCHAR*) uv__malloc(dst_len * sizeof(WCHAR));
564  if (dst == NULL) {
565  err = ERROR_OUTOFMEMORY;
566  goto error;
567  }
568 
569  /* Allocate temporary working buffer. */
570  temp_buffer = (WCHAR*) uv__malloc(temp_buffer_len * sizeof(WCHAR));
571  if (temp_buffer == NULL) {
572  err = ERROR_OUTOFMEMORY;
573  goto error;
574  }
575 
576  pos = dst;
577  for (arg = args; *arg; arg++) {
578  DWORD arg_len;
579 
580  /* Convert argument to wide char. */
581  arg_len = MultiByteToWideChar(CP_UTF8,
582  0,
583  *arg,
584  -1,
585  temp_buffer,
586  (int) (dst + dst_len - pos));
587  if (arg_len == 0) {
588  err = GetLastError();
589  goto error;
590  }
591 
592  if (verbatim_arguments) {
593  /* Copy verbatim. */
594  wcscpy(pos, temp_buffer);
595  pos += arg_len - 1;
596  } else {
597  /* Quote/escape, if needed. */
598  pos = quote_cmd_arg(temp_buffer, pos);
599  }
600 
601  *pos++ = *(arg + 1) ? L' ' : L'\0';
602  }
603 
604  uv__free(temp_buffer);
605 
606  *dst_ptr = dst;
607  return 0;
608 
609 error:
610  uv__free(dst);
611  uv__free(temp_buffer);
612  return err;
613 }
614 
615 
616 int env_strncmp(const wchar_t* a, int na, const wchar_t* b) {
617  wchar_t* a_eq;
618  wchar_t* b_eq;
619  wchar_t* A;
620  wchar_t* B;
621  int nb;
622  int r;
623 
624  if (na < 0) {
625  a_eq = wcschr(a, L'=');
626  assert(a_eq);
627  na = (int)(long)(a_eq - a);
628  } else {
629  na--;
630  }
631  b_eq = wcschr(b, L'=');
632  assert(b_eq);
633  nb = b_eq - b;
634 
635  A = alloca((na+1) * sizeof(wchar_t));
636  B = alloca((nb+1) * sizeof(wchar_t));
637 
638  r = LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, a, na, A, na);
639  assert(r==na);
640  A[na] = L'\0';
641  r = LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, b, nb, B, nb);
642  assert(r==nb);
643  B[nb] = L'\0';
644 
645  while (1) {
646  wchar_t AA = *A++;
647  wchar_t BB = *B++;
648  if (AA < BB) {
649  return -1;
650  } else if (AA > BB) {
651  return 1;
652  } else if (!AA && !BB) {
653  return 0;
654  }
655  }
656 }
657 
658 
659 static int qsort_wcscmp(const void *a, const void *b) {
660  wchar_t* astr = *(wchar_t* const*)a;
661  wchar_t* bstr = *(wchar_t* const*)b;
662  return env_strncmp(astr, -1, bstr);
663 }
664 
665 
666 /*
667  * The way windows takes environment variables is different than what C does;
668  * Windows wants a contiguous block of null-terminated strings, terminated
669  * with an additional null.
670  *
671  * Windows has a few "essential" environment variables. winsock will fail
672  * to initialize if SYSTEMROOT is not defined; some APIs make reference to
673  * TEMP. SYSTEMDRIVE is probably also important. We therefore ensure that
674  * these get defined if the input environment block does not contain any
675  * values for them.
676  *
677  * Also add variables known to Cygwin to be required for correct
678  * subprocess operation in many cases:
679  * https://github.com/Alexpux/Cygwin/blob/b266b04fbbd3a595f02ea149e4306d3ab9b1fe3d/winsup/cygwin/environ.cc#L955
680  *
681  */
682 int make_program_env(char* env_block[], WCHAR** dst_ptr) {
683  WCHAR* dst;
684  WCHAR* ptr;
685  char** env;
686  size_t env_len = 0;
687  int len;
688  size_t i;
689  DWORD var_size;
690  size_t env_block_count = 1; /* 1 for null-terminator */
691  WCHAR* dst_copy;
692  WCHAR** ptr_copy;
693  WCHAR** env_copy;
694  DWORD required_vars_value_len[ARRAY_SIZE(required_vars)];
695 
696  /* first pass: determine size in UTF-16 */
697  for (env = env_block; *env; env++) {
698  int len;
699  if (strchr(*env, '=')) {
700  len = MultiByteToWideChar(CP_UTF8,
701  0,
702  *env,
703  -1,
704  NULL,
705  0);
706  if (len <= 0) {
707  return GetLastError();
708  }
709  env_len += len;
710  env_block_count++;
711  }
712  }
713 
714  /* second pass: copy to UTF-16 environment block */
715  dst_copy = (WCHAR*)uv__malloc(env_len * sizeof(WCHAR));
716  if (dst_copy == NULL && env_len > 0) {
717  return ERROR_OUTOFMEMORY;
718  }
719  env_copy = alloca(env_block_count * sizeof(WCHAR*));
720 
721  ptr = dst_copy;
722  ptr_copy = env_copy;
723  for (env = env_block; *env; env++) {
724  if (strchr(*env, '=')) {
725  len = MultiByteToWideChar(CP_UTF8,
726  0,
727  *env,
728  -1,
729  ptr,
730  (int) (env_len - (ptr - dst_copy)));
731  if (len <= 0) {
732  DWORD err = GetLastError();
733  uv__free(dst_copy);
734  return err;
735  }
736  *ptr_copy++ = ptr;
737  ptr += len;
738  }
739  }
740  *ptr_copy = NULL;
741  assert(env_len == 0 || env_len == (size_t) (ptr - dst_copy));
742 
743  /* sort our (UTF-16) copy */
744  qsort(env_copy, env_block_count-1, sizeof(wchar_t*), qsort_wcscmp);
745 
746  /* third pass: check for required variables */
747  for (ptr_copy = env_copy, i = 0; i < ARRAY_SIZE(required_vars); ) {
748  int cmp;
749  if (!*ptr_copy) {
750  cmp = -1;
751  } else {
752  cmp = env_strncmp(required_vars[i].wide_eq,
754  *ptr_copy);
755  }
756  if (cmp < 0) {
757  /* missing required var */
758  var_size = GetEnvironmentVariableW(required_vars[i].wide, NULL, 0);
759  required_vars_value_len[i] = var_size;
760  if (var_size != 0) {
761  env_len += required_vars[i].len;
762  env_len += var_size;
763  }
764  i++;
765  } else {
766  ptr_copy++;
767  if (cmp == 0)
768  i++;
769  }
770  }
771 
772  /* final pass: copy, in sort order, and inserting required variables */
773  dst = uv__malloc((1+env_len) * sizeof(WCHAR));
774  if (!dst) {
775  uv__free(dst_copy);
776  return ERROR_OUTOFMEMORY;
777  }
778 
779  for (ptr = dst, ptr_copy = env_copy, i = 0;
780  *ptr_copy || i < ARRAY_SIZE(required_vars);
781  ptr += len) {
782  int cmp;
783  if (i >= ARRAY_SIZE(required_vars)) {
784  cmp = 1;
785  } else if (!*ptr_copy) {
786  cmp = -1;
787  } else {
788  cmp = env_strncmp(required_vars[i].wide_eq,
790  *ptr_copy);
791  }
792  if (cmp < 0) {
793  /* missing required var */
794  len = required_vars_value_len[i];
795  if (len) {
796  wcscpy(ptr, required_vars[i].wide_eq);
797  ptr += required_vars[i].len;
798  var_size = GetEnvironmentVariableW(required_vars[i].wide,
799  ptr,
800  (int) (env_len - (ptr - dst)));
801  if (var_size != (DWORD) (len - 1)) { /* TODO: handle race condition? */
802  uv_fatal_error(GetLastError(), "GetEnvironmentVariableW");
803  }
804  }
805  i++;
806  } else {
807  /* copy var from env_block */
808  len = wcslen(*ptr_copy) + 1;
809  wmemcpy(ptr, *ptr_copy, len);
810  ptr_copy++;
811  if (cmp == 0)
812  i++;
813  }
814  }
815 
816  /* Terminate with an extra NULL. */
817  assert(env_len == (size_t) (ptr - dst));
818  *ptr = L'\0';
819 
820  uv__free(dst_copy);
821  *dst_ptr = dst;
822  return 0;
823 }
824 
825 /*
826  * Attempt to find the value of the PATH environment variable in the child's
827  * preprocessed environment.
828  *
829  * If found, a pointer into `env` is returned. If not found, NULL is returned.
830  */
831 static WCHAR* find_path(WCHAR *env) {
832  for (; env != NULL && *env != 0; env += wcslen(env) + 1) {
833  if ((env[0] == L'P' || env[0] == L'p') &&
834  (env[1] == L'A' || env[1] == L'a') &&
835  (env[2] == L'T' || env[2] == L't') &&
836  (env[3] == L'H' || env[3] == L'h') &&
837  (env[4] == L'=')) {
838  return &env[5];
839  }
840  }
841 
842  return NULL;
843 }
844 
845 /*
846  * Called on Windows thread-pool thread to indicate that
847  * a child process has exited.
848  */
849 static void CALLBACK exit_wait_callback(void* data, BOOLEAN didTimeout) {
850  uv_process_t* process = (uv_process_t*) data;
851  uv_loop_t* loop = process->loop;
852 
853  assert(didTimeout == FALSE);
854  assert(process);
855  assert(!process->exit_cb_pending);
856 
857  process->exit_cb_pending = 1;
858 
859  /* Post completed */
860  POST_COMPLETION_FOR_REQ(loop, &process->exit_req);
861 }
862 
863 
864 /* Called on main thread after a child process has exited. */
866  int64_t exit_code;
867  DWORD status;
868 
869  assert(handle->exit_cb_pending);
870  handle->exit_cb_pending = 0;
871 
872  /* If we're closing, don't call the exit callback. Just schedule a close
873  * callback now. */
874  if (handle->flags & UV_HANDLE_CLOSING) {
876  return;
877  }
878 
879  /* Unregister from process notification. */
880  if (handle->wait_handle != INVALID_HANDLE_VALUE) {
881  UnregisterWait(handle->wait_handle);
882  handle->wait_handle = INVALID_HANDLE_VALUE;
883  }
884 
885  /* Set the handle to inactive: no callbacks will be made after the exit
886  * callback. */
888 
889  if (GetExitCodeProcess(handle->process_handle, &status)) {
890  exit_code = status;
891  } else {
892  /* Unable to obtain the exit code. This should never happen. */
893  exit_code = uv_translate_sys_error(GetLastError());
894  }
895 
896  /* Fire the exit callback. */
897  if (handle->exit_cb) {
898  handle->exit_cb(handle, exit_code, handle->exit_signal);
899  }
900 }
901 
902 
905 
906  if (handle->wait_handle != INVALID_HANDLE_VALUE) {
907  /* This blocks until either the wait was cancelled, or the callback has
908  * completed. */
909  BOOL r = UnregisterWaitEx(handle->wait_handle, INVALID_HANDLE_VALUE);
910  if (!r) {
911  /* This should never happen, and if it happens, we can't recover... */
912  uv_fatal_error(GetLastError(), "UnregisterWaitEx");
913  }
914 
915  handle->wait_handle = INVALID_HANDLE_VALUE;
916  }
917 
918  if (!handle->exit_cb_pending) {
920  }
921 }
922 
923 
925  assert(!handle->exit_cb_pending);
926  assert(handle->flags & UV_HANDLE_CLOSING);
927  assert(!(handle->flags & UV_HANDLE_CLOSED));
928 
929  /* Clean-up the process handle. */
930  CloseHandle(handle->process_handle);
931 
933 }
934 
935 
938  const uv_process_options_t* options) {
939  int i;
940  int err = 0;
941  WCHAR* path = NULL, *alloc_path = NULL;
942  BOOL result;
943  WCHAR* application_path = NULL, *application = NULL, *arguments = NULL,
944  *env = NULL, *cwd = NULL;
945  STARTUPINFOW startup;
946  PROCESS_INFORMATION info;
947  DWORD process_flags;
948 
950  process->exit_cb = options->exit_cb;
951 
952  if (options->flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) {
953  return UV_ENOTSUP;
954  }
955 
956  if (options->file == NULL ||
957  options->args == NULL) {
958  return UV_EINVAL;
959  }
960 
961  assert(options->file != NULL);
962  assert(!(options->flags & ~(UV_PROCESS_DETACHED |
969 
970  err = uv_utf8_to_utf16_alloc(options->file, &application);
971  if (err)
972  goto done;
973 
975  options->args,
977  &arguments);
978  if (err)
979  goto done;
980 
981  if (options->env) {
982  err = make_program_env(options->env, &env);
983  if (err)
984  goto done;
985  }
986 
987  if (options->cwd) {
988  /* Explicit cwd */
990  if (err)
991  goto done;
992 
993  } else {
994  /* Inherit cwd */
995  DWORD cwd_len, r;
996 
997  cwd_len = GetCurrentDirectoryW(0, NULL);
998  if (!cwd_len) {
999  err = GetLastError();
1000  goto done;
1001  }
1002 
1003  cwd = (WCHAR*) uv__malloc(cwd_len * sizeof(WCHAR));
1004  if (cwd == NULL) {
1005  err = ERROR_OUTOFMEMORY;
1006  goto done;
1007  }
1008 
1009  r = GetCurrentDirectoryW(cwd_len, cwd);
1010  if (r == 0 || r >= cwd_len) {
1011  err = GetLastError();
1012  goto done;
1013  }
1014  }
1015 
1016  /* Get PATH environment variable. */
1017  path = find_path(env);
1018  if (path == NULL) {
1019  DWORD path_len, r;
1020 
1021  path_len = GetEnvironmentVariableW(L"PATH", NULL, 0);
1022  if (path_len == 0) {
1023  err = GetLastError();
1024  goto done;
1025  }
1026 
1027  alloc_path = (WCHAR*) uv__malloc(path_len * sizeof(WCHAR));
1028  if (alloc_path == NULL) {
1029  err = ERROR_OUTOFMEMORY;
1030  goto done;
1031  }
1032  path = alloc_path;
1033 
1034  r = GetEnvironmentVariableW(L"PATH", path, path_len);
1035  if (r == 0 || r >= path_len) {
1036  err = GetLastError();
1037  goto done;
1038  }
1039  }
1040 
1041  err = uv__stdio_create(loop, options, &process->child_stdio_buffer);
1042  if (err)
1043  goto done;
1044 
1045  application_path = search_path(application,
1046  cwd,
1047  path);
1048  if (application_path == NULL) {
1049  /* Not found. */
1050  err = ERROR_FILE_NOT_FOUND;
1051  goto done;
1052  }
1053 
1054  startup.cb = sizeof(startup);
1055  startup.lpReserved = NULL;
1056  startup.lpDesktop = NULL;
1057  startup.lpTitle = NULL;
1058  startup.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
1059 
1060  startup.cbReserved2 = uv__stdio_size(process->child_stdio_buffer);
1061  startup.lpReserved2 = (BYTE*) process->child_stdio_buffer;
1062 
1063  startup.hStdInput = uv__stdio_handle(process->child_stdio_buffer, 0);
1064  startup.hStdOutput = uv__stdio_handle(process->child_stdio_buffer, 1);
1065  startup.hStdError = uv__stdio_handle(process->child_stdio_buffer, 2);
1066 
1067  process_flags = CREATE_UNICODE_ENVIRONMENT;
1068 
1069  if ((options->flags & UV_PROCESS_WINDOWS_HIDE_CONSOLE) ||
1070  (options->flags & UV_PROCESS_WINDOWS_HIDE)) {
1071  /* Avoid creating console window if stdio is not inherited. */
1072  for (i = 0; i < options->stdio_count; i++) {
1073  if (options->stdio[i].flags & UV_INHERIT_FD)
1074  break;
1075  if (i == options->stdio_count - 1)
1076  process_flags |= CREATE_NO_WINDOW;
1077  }
1078  }
1079  if ((options->flags & UV_PROCESS_WINDOWS_HIDE_GUI) ||
1080  (options->flags & UV_PROCESS_WINDOWS_HIDE)) {
1081  /* Use SW_HIDE to avoid any potential process window. */
1082  startup.wShowWindow = SW_HIDE;
1083  } else {
1084  startup.wShowWindow = SW_SHOWDEFAULT;
1085  }
1086 
1087  if (options->flags & UV_PROCESS_DETACHED) {
1088  /* Note that we're not setting the CREATE_BREAKAWAY_FROM_JOB flag. That
1089  * means that libuv might not let you create a fully daemonized process
1090  * when run under job control. However the type of job control that libuv
1091  * itself creates doesn't trickle down to subprocesses so they can still
1092  * daemonize.
1093  *
1094  * A reason to not do this is that CREATE_BREAKAWAY_FROM_JOB makes the
1095  * CreateProcess call fail if we're under job control that doesn't allow
1096  * breakaway.
1097  */
1098  process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP;
1099  }
1100 
1101  if (!CreateProcessW(application_path,
1102  arguments,
1103  NULL,
1104  NULL,
1105  1,
1106  process_flags,
1107  env,
1108  cwd,
1109  &startup,
1110  &info)) {
1111  /* CreateProcessW failed. */
1112  err = GetLastError();
1113  goto done;
1114  }
1115 
1116  /* Spawn succeeded. Beyond this point, failure is reported asynchronously. */
1117 
1118  process->process_handle = info.hProcess;
1119  process->pid = info.dwProcessId;
1120 
1121  /* If the process isn't spawned as detached, assign to the global job object
1122  * so windows will kill it when the parent process dies. */
1123  if (!(options->flags & UV_PROCESS_DETACHED)) {
1125 
1126  if (!AssignProcessToJobObject(uv_global_job_handle_, info.hProcess)) {
1127  /* AssignProcessToJobObject might fail if this process is under job
1128  * control and the job doesn't have the
1129  * JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK flag set, on a Windows version
1130  * that doesn't support nested jobs.
1131  *
1132  * When that happens we just swallow the error and continue without
1133  * establishing a kill-child-on-parent-exit relationship, otherwise
1134  * there would be no way for libuv applications run under job control
1135  * to spawn processes at all.
1136  */
1137  DWORD err = GetLastError();
1138  if (err != ERROR_ACCESS_DENIED)
1139  uv_fatal_error(err, "AssignProcessToJobObject");
1140  }
1141  }
1142 
1143  /* Set IPC pid to all IPC pipes. */
1144  for (i = 0; i < options->stdio_count; i++) {
1145  const uv_stdio_container_t* fdopt = &options->stdio[i];
1146  if (fdopt->flags & UV_CREATE_PIPE &&
1147  fdopt->data.stream->type == UV_NAMED_PIPE &&
1148  ((uv_pipe_t*) fdopt->data.stream)->ipc) {
1149  ((uv_pipe_t*) fdopt->data.stream)->pipe.conn.ipc_remote_pid =
1150  info.dwProcessId;
1151  }
1152  }
1153 
1154  /* Setup notifications for when the child process exits. */
1155  result = RegisterWaitForSingleObject(&process->wait_handle,
1156  process->process_handle, exit_wait_callback, (void*)process, INFINITE,
1157  WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE);
1158  if (!result) {
1159  uv_fatal_error(GetLastError(), "RegisterWaitForSingleObject");
1160  }
1161 
1162  CloseHandle(info.hThread);
1163 
1164  assert(!err);
1165 
1166  /* Make the handle active. It will remain active until the exit callback is
1167  * made or the handle is closed, whichever happens first. */
1169 
1170  /* Cleanup, whether we succeeded or failed. */
1171  done:
1172  uv__free(application);
1173  uv__free(application_path);
1174  uv__free(arguments);
1175  uv__free(cwd);
1176  uv__free(env);
1177  uv__free(alloc_path);
1178 
1179  if (process->child_stdio_buffer != NULL) {
1180  /* Clean up child stdio handles. */
1181  uv__stdio_destroy(process->child_stdio_buffer);
1182  process->child_stdio_buffer = NULL;
1183  }
1184 
1185  return uv_translate_sys_error(err);
1186 }
1187 
1188 
1189 static int uv__kill(HANDLE process_handle, int signum) {
1190  if (signum < 0 || signum >= NSIG) {
1191  return UV_EINVAL;
1192  }
1193 
1194  switch (signum) {
1195  case SIGTERM:
1196  case SIGKILL:
1197  case SIGINT: {
1198  /* Unconditionally terminate the process. On Windows, killed processes
1199  * normally return 1. */
1200  DWORD status;
1201  int err;
1202 
1203  if (TerminateProcess(process_handle, 1))
1204  return 0;
1205 
1206  /* If the process already exited before TerminateProcess was called,.
1207  * TerminateProcess will fail with ERROR_ACCESS_DENIED. */
1208  err = GetLastError();
1209  if (err == ERROR_ACCESS_DENIED &&
1210  GetExitCodeProcess(process_handle, &status) &&
1211  status != STILL_ACTIVE) {
1212  return UV_ESRCH;
1213  }
1214 
1215  return uv_translate_sys_error(err);
1216  }
1217 
1218  case 0: {
1219  /* Health check: is the process still alive? */
1220  DWORD status;
1221 
1222  if (!GetExitCodeProcess(process_handle, &status))
1223  return uv_translate_sys_error(GetLastError());
1224 
1225  if (status != STILL_ACTIVE)
1226  return UV_ESRCH;
1227 
1228  return 0;
1229  }
1230 
1231  default:
1232  /* Unsupported signal. */
1233  return UV_ENOSYS;
1234  }
1235 }
1236 
1237 
1239  int err;
1240 
1241  if (process->process_handle == INVALID_HANDLE_VALUE) {
1242  return UV_EINVAL;
1243  }
1244 
1245  err = uv__kill(process->process_handle, signum);
1246  if (err) {
1247  return err; /* err is already translated. */
1248  }
1249 
1250  process->exit_signal = signum;
1251 
1252  return 0;
1253 }
1254 
1255 
1256 int uv_kill(int pid, int signum) {
1257  int err;
1258  HANDLE process_handle;
1259 
1260  if (pid == 0) {
1261  process_handle = GetCurrentProcess();
1262  } else {
1263  process_handle = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION,
1264  FALSE,
1265  pid);
1266  }
1267 
1268  if (process_handle == NULL) {
1269  err = GetLastError();
1270  if (err == ERROR_INVALID_PARAMETER) {
1271  return UV_ESRCH;
1272  } else {
1273  return uv_translate_sys_error(err);
1274  }
1275  }
1276 
1277  err = uv__kill(process_handle, signum);
1278  CloseHandle(process_handle);
1279 
1280  return err; /* err is already translated. */
1281 }
size_t len
Definition: 6502dis.c:15
#define ARRAY_SIZE(a)
lzma_index ** i
Definition: index.h:629
static const char ext[]
Definition: apprentice.c:1981
#define A(x)
Definition: arc.h:165
#define B(x)
Definition: arc.h:166
static const char * arg(RzAnalysis *a, csh *handle, cs_insn *insn, char *buf, int n)
Definition: arm_esil32.c:136
static RzILOpEffect * cmp(cs_insn *insn, bool is_thumb)
Definition: arm_il32.c:942
static bool err
Definition: armass.c:435
static mcore_handle handle
Definition: asm_mcore.c:8
RzBinInfo * info(RzBinFile *bf)
Definition: bin_ne.c:86
char * alloca()
#define NULL
Definition: cris-opc.c:27
#define r
Definition: crypto_rc6.c:12
static static fork const void static count static fd const char const char static newpath const char static path const char path
Definition: sflib.h:35
static static sync static getppid static getegid const char static filename char static len const char char static bufsiz static mask static vfork const void static prot static getpgrp const char static swapflags static arg static fd static protocol static who struct sockaddr static addrlen static backlog struct timeval struct timezone static tz const struct iovec static count static mode const void const struct sockaddr static tolen const char static pathname void static offset struct stat static buf void long static basep static whence static length const void static len static semflg const void static shmflg const struct timespec struct timespec static rem const char static group const void start
Definition: sflib.h:133
struct tab * done
Definition: enough.c:233
void uv_fatal_error(const int errorno, const char *syscall)
Definition: error.c:35
static INLINE void uv_want_endgame(uv_loop_t *loop, uv_handle_t *handle)
Definition: handle-inl.h:88
#define uv__handle_close(handle)
Definition: handle-inl.h:76
#define uv__handle_closing(handle)
Definition: handle-inl.h:63
#define INVALID_HANDLE_VALUE
Definition: iowin32.c:21
return memset(p, 0, total)
static static fork const void static count static fd const char const char static newpath char char char static envp time_t static t const char static mode static whence const char static dir time_t static t unsigned static seconds const char struct utimbuf static buf static inc pid
Definition: sflib.h:64
static const char struct stat static buf struct stat static buf static vhangup int options
Definition: sflib.h:145
static const char struct stat static buf struct stat static buf static vhangup int status
Definition: sflib.h:145
static static fork const void static count static fd const char static mode const char static pathname const char static path const char static dev const char static group static getpid static getuid void void static data static pause const char static mode static sync const char const char static newpath const char static pathname unsigned long static filedes void static end_data_segment signum
Definition: sflib.h:79
unsigned char BYTE
Definition: lz4.c:286
const char * source
Definition: lz4.h:699
char * dst
Definition: lz4.h:724
assert(limit<=UINT32_MAX/2)
int args
Definition: mipsasm.c:18
#define FALSE
Definition: mybfd.h:102
void qsort(void *a, size_t n, size_t es, int(*cmp)(const void *, const void *))
Definition: qsort.h:130
#define POST_COMPLETION_FOR_REQ(loop, req)
Definition: req-inl.h:76
static RzSocket * s
Definition: rtr.c:28
static int
Definition: sfsocketcall.h:114
long int64_t
Definition: sftypes.h:32
#define b(i)
Definition: sha256.c:42
#define a(i)
Definition: sha256.c:41
const size_t len
Definition: process.c:43
const WCHAR *const wide_eq
Definition: process.c:42
const WCHAR *const wide
Definition: process.c:41
Definition: gzappend.c:170
Definition: z80asm.h:102
Definition: uv.h:1780
Definition: uv.h:767
uv_stream_t * stream
Definition: uv.h:946
uv_stdio_flags flags
Definition: uv.h:943
union uv_stdio_container_s::@397 data
uv_loop_t * loop
Definition: main.c:7
int pos
Definition: main.c:11
void uv__stdio_destroy(BYTE *buffer)
WORD uv__stdio_size(BYTE *buffer)
int uv__stdio_create(uv_loop_t *loop, const uv_process_options_t *options, BYTE **buffer_ptr)
HANDLE uv__stdio_handle(BYTE *buffer, int fd)
static char ** env
Definition: sys.c:32
int uv_spawn(uv_loop_t *loop, uv_process_t *process, const uv_process_options_t *options)
Definition: process.c:408
int uv_kill(int pid, int signum)
Definition: process.c:582
int uv_process_kill(uv_process_t *process, int signum)
Definition: process.c:577
pthread_once_t uv_once_t
Definition: unix.h:135
#define UV_ONCE_INIT
Definition: unix.h:133
void error(const char *msg)
Definition: untgz.c:593
void * uv__malloc(size_t size)
Definition: uv-common.c:75
void uv__free(void *ptr)
Definition: uv-common.c:81
@ UV_HANDLE_CLOSING
Definition: uv-common.h:74
@ UV_HANDLE_CLOSED
Definition: uv-common.h:75
#define UV_REQ_INIT(req, typ)
Definition: uv-common.h:322
#define uv__handle_init(loop_, h, type_)
Definition: uv-common.h:301
#define uv__handle_stop(h)
Definition: uv-common.h:266
#define uv__handle_start(h)
Definition: uv-common.h:258
@ UV_INHERIT_FD
Definition: uv.h:924
@ UV_CREATE_PIPE
Definition: uv.h:923
@ UV_PROCESS_WINDOWS_HIDE_CONSOLE
Definition: uv.h:1036
@ UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS
Definition: uv.h:1017
@ UV_PROCESS_WINDOWS_HIDE
Definition: uv.h:1030
@ UV_PROCESS_SETUID
Definition: uv.h:1005
@ UV_PROCESS_SETGID
Definition: uv.h:1011
@ UV_PROCESS_DETACHED
Definition: uv.h:1025
@ UV_PROCESS_WINDOWS_HIDE_GUI
Definition: uv.h:1042
UV_EXTERN int uv_translate_sys_error(int sys_errno)
Definition: core.c:1249
UV_EXTERN void uv_once(uv_once_t *guard, void(*callback)(void))
Definition: thread.c:419
static int qsort_wcscmp(const void *a, const void *b)
Definition: process.c:659
static void uv__init_global_job_handle(void)
Definition: process.c:67
WCHAR * quote_cmd_arg(const WCHAR *source, WCHAR *target)
Definition: process.c:453
void uv_process_close(uv_loop_t *loop, uv_process_t *handle)
Definition: process.c:903
static int uv_utf8_to_utf16_alloc(const char *s, WCHAR **ws_ptr)
Definition: process.c:108
static void uv_process_init(uv_loop_t *loop, uv_process_t *handle)
Definition: process.c:140
static WCHAR * search_path(const WCHAR *file, WCHAR *cwd, const WCHAR *path)
Definition: process.c:340
int env_strncmp(const wchar_t *a, int na, const wchar_t *b)
Definition: process.c:616
static uv_once_t uv_global_job_handle_init_guard_
Definition: process.c:64
static WCHAR * find_path(WCHAR *env)
Definition: process.c:831
static const env_var_t required_vars[]
Definition: process.c:48
static WCHAR * path_search_walk_ext(const WCHAR *dir, size_t dir_len, const WCHAR *name, size_t name_len, WCHAR *cwd, size_t cwd_len, int name_has_ext)
Definition: process.c:255
struct env_var env_var_t
static HANDLE uv_global_job_handle_
Definition: process.c:63
static WCHAR * search_path_join_test(const WCHAR *dir, size_t dir_len, const WCHAR *name, size_t name_len, const WCHAR *ext, size_t ext_len, const WCHAR *cwd, size_t cwd_len)
Definition: process.c:162
void uv_process_endgame(uv_loop_t *loop, uv_process_t *handle)
Definition: process.c:924
static void CALLBACK exit_wait_callback(void *data, BOOLEAN didTimeout)
Definition: process.c:849
void uv_process_proc_exit(uv_loop_t *loop, uv_process_t *handle)
Definition: process.c:865
#define E_V(str)
Definition: process.c:46
int make_program_args(char **args, int verbatim_arguments, WCHAR **dst_ptr)
Definition: process.c:526
#define SIGKILL
Definition: process.c:37
static int uv__kill(HANDLE process_handle, int signum)
Definition: process.c:1189
int make_program_env(char *env_block[], WCHAR **dst_ptr)
Definition: process.c:682
#define LOCALE_INVARIANT
Definition: win.h:44
#define NSIG
Definition: win.h:95
#define JOB_OBJECT_LIMIT_BREAKAWAY_OK
Definition: winapi.h:4098
#define JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
Definition: winapi.h:4104
#define JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION
Definition: winapi.h:4095
#define JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK
Definition: winapi.h:4101
DWORD * HANDLE
DWORD
static int file
Definition: z80asm.c:58
static zip_compression_status_t process(void *ud, zip_uint8_t *data, zip_uint64_t *length)
#define L
Definition: zip_err_str.c:7