00001
00017 #include <stdio.h>
00018 #include <stdlib.h>
00019 #include <string.h>
00020 #include <signal.h>
00021 #include <stdarg.h>
00022 #include <unistd.h>
00023 #include <fcntl.h>
00024 #include <errno.h>
00025 #include <sys/types.h>
00026
00027 #ifndef __MINGW32__
00028 #include <sys/wait.h>
00029 #endif
00030 #include <grass/config.h>
00031 #include <grass/gis.h>
00032 #include <grass/glocale.h>
00033 #include <grass/spawn.h>
00034
00042 #define MAX_ARGS 256
00043 #define MAX_BINDINGS 256
00044 #define MAX_SIGNALS 32
00045 #define MAX_REDIRECTS 32
00046
00047
00059 #ifdef __MINGW32__
00060
00061 int G_spawn(const char *command, ...)
00062 {
00063 va_list va;
00064 char *args[MAX_ARGS];
00065 int num_args = 0;
00066
00067 va_start(va, command);
00068
00069 for (num_args = 0; num_args < MAX_ARGS;) {
00070 char *arg = va_arg(va, char *);
00071
00072 args[num_args++] = arg;
00073 if (!arg)
00074 break;
00075 }
00076
00077 va_end(va);
00078
00079 if (num_args >= MAX_ARGS) {
00080 G_warning(_("Too many arguments"));
00081 return -1;
00082 }
00083
00084 G_debug(3, "spawning '%s' ...", command);
00085
00086 return _spawnvp(_P_WAIT, (char *)command, args);
00087 }
00088
00089 #else
00090
00091 int G_spawn(const char *command, ...)
00092 {
00093 va_list va;
00094 char *args[MAX_ARGS];
00095 int num_args = 0;
00096 struct sigaction act, intr, quit;
00097 sigset_t block, oldmask;
00098 int status = -1;
00099 pid_t pid;
00100
00101 va_start(va, command);
00102
00103 for (num_args = 0; num_args < MAX_ARGS;) {
00104 char *arg = va_arg(va, char *);
00105
00106 args[num_args++] = arg;
00107 if (!arg)
00108 break;
00109 }
00110
00111 va_end(va);
00112
00113 if (num_args >= MAX_ARGS) {
00114 G_warning(_("Too many arguments"));
00115 return -1;
00116 }
00117
00118 sigemptyset(&act.sa_mask);
00119 act.sa_flags = SA_RESTART;
00120
00121 act.sa_handler = SIG_IGN;
00122 if (sigaction(SIGINT, &act, &intr) < 0)
00123 goto error_1;
00124 if (sigaction(SIGQUIT, &act, &quit) < 0)
00125 goto error_2;
00126
00127 sigemptyset(&block);
00128 sigaddset(&block, SIGCHLD);
00129 if (sigprocmask(SIG_BLOCK, &block, &oldmask) < 0)
00130 goto error_3;
00131
00132 G_debug(3, "forking '%s' ...", command);
00133
00134 pid = fork();
00135
00136 if (pid < 0) {
00137 G_warning(_("Unable to create a new process"));
00138 goto error_4;
00139 }
00140
00141 if (pid == 0) {
00142 sigaction(SIGINT, &intr, NULL);
00143 sigaction(SIGQUIT, &quit, NULL);
00144
00145 execvp(command, args);
00146 G_warning(_("Unable to execute command"));
00147 _exit(127);
00148 }
00149 else {
00150 pid_t n;
00151
00152 do
00153 n = waitpid(pid, &status, 0);
00154 while (n == (pid_t) - 1 && errno == EINTR);
00155
00156 if (n != pid)
00157 status = -1;
00158 }
00159
00160 error_4:
00161 sigprocmask(SIG_SETMASK, &oldmask, NULL);
00162 error_3:
00163 sigaction(SIGQUIT, &quit, NULL);
00164 error_2:
00165 sigaction(SIGINT, &intr, NULL);
00166 error_1:
00167 return status;
00168 }
00169
00170 #endif
00171
00172 struct redirect
00173 {
00174 int dst_fd;
00175 int src_fd;
00176 const char *file;
00177 int mode;
00178 };
00179
00180 struct signal
00181 {
00182 int which;
00183 int action;
00184 int signum;
00185 int valid;
00186 #ifndef __MINGW32__
00187 struct sigaction old_act;
00188 sigset_t old_mask;
00189 #endif
00190 };
00191
00192 struct binding
00193 {
00194 const char *var;
00195 const char *val;
00196 };
00197
00198 static const char *args[MAX_ARGS];
00199 static int num_args;
00200 static struct redirect redirects[MAX_REDIRECTS];
00201 static int num_redirects;
00202 static struct signal signals[MAX_SIGNALS];
00203 static int num_signals;
00204 static struct binding bindings[MAX_BINDINGS];
00205 static int num_bindings;
00206 static int background;
00207 static const char *directory;
00208
00209 #ifdef __MINGW32__
00210
00211 static int do_redirects(struct redirect *redirects, int num_redirects)
00212 {
00213 if (num_redirects > 0)
00214 G_fatal_error
00215 ("G_spawn_ex: redirection not (yet) supported on Windows");
00216 }
00217
00218 static char **do_bindings(char **env, struct binding *bindings,
00219 int num_bindings)
00220 {
00221 if (num_bindings > 0)
00222 G_fatal_error
00223 ("G_spawn_ex: redirection not (yet) supported on Windows");
00224
00225 return env;
00226 }
00227
00228 static int do_spawn(const char *command)
00229 {
00230 char **env;
00231 int status;
00232
00233 do_redirects(redirects, num_redirects);
00234 env = do_bindings(_environ, bindings, num_bindings);
00235
00236 status =
00237 spawnvpe(background ? _P_NOWAIT : _P_WAIT, command, (char **)args,
00238 env);
00239
00240 if (!background && status < 0)
00241 G_warning(_("Unable to execute command"));
00242
00243 return status;
00244 }
00245
00246 #else
00247
00248 static int undo_signals(struct signal *signals, int num_signals, int which)
00249 {
00250 int error = 0;
00251 int i;
00252
00253 for (i = num_signals - 1; i >= 0; i--) {
00254 struct signal *s = &signals[i];
00255
00256 if (s->which != which)
00257 continue;
00258
00259 if (!s->valid)
00260 continue;
00261
00262 switch (s->action) {
00263 case SSA_IGNORE:
00264 case SSA_DEFAULT:
00265 if (sigaction(s->signum, &s->old_act, NULL) < 0) {
00266 G_warning(_("G_spawn: unable to restore signal %d"),
00267 s->signum);
00268 error = 1;
00269 }
00270 break;
00271 case SSA_BLOCK:
00272 case SSA_UNBLOCK:
00273 if (sigprocmask(SIG_UNBLOCK, &s->old_mask, NULL) < 0) {
00274 G_warning(_("G_spawn: unable to restore signal %d"),
00275 s->signum);
00276 error = 1;
00277 }
00278 break;
00279 }
00280 }
00281
00282 return !error;
00283 }
00284
00285 static int do_signals(struct signal *signals, int num_signals, int which)
00286 {
00287 struct sigaction act;
00288 sigset_t mask;
00289 int error = 0;
00290 int i;
00291
00292 sigemptyset(&act.sa_mask);
00293 act.sa_flags = SA_RESTART;
00294
00295 for (i = 0; i < num_signals; i++) {
00296 struct signal *s = &signals[i];
00297
00298 if (s->which != which)
00299 continue;
00300
00301 switch (s->action) {
00302 case SSA_IGNORE:
00303 act.sa_handler = SIG_IGN;
00304 if (sigaction(s->signum, &act, &s->old_act) < 0) {
00305 G_warning(_("G_spawn: unable to reset signal %d"), s->signum);
00306 error = 1;
00307 }
00308 else
00309 s->valid = 1;
00310 break;
00311 case SSA_DEFAULT:
00312 act.sa_handler = SIG_DFL;
00313 if (sigaction(s->signum, &act, &s->old_act) < 0) {
00314 G_warning(_("G_spawn: unable to ignore signal %d"),
00315 s->signum);
00316 error = 1;
00317 }
00318 else
00319 s->valid = 1;
00320 break;
00321 case SSA_BLOCK:
00322 sigemptyset(&mask);
00323 sigaddset(&mask, s->signum);
00324 if (sigprocmask(SIG_BLOCK, &mask, &s->old_mask) < 0) {
00325 G_warning(_("G_spawn: unable to block signal %d"), s->signum);
00326 error = 1;
00327 }
00328 break;
00329 case SSA_UNBLOCK:
00330 sigemptyset(&mask);
00331 sigaddset(&mask, s->signum);
00332 if (sigprocmask(SIG_UNBLOCK, &mask, &s->old_mask) < 0) {
00333 G_warning(_("G_spawn: unable to unblock signal %d"),
00334 s->signum);
00335 error = 1;
00336 }
00337 else
00338 s->valid = 1;
00339 break;
00340 }
00341 }
00342
00343 return !error;
00344 }
00345
00346 static void do_redirects(struct redirect *redirects, int num_redirects)
00347 {
00348 int i;
00349
00350 for (i = 0; i < num_redirects; i++) {
00351 struct redirect *r = &redirects[i];
00352
00353 if (r->file) {
00354 r->src_fd = open(r->file, r->mode, 0666);
00355
00356 if (r->src_fd < 0) {
00357 G_warning(_("G_spawn: unable to open file %s"), r->file);
00358 _exit(127);
00359 }
00360
00361 if (dup2(r->src_fd, r->dst_fd) < 0) {
00362 G_warning(_("G_spawn: unable to duplicate descriptor %d to %d"),
00363 r->src_fd, r->dst_fd);
00364 _exit(127);
00365 }
00366
00367 close(r->src_fd);
00368 }
00369 else if (r->src_fd >= 0) {
00370 if (dup2(r->src_fd, r->dst_fd) < 0) {
00371 G_warning(_("G_spawn: unable to duplicate descriptor %d to %d"),
00372 r->src_fd, r->dst_fd);
00373 _exit(127);
00374 }
00375 }
00376 else
00377 close(r->dst_fd);
00378 }
00379 }
00380
00381 static void do_bindings(struct binding *bindings, int num_bindings)
00382 {
00383 int i;
00384
00385 for (i = 0; i < num_bindings; i++) {
00386 struct binding *b = &bindings[i];
00387 static char *str = NULL;
00388
00389 str = G_realloc(str, strlen(b->var) + strlen(b->val) + 2);
00390 sprintf(str, "%s=%s", b->var, b->val);
00391 putenv(str);
00392 }
00393 }
00394
00395 static int do_spawn(const char *command)
00396 {
00397 int status = -1;
00398 pid_t pid;
00399
00400 if (!do_signals(signals, num_signals, SST_PRE))
00401 return status;
00402
00403 pid = fork();
00404 if (pid < 0) {
00405 G_warning(_("Unable to create a new process"));
00406 undo_signals(signals, num_signals, SST_PRE);
00407
00408 return status;
00409 }
00410
00411 if (pid == 0) {
00412 if (!undo_signals(signals, num_signals, SST_PRE))
00413 _exit(127);
00414
00415 if (!do_signals(signals, num_signals, SST_CHILD))
00416 _exit(127);
00417
00418 if (directory)
00419 if (chdir(directory) < 0) {
00420 G_warning(_("Unable to change directory to %s"), directory);
00421 _exit(127);
00422 }
00423
00424 do_redirects(redirects, num_redirects);
00425 do_bindings(bindings, num_bindings);
00426
00427 execvp(command, (char **)args);
00428 G_warning(_("Unable to execute command"));
00429 _exit(127);
00430 }
00431
00432 do_signals(signals, num_signals, SST_POST);
00433
00434 if (background)
00435 status = (int)pid;
00436 else {
00437 pid_t n;
00438
00439 do
00440 n = waitpid(pid, &status, 0);
00441 while (n == (pid_t) - 1 && errno == EINTR);
00442
00443 if (n != pid)
00444 status = -1;
00445 }
00446
00447 undo_signals(signals, num_signals, SST_POST);
00448 undo_signals(signals, num_signals, SST_PRE);
00449
00450 return status;
00451 }
00452
00453 #endif
00454
00455 static void begin_spawn(void)
00456 {
00457 num_args = 0;
00458 num_redirects = 0;
00459 num_signals = 0;
00460 num_bindings = 0;
00461 background = 0;
00462 directory = NULL;
00463 }
00464
00465 #define NEXT_ARG(var, type) ((type) *(var)++)
00466
00467 static void parse_argvec(const char **va)
00468 {
00469 for (;;) {
00470 const char *arg = NEXT_ARG(va, const char *);
00471 const char *var, *val;
00472
00473 switch ((int)arg) {
00474 case 0:
00475 args[num_args++] = NULL;
00476 break;
00477 case ((int)SF_REDIRECT_FILE):
00478 redirects[num_redirects].dst_fd = NEXT_ARG(va, int);
00479
00480 redirects[num_redirects].src_fd = -1;
00481 redirects[num_redirects].mode = NEXT_ARG(va, int);
00482 redirects[num_redirects].file = NEXT_ARG(va, const char *);
00483
00484 num_redirects++;
00485 break;
00486 case ((int)SF_REDIRECT_DESCRIPTOR):
00487 redirects[num_redirects].dst_fd = NEXT_ARG(va, int);
00488 redirects[num_redirects].src_fd = NEXT_ARG(va, int);
00489
00490 redirects[num_redirects].file = NULL;
00491 num_redirects++;
00492 break;
00493 case ((int)SF_CLOSE_DESCRIPTOR):
00494 redirects[num_redirects].dst_fd = NEXT_ARG(va, int);
00495
00496 redirects[num_redirects].src_fd = -1;
00497 redirects[num_redirects].file = NULL;
00498 num_redirects++;
00499 break;
00500 case ((int)SF_SIGNAL):
00501 signals[num_signals].which = NEXT_ARG(va, int);
00502 signals[num_signals].action = NEXT_ARG(va, int);
00503 signals[num_signals].signum = NEXT_ARG(va, int);
00504
00505 signals[num_signals].valid = 0;
00506 num_signals++;
00507 break;
00508 case ((int)SF_VARIABLE):
00509 var = NEXT_ARG(va, const char *);
00510
00511 val = getenv(var);
00512 args[num_args++] = val ? val : "";
00513 break;
00514 case ((int)SF_BINDING):
00515 bindings[num_bindings].var = NEXT_ARG(va, const char *);
00516 bindings[num_bindings].val = NEXT_ARG(va, const char *);
00517
00518 num_bindings++;
00519 break;
00520 case ((int)SF_BACKGROUND):
00521 background = 1;
00522 break;
00523 case ((int)SF_DIRECTORY):
00524 directory = NEXT_ARG(va, const char *);
00525
00526 break;
00527 case ((int)SF_ARGVEC):
00528 parse_argvec(NEXT_ARG(va, const char **));
00529
00530 break;
00531 default:
00532 args[num_args++] = arg;
00533 break;
00534 }
00535
00536 if (!arg)
00537 break;
00538 }
00539 }
00540
00541 static void parse_arglist(va_list va)
00542 {
00543 for (;;) {
00544 const char *arg = va_arg(va, const char *);
00545 const char *var, *val;
00546
00547 switch ((int)arg) {
00548 case 0:
00549 args[num_args++] = NULL;
00550 break;
00551 case ((int)SF_REDIRECT_FILE):
00552 redirects[num_redirects].dst_fd = va_arg(va, int);
00553
00554 redirects[num_redirects].src_fd = -1;
00555 redirects[num_redirects].mode = va_arg(va, int);
00556 redirects[num_redirects].file = va_arg(va, const char *);
00557
00558 num_redirects++;
00559 break;
00560 case ((int)SF_REDIRECT_DESCRIPTOR):
00561 redirects[num_redirects].dst_fd = va_arg(va, int);
00562 redirects[num_redirects].src_fd = va_arg(va, int);
00563
00564 redirects[num_redirects].file = NULL;
00565 num_redirects++;
00566 break;
00567 case ((int)SF_CLOSE_DESCRIPTOR):
00568 redirects[num_redirects].dst_fd = va_arg(va, int);
00569
00570 redirects[num_redirects].src_fd = -1;
00571 redirects[num_redirects].file = NULL;
00572 num_redirects++;
00573 break;
00574 case ((int)SF_SIGNAL):
00575 signals[num_signals].which = va_arg(va, int);
00576 signals[num_signals].action = va_arg(va, int);
00577 signals[num_signals].signum = va_arg(va, int);
00578
00579 signals[num_signals].valid = 0;
00580 num_signals++;
00581 break;
00582 case ((int)SF_VARIABLE):
00583 var = va_arg(va, char *);
00584
00585 val = getenv(var);
00586 args[num_args++] = val ? val : "";
00587 break;
00588 case ((int)SF_BINDING):
00589 bindings[num_bindings].var = va_arg(va, const char *);
00590 bindings[num_bindings].val = va_arg(va, const char *);
00591
00592 num_bindings++;
00593 break;
00594 case ((int)SF_BACKGROUND):
00595 background = 1;
00596 break;
00597 case ((int)SF_DIRECTORY):
00598 directory = va_arg(va, const char *);
00599
00600 break;
00601 case ((int)SF_ARGVEC):
00602 parse_argvec(va_arg(va, const char **));
00603
00604 break;
00605 default:
00606 args[num_args++] = arg;
00607 break;
00608 }
00609
00610 if (!arg)
00611 break;
00612 }
00613 }
00614
00625 int G_vspawn_ex(const char *command, const char **args)
00626 {
00627 begin_spawn();
00628
00629 parse_argvec(args);
00630
00631 return do_spawn(command);
00632 }
00633
00644 int G_spawn_ex(const char *command, ...)
00645 {
00646 va_list va;
00647
00648 begin_spawn();
00649
00650 va_start(va, command);
00651 parse_arglist(va);
00652 va_end(va);
00653
00654 return do_spawn(command);
00655 }