GRASS GIS 8 Programmer's Manual 8.2.1(2023)-exported
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
61struct redirect
62{
63 int dst_fd;
64 int src_fd;
65 const char *file;
66 int mode;
67};
68
69struct 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
81struct binding
82{
83 const char *var;
84 const char *val;
85};
86
87struct 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
101static void parse_arglist(struct spawn *sp, va_list va);
102static void parse_argvec(struct spawn *sp, const char **va);
103
104#ifdef __MINGW32__
105
106struct buffer {
107 char *str;
108 size_t len;
109 size_t size;
110};
111
112static const int INCREMENT = 50;
113
114static void clear(struct buffer *b)
115{
116 b->len = 0;
117 b->str[b->len] = '\0';
118}
119
120static void init(struct buffer *b)
121{
122 b->str = G_malloc(1);
123 b->size = 1;
124 clear(b);
125}
126
127static 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
138static void finish(struct buffer *b)
139{
140 if (b->str)
141 G_free(b->str);
142 release(b);
143}
144
145static 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
153static 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
163static 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
171static 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
217static 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
227static 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
244static 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
267static 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);
273 G_free_tokens(pathext);
274 return result;
275}
276
277static 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
302static 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
319static 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
335static 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
409static 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
451static 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
469static 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
491static 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
510static 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
547static 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
608static 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
643static 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
656static 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
724static 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
737static 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
805static 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 */
882int 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
903int 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
925int 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
956int 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
void G_free(void *buf)
Free allocated memory.
Definition: alloc.c:149
void init(double work[])
Definition: as177.c:65
#define NULL
Definition: ccmath.h:32
#define TRUE
Definition: dbfopen.c:183
#define FALSE
Definition: dbfopen.c:182
int G_debug(int level, const char *msg,...)
Print debugging message.
Definition: debug.c:65
double b
double r
void G_warning(const char *msg,...)
Print a warning message to stderr.
Definition: gis/error.c:204
#define file
#define NEXT_ARG(var, type)
Definition: spawn.c:734
#define MAX_ARGS
Definition: spawn.c:44
int G_vspawn_ex(const char *command, const char **args)
Spawn new process based on command.
Definition: spawn.c:882
int G_wait(int i_pid)
Definition: spawn.c:956
int G_spawn(const char *command,...)
Spawn new process based on command.
Definition: spawn.c:925
#define MAX_BINDINGS
Definition: spawn.c:45
int G_spawn_ex(const char *command,...)
Spawn new process based on command.
Definition: spawn.c:903
#define NEXT_ARG_INT(var)
Definition: spawn.c:735
#define MAX_REDIRECTS
Definition: spawn.c:47
#define MAX_SIGNALS
Definition: spawn.c:46
int G_strcasecmp(const char *x, const char *y)
String compare ignoring case (upper or lower)
Definition: strings.c:47
char * G_store(const char *s)
Copy string to allocated memory.
Definition: strings.c:87
Definition: path.h:16
void G_free_tokens(char **tokens)
Free memory allocated to tokens.
Definition: token.c:204
char ** G_tokenize(const char *buf, const char *delim)
Tokenize string.
Definition: token.c:48