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