GRASS GIS 8 Programmer's Manual  8.2.2dev(2023)-3d2c704037
spawn.c
Go to the documentation of this file.
1 
2 /*!
3  * \file lib/gis/spawn.c
4  *
5  * \brief GIS Library - Handles process spawning.
6  *
7  * (C) 2001-2014 by the GRASS Development Team
8  *
9  * This program is free software under the GNU General Public License
10  * (>=v2). Read the file COPYING that comes with GRASS for details.
11  *
12  * \author Glynn Clements
13  *
14  * \date 2004-2006
15  */
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <signal.h>
21 #include <stdarg.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <errno.h>
25 #include <sys/types.h>
26 
27 #ifndef __MINGW32__
28 #include <sys/wait.h>
29 #else
30 #include <windows.h>
31 #endif
32 #include <grass/config.h>
33 #include <grass/gis.h>
34 #include <grass/glocale.h>
35 #include <grass/spawn.h>
36 
37 /** \def MAX_ARGS Maximum number of arguments */
38 
39 /** \def MAX_BINDINGS Maximum number of bindings */
40 
41 /** \def MAX_SIGNALS Maximum number of signals */
42 
43 /** \def MAX_REDIRECTS Maximum number of redirects */
44 #define MAX_ARGS 256
45 #define MAX_BINDINGS 256
46 #define MAX_SIGNALS 32
47 #define MAX_REDIRECTS 32
48 
49 
50 /**
51  * \brief Spawns a new process.
52  *
53  * A more useful alternative to G_system(), which takes the
54  * arguments of <b>command</b> as parameters.
55  *
56  * \param[in] command command to execute
57  * \return -1 on error
58  * \return process status on success
59  */
60 
61 struct redirect
62 {
63  int dst_fd;
64  int src_fd;
65  const char *file;
66  int mode;
67 };
68 
69 struct signal
70 {
71  int which;
72  int action;
73  int signum;
74  int valid;
75 #ifndef __MINGW32__
76  struct sigaction old_act;
77  sigset_t old_mask;
78 #endif
79 };
80 
81 struct binding
82 {
83  const char *var;
84  const char *val;
85 };
86 
87 struct spawn
88 {
89  const char *args[MAX_ARGS];
90  int num_args;
91  struct redirect redirects[MAX_REDIRECTS];
92  int num_redirects;
93  struct signal signals[MAX_SIGNALS];
94  int num_signals;
95  struct binding bindings[MAX_BINDINGS];
96  int num_bindings;
97  int background;
98  const char *directory;
99 };
100 
101 static void parse_arglist(struct spawn *sp, va_list va);
102 static void parse_argvec(struct spawn *sp, const char **va);
103 
104 #ifdef __MINGW32__
105 
106 struct buffer {
107  char *str;
108  size_t len;
109  size_t size;
110 };
111 
112 static const int INCREMENT = 50;
113 
114 static void clear(struct buffer *b)
115 {
116  b->len = 0;
117  b->str[b->len] = '\0';
118 }
119 
120 static void init(struct buffer *b)
121 {
122  b->str = G_malloc(1);
123  b->size = 1;
124  clear(b);
125 }
126 
127 static char *release(struct buffer *b)
128 {
129  char *p = b->str;
130 
131  b->str = NULL;
132  b->size = 0;
133  b->len = 0;
134 
135  return p;
136 }
137 
138 static void finish(struct buffer *b)
139 {
140  if (b->str)
141  G_free(b->str);
142  release(b);
143 }
144 
145 static void ensure(struct buffer *b, size_t n)
146 {
147  if (b->size <= b->len + n + 1) {
148  b->size = b->len + n + INCREMENT;
149  b->str = G_realloc(b->str, b->size);
150  }
151 }
152 
153 static void append(struct buffer *b, const char *str)
154 {
155  size_t n = strlen(str);
156 
157  ensure(b, n);
158  memcpy(&b->str[b->len], str, n);
159  b->len += n;
160  b->str[b->len] = '\0';
161 }
162 
163 static void append_char(struct buffer *b, char c)
164 {
165  ensure(b, 1);
166  b->str[b->len] = c;
167  b->len++;
168  b->str[b->len] = '\0';
169 }
170 
171 static void escape_arg(struct buffer *result, const char *arg)
172 {
173  struct buffer buf;
174  int quote, j;
175 
176  init(&buf);
177 
178  quote = arg[0] == '\0' || strchr(arg, ' ') || strchr(arg, '\t');
179 
180  if (quote)
181  append_char(result, '\"');
182 
183  for (j = 0; arg[j]; j++) {
184  int c = arg[j];
185  int k;
186 
187  switch (c) {
188  case '\\':
189  append_char(&buf, '\\');
190  break;
191  case '\"':
192  for (k = 0; k < buf.len; k++)
193  append(result, "\\\\");
194  clear(&buf);
195  append(result, "\\\"");
196  break;
197  default:
198  if (buf.len > 0) {
199  append(result, buf.str);
200  clear(&buf);
201  }
202  append_char(result, c);
203  }
204  }
205 
206  if (buf.len > 0)
207  append(result, buf.str);
208 
209  if (quote) {
210  append(result, buf.str);
211  append_char(result, '\"');
212  }
213 
214  finish(&buf);
215 }
216 
217 static char *check_program(const char *pgm, const char *dir, const char *ext)
218 {
219  char pathname[GPATH_MAX];
220 
221  sprintf(pathname, "%s%s%s%s", dir, *dir ? "\\" : "", pgm, ext);
222  return access(pathname, 0) == 0
223  ? G_store(pathname)
224  : NULL;
225 }
226 
227 static char *find_program_ext(const char *pgm, const char *dir, char **pathext)
228 {
229  char *result;
230  int i;
231 
232  if (result = check_program(pgm, dir, ""), result)
233  return result;
234 
235  for (i = 0; pathext[i]; i++) {
236  const char *ext = pathext[i];
237  if (result = check_program(pgm, dir, ext), result)
238  return result;
239  }
240 
241  return NULL;
242 }
243 
244 static char *find_program_dir_ext(const char *pgm, char **path, char **pathext)
245 {
246  char *result = NULL;
247  int i;
248 
249  if (strchr(pgm, '\\') || strchr(pgm, '/')) {
250  if (result = find_program_ext(pgm, "", pathext), result)
251  return result;
252  }
253  else {
254  if (result = find_program_ext(pgm, ".", pathext), result)
255  return result;
256 
257  for (i = 0; path[i]; i++) {
258  const char *dir = path[i];
259  if (result = find_program_ext(pgm, dir, pathext), result)
260  return result;
261  }
262  }
263 
264  return NULL;
265 }
266 
267 static char *find_program(const char *pgm)
268 {
269  char **path = G_tokenize(getenv("PATH"), ";");
270  char **pathext = G_tokenize(getenv("PATHEXT"), ";");
271  char *result = find_program_dir_ext(pgm, path, pathext);
272  G_free_tokens(path);
273  G_free_tokens(pathext);
274  return result;
275 }
276 
277 static char *make_command_line(int shell, const char *cmd, const char **argv)
278 {
279  struct buffer result;
280  int i;
281 
282  init(&result);
283 
284  if (shell) {
285  const char *comspec = getenv("COMSPEC");
286  append(&result, comspec ? comspec : "cmd.exe");
287  append(&result, " /c \"");
288  escape_arg(&result, cmd);
289  }
290 
291  for (i = shell ? 1 : 0; argv[i]; i++) {
292  if (result.len > 0)
293  append_char(&result, ' ');
294  escape_arg(&result, argv[i]);
295  }
296 
297  append(&result, "\"");
298 
299  return release(&result);
300 }
301 
302 static char *make_environment(const char **envp)
303 {
304  struct buffer result;
305  int i;
306 
307  init(&result);
308 
309  for (i = 0; envp[i]; i++) {
310  const char *env = envp[i];
311 
312  append(&result, env);
313  append_char(&result, '\0');
314  }
315 
316  return release(&result);
317 }
318 
319 static HANDLE get_handle(int fd)
320 {
321  HANDLE h1, h2;
322 
323  if (fd < 0)
324  return INVALID_HANDLE_VALUE;
325 
326  h1 = (HANDLE) _get_osfhandle(fd);
327  if (!DuplicateHandle(GetCurrentProcess(), h1,
328  GetCurrentProcess(), &h2,
329  0, TRUE, DUPLICATE_SAME_ACCESS))
330  return INVALID_HANDLE_VALUE;
331 
332  return h2;
333 }
334 
335 static int win_spawn(const char *cmd, const char **argv, const char **envp,
336  const char *cwd, HANDLE handles[3], int background,
337  int shell)
338 {
339  char *args = make_command_line(shell, cmd, argv);
340  char *env = make_environment(envp);
341  char *program = shell ? NULL : find_program(cmd);
342  STARTUPINFO si;
343  PROCESS_INFORMATION pi;
344  BOOL result;
345  DWORD exitcode;
346  int i;
347 
348  if (!shell) {
349  G_debug(3, "win_spawn: program = %s", program);
350 
351  if (!program) {
352  G_free(args);
353  G_free(env);
354  return -1;
355  }
356  }
357 
358  G_debug(3, "win_spawn: args = %s", args);
359 
360  memset(&si, 0, sizeof(si));
361  si.cb = sizeof(si);
362 
363  si.dwFlags |= STARTF_USESTDHANDLES;
364  si.hStdInput = handles[0];
365  si.hStdOutput = handles[1];
366  si.hStdError = handles[2];
367 
368  result = CreateProcess(
369  program, /* lpApplicationName */
370  args, /* lpCommandLine */
371  NULL, /* lpProcessAttributes */
372  NULL, /* lpThreadAttributes */
373  1, /* bInheritHandles */
374  0, /* dwCreationFlags */
375  env, /* lpEnvironment */
376  cwd, /* lpCurrentDirectory */
377  &si, /* lpStartupInfo */
378  &pi /* lpProcessInformation */
379  );
380 
381  G_free(args);
382  G_free(env);
383  G_free(program);
384 
385  if (!result) {
386  G_warning(_("CreateProcess() failed: error = %d"), GetLastError());
387  return -1;
388  }
389 
390  CloseHandle(pi.hThread);
391 
392  for (i = 0; i < 3; i++)
393  if (handles[i] != INVALID_HANDLE_VALUE)
394  CloseHandle(handles[i]);
395 
396  if (!background) {
397  WaitForSingleObject(pi.hProcess, INFINITE);
398  if (!GetExitCodeProcess(pi.hProcess, &exitcode))
399  return -1;
400  CloseHandle(pi.hProcess);
401  return (int) exitcode;
402  }
403 
404  CloseHandle(pi.hProcess);
405 
406  return pi.dwProcessId;
407 }
408 
409 static void do_redirects(struct redirect *redirects, int num_redirects, HANDLE handles[3])
410 {
411  int i;
412 
413  for (i = 0; i < 3; i++)
414  handles[i] = get_handle(i);
415 
416  for (i = 0; i < num_redirects; i++) {
417  struct redirect *r = &redirects[i];
418 
419  if (r->dst_fd < 0 || r->dst_fd > 2) {
420  if (r->file || r->src_fd >= 0)
421  G_warning(_("G_spawn: unable to redirect descriptor %d"), r->dst_fd);
422  continue;
423  }
424 
425  if (r->file) {
426  r->src_fd = open(r->file, r->mode, 0666);
427 
428  if (r->src_fd < 0) {
429  G_warning(_("G_spawn: unable to open file %s"), r->file);
430  _exit(127);
431  }
432 
433  handles[r->dst_fd] = get_handle(r->src_fd);
434 
435  close(r->src_fd);
436 
437  }
438  else if (r->src_fd >= 0) {
439  handles[r->dst_fd] = get_handle(r->src_fd);
440  }
441  else {
442  if (r->dst_fd < 3) {
443  CloseHandle(handles[r->dst_fd]);
444  handles[r->dst_fd] = INVALID_HANDLE_VALUE;
445  }
446  close(r->dst_fd);
447  }
448  }
449 }
450 
451 static void add_binding(const char **env, int *pnum, const struct binding *b)
452 {
453  char *str = G_malloc(strlen(b->var) + strlen(b->val) + 2);
454  int n = *pnum;
455  int i;
456 
457  sprintf(str, "%s=%s", b->var, b->val);
458 
459  for (i = 0; i < n; i++)
460  if (G_strcasecmp(env[i], b->var) == 0) {
461  env[i] = str;
462  return;
463  }
464 
465  env[n++] = str;
466  *pnum = n;
467 }
468 
469 static const char **do_bindings(const struct binding *bindings, int num_bindings)
470 {
471  const char **newenv;
472  int i, n;
473 
474  for (i = 0; _environ[i]; i++)
475  ;
476  n = i;
477 
478  newenv = G_malloc((num_bindings + n + 1) * sizeof(char *));
479 
480  for (i = 0; i < n; i++)
481  newenv[i] = _environ[i];
482 
483  for (i = 0; i < num_bindings; i++)
484  add_binding(newenv, &n, &bindings[i]);
485 
486  newenv[num_bindings + n] = NULL;
487 
488  return newenv;
489 }
490 
491 static int do_spawn(struct spawn *sp, const char *command)
492 {
493  HANDLE handles[3];
494  const char **env;
495  int status;
496 
497  do_redirects(sp->redirects, sp->num_redirects, handles);
498  env = do_bindings(sp->bindings, sp->num_bindings);
499 
500  status = win_spawn(command, sp->args, env, sp->directory, handles, sp->background, 1);
501 
502  if (!sp->background && status < 0)
503  G_warning(_("G_spawn: unable to execute command"));
504 
505  return status;
506 }
507 
508 #else /* __MINGW32__ */
509 
510 static int undo_signals(const struct signal *signals, int num_signals, int which)
511 {
512  int error = 0;
513  int i;
514 
515  for (i = num_signals - 1; i >= 0; i--) {
516  const struct signal *s = &signals[i];
517 
518  if (s->which != which)
519  continue;
520 
521  if (!s->valid)
522  continue;
523 
524  switch (s->action) {
525  case SSA_IGNORE:
526  case SSA_DEFAULT:
527  if (sigaction(s->signum, &s->old_act, NULL) < 0) {
528  G_warning(_("G_spawn: unable to restore signal %d"),
529  s->signum);
530  error = 1;
531  }
532  break;
533  case SSA_BLOCK:
534  case SSA_UNBLOCK:
535  if (sigprocmask(SIG_UNBLOCK, &s->old_mask, NULL) < 0) {
536  G_warning(_("G_spawn: unable to restore signal %d"),
537  s->signum);
538  error = 1;
539  }
540  break;
541  }
542  }
543 
544  return !error;
545 }
546 
547 static int do_signals(struct signal *signals, int num_signals, int which)
548 {
549  struct sigaction act;
550  sigset_t mask;
551  int error = 0;
552  int i;
553 
554  sigemptyset(&act.sa_mask);
555  act.sa_flags = SA_RESTART;
556 
557  for (i = 0; i < num_signals; i++) {
558  struct signal *s = &signals[i];
559 
560  if (s->which != which)
561  continue;
562 
563  switch (s->action) {
564  case SSA_IGNORE:
565  act.sa_handler = SIG_IGN;
566  if (sigaction(s->signum, &act, &s->old_act) < 0) {
567  G_warning(_("G_spawn: unable to reset signal %d"), s->signum);
568  error = 1;
569  }
570  else
571  s->valid = 1;
572  break;
573  case SSA_DEFAULT:
574  act.sa_handler = SIG_DFL;
575  if (sigaction(s->signum, &act, &s->old_act) < 0) {
576  G_warning(_("G_spawn: unable to ignore signal %d"),
577  s->signum);
578  error = 1;
579  }
580  else
581  s->valid = 1;
582  break;
583  case SSA_BLOCK:
584  sigemptyset(&mask);
585  sigaddset(&mask, s->signum);
586  if (sigprocmask(SIG_BLOCK, &mask, &s->old_mask) < 0) {
587  G_warning(_("G_spawn: unable to block signal %d"), s->signum);
588  error = 1;
589  }
590  break;
591  case SSA_UNBLOCK:
592  sigemptyset(&mask);
593  sigaddset(&mask, s->signum);
594  if (sigprocmask(SIG_UNBLOCK, &mask, &s->old_mask) < 0) {
595  G_warning(_("G_spawn: unable to unblock signal %d"),
596  s->signum);
597  error = 1;
598  }
599  else
600  s->valid = 1;
601  break;
602  }
603  }
604 
605  return !error;
606 }
607 
608 static void do_redirects(struct redirect *redirects, int num_redirects)
609 {
610  int i;
611 
612  for (i = 0; i < num_redirects; i++) {
613  struct redirect *r = &redirects[i];
614 
615  if (r->file) {
616  r->src_fd = open(r->file, r->mode, 0666);
617 
618  if (r->src_fd < 0) {
619  G_warning(_("G_spawn: unable to open file %s"), r->file);
620  _exit(127);
621  }
622 
623  if (dup2(r->src_fd, r->dst_fd) < 0) {
624  G_warning(_("G_spawn: unable to duplicate descriptor %d to %d"),
625  r->src_fd, r->dst_fd);
626  _exit(127);
627  }
628 
629  close(r->src_fd);
630  }
631  else if (r->src_fd >= 0) {
632  if (dup2(r->src_fd, r->dst_fd) < 0) {
633  G_warning(_("G_spawn: unable to duplicate descriptor %d to %d"),
634  r->src_fd, r->dst_fd);
635  _exit(127);
636  }
637  }
638  else
639  close(r->dst_fd);
640  }
641 }
642 
643 static void do_bindings(const struct binding *bindings, int num_bindings)
644 {
645  int i;
646 
647  for (i = 0; i < num_bindings; i++) {
648  const struct binding *b = &bindings[i];
649  char *str = G_malloc(strlen(b->var) + strlen(b->val) + 2);
650 
651  sprintf(str, "%s=%s", b->var, b->val);
652  putenv(str);
653  }
654 }
655 
656 static int do_spawn(struct spawn *sp, const char *command)
657 {
658  int status = -1;
659  pid_t pid;
660 
661  if (!do_signals(sp->signals, sp->num_signals, SST_PRE))
662  return status;
663 
664  pid = fork();
665  if (pid < 0) {
666  G_warning(_("Unable to create a new process: %s"), strerror(errno));
667  undo_signals(sp->signals, sp->num_signals, SST_PRE);
668 
669  return status;
670  }
671 
672  if (pid == 0) {
673  if (!undo_signals(sp->signals, sp->num_signals, SST_PRE))
674  _exit(127);
675 
676  if (!do_signals(sp->signals, sp->num_signals, SST_CHILD))
677  _exit(127);
678 
679  if (sp->directory)
680  if (chdir(sp->directory) < 0) {
681  G_warning(_("Unable to change directory to %s"), sp->directory);
682  _exit(127);
683  }
684 
685  do_redirects(sp->redirects, sp->num_redirects);
686  do_bindings(sp->bindings, sp->num_bindings);
687 
688  execvp(command, (char **)sp->args);
689  G_warning(_("Unable to execute command '%s': %s"), command, strerror(errno));
690  _exit(127);
691  }
692 
693  do_signals(sp->signals, sp->num_signals, SST_POST);
694 
695  if (sp->background)
696  status = (int)pid;
697  else {
698  pid_t n;
699 
700  do
701  n = waitpid(pid, &status, 0);
702  while (n == (pid_t) - 1 && errno == EINTR);
703 
704  if (n != pid)
705  status = -1;
706  else {
707  if (WIFEXITED(status))
708  status = WEXITSTATUS(status);
709  else if (WIFSIGNALED(status))
710  status = WTERMSIG(status);
711  else
712  status = -0x100;
713  }
714  }
715 
716  undo_signals(sp->signals, sp->num_signals, SST_POST);
717  undo_signals(sp->signals, sp->num_signals, SST_PRE);
718 
719  return status;
720 }
721 
722 #endif /* __MINGW32__ */
723 
724 static void begin_spawn(struct spawn *sp)
725 {
726  sp->num_args = 0;
727  sp->num_redirects = 0;
728  sp->num_signals = 0;
729  sp->num_bindings = 0;
730  sp->background = 0;
731  sp->directory = NULL;
732 }
733 
734 #define NEXT_ARG(var, type) ((type) *(var)++)
735 #define NEXT_ARG_INT(var) (int)((intptr_t) *(var)++)
736 
737 static void parse_argvec(struct spawn *sp, const char **va)
738 {
739  for (;;) {
740  const char *arg = NEXT_ARG(va, const char *);
741  const char *var, *val;
742 
743  if (!arg) {
744  sp->args[sp->num_args++] = NULL;
745  break;
746  }
747  else if (arg == SF_REDIRECT_FILE) {
748  sp->redirects[sp->num_redirects].dst_fd = NEXT_ARG_INT(va);
749 
750  sp->redirects[sp->num_redirects].src_fd = -1;
751  sp->redirects[sp->num_redirects].mode = NEXT_ARG_INT(va);
752  sp->redirects[sp->num_redirects].file = NEXT_ARG(va, const char *);
753 
754  sp->num_redirects++;
755  }
756  else if (arg == SF_REDIRECT_DESCRIPTOR) {
757  sp->redirects[sp->num_redirects].dst_fd = NEXT_ARG_INT(va);
758  sp->redirects[sp->num_redirects].src_fd = NEXT_ARG_INT(va);
759 
760  sp->redirects[sp->num_redirects].file = NULL;
761  sp->num_redirects++;
762  }
763  else if (arg == SF_CLOSE_DESCRIPTOR) {
764  sp->redirects[sp->num_redirects].dst_fd = NEXT_ARG_INT(va);
765 
766  sp->redirects[sp->num_redirects].src_fd = -1;
767  sp->redirects[sp->num_redirects].file = NULL;
768  sp->num_redirects++;
769  }
770  else if (arg == SF_SIGNAL) {
771  sp->signals[sp->num_signals].which = NEXT_ARG_INT(va);
772  sp->signals[sp->num_signals].action = NEXT_ARG_INT(va);
773  sp->signals[sp->num_signals].signum = NEXT_ARG_INT(va);
774 
775  sp->signals[sp->num_signals].valid = 0;
776  sp->num_signals++;
777  }
778  else if (arg == SF_VARIABLE) {
779  var = NEXT_ARG(va, const char *);
780 
781  val = getenv(var);
782  sp->args[sp->num_args++] = val ? val : "";
783  }
784  else if (arg == SF_BINDING) {
785  sp->bindings[sp->num_bindings].var = NEXT_ARG(va, const char *);
786  sp->bindings[sp->num_bindings].val = NEXT_ARG(va, const char *);
787 
788  sp->num_bindings++;
789  }
790  else if (arg == SF_BACKGROUND) {
791  sp->background = 1;
792  }
793  else if (arg == SF_DIRECTORY) {
794  sp->directory = NEXT_ARG(va, const char *);
795 
796  }
797  else if (arg == SF_ARGVEC) {
798  parse_argvec(sp, NEXT_ARG(va, const char **));
799  }
800  else
801  sp->args[sp->num_args++] = arg;
802  }
803 }
804 
805 static void parse_arglist(struct spawn *sp, va_list va)
806 {
807  for (;;) {
808  const char *arg = va_arg(va, const char *);
809  const char *var, *val;
810 
811  if (!arg) {
812  sp->args[sp->num_args++] = NULL;
813  break;
814  }
815  else if (arg == SF_REDIRECT_FILE) {
816  sp->redirects[sp->num_redirects].dst_fd = va_arg(va, int);
817 
818  sp->redirects[sp->num_redirects].src_fd = -1;
819  sp->redirects[sp->num_redirects].mode = va_arg(va, int);
820  sp->redirects[sp->num_redirects].file = va_arg(va, const char *);
821 
822  sp->num_redirects++;
823  }
824  else if (arg == SF_REDIRECT_DESCRIPTOR) {
825  sp->redirects[sp->num_redirects].dst_fd = va_arg(va, int);
826  sp->redirects[sp->num_redirects].src_fd = va_arg(va, int);
827 
828  sp->redirects[sp->num_redirects].file = NULL;
829  sp->num_redirects++;
830  }
831  else if (arg == SF_CLOSE_DESCRIPTOR) {
832  sp->redirects[sp->num_redirects].dst_fd = va_arg(va, int);
833 
834  sp->redirects[sp->num_redirects].src_fd = -1;
835  sp->redirects[sp->num_redirects].file = NULL;
836  sp->num_redirects++;
837  }
838  else if (arg == SF_SIGNAL) {
839  sp->signals[sp->num_signals].which = va_arg(va, int);
840  sp->signals[sp->num_signals].action = va_arg(va, int);
841  sp->signals[sp->num_signals].signum = va_arg(va, int);
842 
843  sp->signals[sp->num_signals].valid = 0;
844  sp->num_signals++;
845  }
846  else if (arg == SF_VARIABLE) {
847  var = va_arg(va, char *);
848 
849  val = getenv(var);
850  sp->args[sp->num_args++] = val ? val : "";
851  }
852  else if (arg == SF_BINDING) {
853  sp->bindings[sp->num_bindings].var = va_arg(va, const char *);
854  sp->bindings[sp->num_bindings].val = va_arg(va, const char *);
855 
856  sp->num_bindings++;
857  }
858  else if (arg == SF_BACKGROUND) {
859  sp->background = 1;
860  }
861  else if (arg == SF_DIRECTORY) {
862  sp->directory = va_arg(va, const char *);
863  }
864  else if (arg == SF_ARGVEC) {
865  parse_argvec(sp, va_arg(va, const char **));
866  }
867  else
868  sp->args[sp->num_args++] = arg;
869  }
870 }
871 
872 /**
873  * \brief Spawn new process based on <b>command</b>.
874  *
875  * This is a more advanced version of G_spawn().
876  *
877  * \param[in] command
878  * \param[in] args arguments
879  * \return -1 on error
880  * \return process status on success
881  */
882 int G_vspawn_ex(const char *command, const char **args)
883 {
884  struct spawn sp;
885 
886  begin_spawn(&sp);
887 
888  parse_argvec(&sp, args);
889 
890  return do_spawn(&sp, command);
891 }
892 
893 /**
894  * \brief Spawn new process based on <b>command</b>.
895  *
896  * This is a more advanced version of G_spawn().
897  *
898  * \param[in] command
899  * \return -1 on error
900  * \return process status on success
901  */
902 
903 int G_spawn_ex(const char *command, ...)
904 {
905  struct spawn sp;
906  va_list va;
907 
908  begin_spawn(&sp);
909 
910  va_start(va, command);
911  parse_arglist(&sp, va);
912  va_end(va);
913 
914  return do_spawn(&sp, command);
915 }
916 
917 /**
918  * \brief Spawn new process based on <b>command</b>.
919  *
920  * \param[in] command
921  * \return -1 on error
922  * \return process status on success
923  */
924 
925 int G_spawn(const char *command, ...)
926 {
927  const char *args[MAX_ARGS];
928  int num_args = 0, i;
929  va_list va;
930  int status = -1;
931 
932  va_start(va, command);
933 
934  for (i = 0; ; i++) {
935  const char *arg = va_arg(va, const char *);
936  args[num_args++] = arg;
937  if (!arg)
938  break;
939  }
940 
941  va_end(va);
942 
943  status = G_spawn_ex(
944  command,
945 #ifndef __MINGW32__
946  SF_SIGNAL, SST_PRE, SSA_IGNORE, SIGINT,
947  SF_SIGNAL, SST_PRE, SSA_IGNORE, SIGQUIT,
948  SF_SIGNAL, SST_PRE, SSA_BLOCK, SIGCHLD,
949 #endif
950  SF_ARGVEC, args,
951  NULL);
952 
953  return status;
954 }
955 
956 int G_wait(int i_pid)
957 {
958 #ifdef __MINGW32__
959  DWORD rights = PROCESS_QUERY_INFORMATION | SYNCHRONIZE;
960  HANDLE hProcess = OpenProcess(rights, FALSE, (DWORD) i_pid);
961  DWORD exitcode;
962 
963  if (!hProcess)
964  return -1;
965 
966  WaitForSingleObject(hProcess, INFINITE);
967  if (!GetExitCodeProcess(hProcess, &exitcode))
968  exitcode = (DWORD) -1;
969 
970  CloseHandle(hProcess);
971 
972  return (int) exitcode;
973 #else
974  pid_t pid = (pid_t) i_pid;
975  int status = -1;
976  pid_t n;
977 
978  do
979  n = waitpid(pid, &status, 0);
980  while (n == (pid_t) - 1 && errno == EINTR);
981 
982  if (n != pid)
983  return -1;
984  else {
985  if (WIFEXITED(status))
986  return WEXITSTATUS(status);
987  else if (WIFSIGNALED(status))
988  return WTERMSIG(status);
989  else
990  return -0x100;
991  }
992 #endif
993 }
994 
int G_wait(int i_pid)
Definition: spawn.c:956
#define TRUE
Definition: gis.h:59
#define G_malloc(n)
Definition: defs/gis.h:112
Definition: spawn.h:39
#define SF_SIGNAL
Definition: spawn.h:20
int G_spawn_ex(const char *command,...)
Spawn new process based on command.
Definition: spawn.c:903
int G_vspawn_ex(const char *command, const char **args)
Spawn new process based on command.
Definition: spawn.c:882
#define SF_REDIRECT_FILE
Definition: spawn.h:17
void G_free(void *)
Free allocated memory.
Definition: gis/alloc.c:149
#define SF_ARGVEC
Definition: spawn.h:25
#define SF_REDIRECT_DESCRIPTOR
Definition: spawn.h:18
char ** G_tokenize(const char *, const char *)
Tokenize string.
Definition: gis/token.c:48
#define NULL
Definition: ccmath.h:32
#define SF_BACKGROUND
Definition: spawn.h:23
int int G_strcasecmp(const char *, const char *)
String compare ignoring case (upper or lower)
Definition: strings.c:47
double b
Definition: r_raster.c:39
#define FALSE
Definition: gis.h:63
#define SF_BINDING
Definition: spawn.h:22
#define GPATH_MAX
Definition: gis.h:180
#define MAX_ARGS
Definition: spawn.c:44
float var(IClass_statistics *statistics, int band1, int band2)
Helper function for computing variance.
void G_free_tokens(char **)
Free memory allocated to tokens.
Definition: gis/token.c:204
void G_warning(const char *,...) __attribute__((format(printf
Definition: path.h:16
#define file
#define G_realloc(p, n)
Definition: defs/gis.h:114
#define SF_DIRECTORY
Definition: spawn.h:24
#define _(str)
Definition: glocale.h:10
#define SF_CLOSE_DESCRIPTOR
Definition: spawn.h:19
#define NEXT_ARG_INT(var)
Definition: spawn.c:735
char * G_store(const char *)
Copy string to allocated memory.
Definition: strings.c:87
char * getenv()
int G_debug(int, const char *,...) __attribute__((format(printf
#define SF_VARIABLE
Definition: spawn.h:21
Definition: spawn.h:38
double r
Definition: r_raster.c:39
#define NEXT_ARG(var, type)
Definition: spawn.c:734
void init(double work[])
Definition: as177.c:65
int G_spawn(const char *command,...)
Spawn new process based on command.
Definition: spawn.c:925