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