spawn.c

Go to the documentation of this file.
00001 #include <stdio.h>
00002 #include <stdlib.h>
00003 #include <string.h>
00004 #include <signal.h>
00005 #include <stdarg.h>
00006 #include <unistd.h>
00007 #include <fcntl.h>
00008 #include <errno.h>
00009 #include <sys/types.h>
00010 #include <sys/wait.h>
00011 
00012 #include "config.h"
00013 #include "gis.h"
00014 #include "glocale.h"
00015 #include "spawn.h"
00016 
00017 #define MAX_ARGS 256
00018 #define MAX_BINDINGS 256
00019 #define MAX_SIGNALS 32
00020 #define MAX_REDIRECTS 32
00021 
00022 /****************************************************************
00023  * G_spawn(char *command, ...)
00024  *
00025  * This is a more useful alternative to G_system(), which takes
00026  * the command's arguments as separate parameters.
00027  *
00028  ****************************************************************/
00029 
00030 int G_spawn(char *command, ...)
00031 {
00032         va_list va;
00033         char *args[MAX_ARGS];
00034         int num_args = 0;
00035         struct sigaction act, intr, quit;
00036         sigset_t block, oldmask;
00037         int status = -1;
00038         pid_t pid;
00039 
00040         args[0] = command;
00041 
00042         va_start(va, command);
00043 
00044         for (num_args = 1; num_args < MAX_ARGS; )
00045         {
00046                 char *arg = va_arg(va, char *);
00047                 if (!arg)
00048                         break;
00049                 args[num_args++] = arg;
00050         }
00051 
00052         va_end(va);
00053 
00054         if (num_args >= MAX_ARGS)
00055         {
00056                 G_warning(_("too many arguments"));
00057                 return -1;
00058         }
00059 
00060         sigemptyset(&act.sa_mask);
00061         act.sa_flags = SA_RESTART;
00062 
00063         act.sa_handler = SIG_IGN;
00064         if (sigaction(SIGINT, &act, &intr) < 0)
00065                 goto error_1;
00066         if (sigaction(SIGQUIT, &act, &quit) < 0)
00067                 goto error_2;
00068 
00069         sigemptyset(&block);
00070         sigaddset(&block, SIGCHLD);
00071         if (sigprocmask(SIG_BLOCK, &block, &oldmask) < 0)
00072                 goto error_3;
00073 
00074         pid = fork();
00075 
00076         if (pid < 0)
00077         {
00078                 G_warning(_("unable to create a new process"));
00079                 goto error_4;
00080         }
00081 
00082         if (pid == 0)
00083         {
00084                 sigaction(SIGINT, &intr, NULL);
00085                 sigaction(SIGQUIT, &quit, NULL);
00086 
00087                 execvp(command, args);
00088                 G_warning(_("unable to execute command"));
00089                 _exit(127);
00090         }
00091         else
00092         {
00093                 pid_t n;
00094 
00095                 do n = waitpid(pid, &status, 0);
00096                 while (n == (pid_t) -1 && errno == EINTR);
00097 
00098                 if (n != pid)
00099                         status = -1;
00100         }
00101 
00102 error_4:
00103         sigprocmask(SIG_SETMASK, &oldmask, NULL);
00104 error_3:
00105         sigaction(SIGQUIT, &quit, NULL);
00106 error_2:
00107         sigaction(SIGINT, &intr, NULL);
00108 error_1:
00109         return status;
00110 }
00111 
00112 /****************************************************************
00113  * G_spawn_ex(char *command, ...)
00114  *
00115  * This is a more advanced version of G_spawn().
00116  *
00117  ****************************************************************/
00118 
00119 struct redirect
00120 {
00121         int dst_fd;
00122         int src_fd;
00123         char *file;
00124         int mode;
00125 };
00126 
00127 struct signal
00128 {
00129         int which;
00130         int action;
00131         int signum;
00132         int valid;
00133         struct sigaction old_act;
00134         sigset_t old_mask;
00135 };
00136 
00137 struct binding
00138 {
00139         char *var;
00140         char *val;
00141 };
00142 
00143 static int undo_signals(struct signal *signals, int num_signals, int which)
00144 {
00145         int error = 0;
00146         int i;
00147 
00148         for (i = num_signals-1; i >= 0; i--)
00149         {
00150                 struct signal *s = &signals[i];
00151 
00152                 if (s->which != which)
00153                         continue;
00154 
00155                 if (!s->valid)
00156                         continue;
00157 
00158                 switch (s->action)
00159                 {
00160                 case SSA_IGNORE:
00161                 case SSA_DEFAULT:
00162                         if (sigaction(s->signum, &s->old_act, NULL) < 0)
00163                         {
00164                                 G_warning(_("G_spawn: unable to restore signal %d"), s->signum);
00165                                 error = 1;
00166                         }
00167                         break;
00168                 case SSA_BLOCK:
00169                 case SSA_UNBLOCK:
00170                         if (sigprocmask(SIG_UNBLOCK, &s->old_mask, NULL) < 0)
00171                         {
00172                                 G_warning(_("G_spawn: unable to restore signal %d"), s->signum);
00173                                 error = 1;
00174                         }
00175                         break;
00176                 }
00177         }
00178 
00179         return !error;
00180 }
00181 
00182 static int do_signals(struct signal *signals, int num_signals, int which)
00183 {
00184         struct sigaction act;
00185         sigset_t mask;
00186         int error = 0;
00187         int i;
00188 
00189         sigemptyset(&act.sa_mask);
00190         act.sa_flags = SA_RESTART;
00191 
00192         for (i = 0; i < num_signals; i++)
00193         {
00194                 struct signal *s = &signals[i];
00195 
00196                 if (s->which != which)
00197                         continue;
00198 
00199                 switch (s->action)
00200                 {
00201                 case SSA_IGNORE:
00202                         act.sa_handler = SIG_IGN;
00203                         if (sigaction(s->signum, &act, &s->old_act) < 0)
00204                         {
00205                                 G_warning(_("G_spawn: unable to reset signal %d"), s->signum);
00206                                 error = 1;
00207                         }
00208                         else
00209                                 s->valid = 1;
00210                         break;
00211                 case SSA_DEFAULT:
00212                         act.sa_handler = SIG_DFL;
00213                         if (sigaction(s->signum, &act, &s->old_act) < 0)
00214                         {
00215                                 G_warning(_("G_spawn: unable to ignore signal %d"), s->signum);
00216                                 error = 1;
00217                         }
00218                         else
00219                                 s->valid = 1;
00220                         break;
00221                 case SSA_BLOCK:
00222                         sigemptyset(&mask);
00223                         sigaddset(&mask, s->signum);
00224                         if (sigprocmask(SIG_BLOCK, &mask, &s->old_mask) < 0)
00225                         {
00226                                 G_warning(_("G_spawn: unable to block signal %d"), s->signum);
00227                                 error = 1;
00228                         }
00229                         break;
00230                 case SSA_UNBLOCK:
00231                         sigemptyset(&mask);
00232                         sigaddset(&mask, s->signum);
00233                         if (sigprocmask(SIG_UNBLOCK, &mask, &s->old_mask) < 0)
00234                         {
00235                                 G_warning(_("G_spawn: unable to unblock signal %d"), s->signum);
00236                                 error = 1;
00237                         }
00238                         else
00239                                 s->valid = 1;
00240                         break;
00241                 }
00242         }
00243 
00244         return !error;
00245 }
00246 
00247 static void do_redirects(struct redirect *redirects, int num_redirects)
00248 {
00249         int i;
00250 
00251         for (i = 0; i < num_redirects; i++)
00252         {
00253                 struct redirect *r = &redirects[i];
00254 
00255                 if (r->file)
00256                 {
00257                         r->src_fd = open(r->file, r->mode, 0666);
00258 
00259                         if (r->src_fd < 0)
00260                         {
00261                                 G_warning(_("G_spawn: unable to open file %s"), r->file);
00262                                 _exit(127);
00263                         }
00264 
00265                         if (dup2(r->src_fd, r->dst_fd) < 0)
00266                         {
00267                                 G_warning(_("G_spawn: unable to duplicate descriptor %d to %d"), r->src_fd, r->dst_fd);
00268                                 _exit(127);
00269                         }
00270 
00271                         close(r->src_fd);
00272                 }
00273                 else if (r->src_fd >= 0)
00274                 {
00275                         if (dup2(r->src_fd, r->dst_fd) < 0)
00276                         {
00277                                 G_warning(_("G_spawn: unable to duplicate descriptor %d to %d"), r->src_fd, r->dst_fd);
00278                                 _exit(127);
00279                         }
00280                 }
00281                 else
00282                         close(r->dst_fd);
00283         }
00284 }
00285 
00286 static void do_bindings(struct binding *bindings, int num_bindings)
00287 {
00288         int i;
00289 
00290         for (i = 0; i < num_bindings; i++)
00291         {
00292                 struct binding *b = &bindings[i];
00293                 char *str;
00294 
00295                 str = G_malloc(strlen(b->var) + strlen(b->val) + 2);
00296                 sprintf(str, "%s=%s", b->var, b->val);
00297                 putenv(str);
00298         }
00299 }
00300 
00301 int G_spawn_ex(char *command, ...)
00302 {
00303         char *args[MAX_ARGS];
00304         int num_args = 0;
00305         struct redirect redirects[MAX_REDIRECTS];
00306         int num_redirects = 0;
00307         struct signal signals[MAX_SIGNALS];
00308         int num_signals = 0;
00309         struct binding bindings[MAX_BINDINGS];
00310         int num_bindings = 0;
00311         int background = 0;
00312         char *directory = NULL;
00313         va_list va;
00314         char *var, *val;
00315         int status = -1;
00316         pid_t pid;
00317 
00318         args[num_args++] = command;
00319 
00320         va_start(va, command);
00321 
00322         for (;;)
00323         {
00324                 char *arg = va_arg(va, char *);
00325 
00326                 switch ((int) arg)
00327                 {
00328                 case 0:
00329                         args[num_args++] = NULL;
00330                         break;
00331                 case ((int) SF_REDIRECT_FILE):
00332                         redirects[num_redirects].dst_fd = va_arg(va, int);
00333                         redirects[num_redirects].src_fd = -1;
00334                         redirects[num_redirects].mode = va_arg(va, int);
00335                         redirects[num_redirects].file = va_arg(va, char *);
00336                         num_redirects++;
00337                         break;
00338                 case ((int) SF_REDIRECT_DESCRIPTOR):
00339                         redirects[num_redirects].dst_fd = va_arg(va, int);
00340                         redirects[num_redirects].src_fd = va_arg(va, int);
00341                         redirects[num_redirects].file = NULL;
00342                         num_redirects++;
00343                         break;
00344                 case ((int) SF_CLOSE_DESCRIPTOR):
00345                         redirects[num_redirects].dst_fd = va_arg(va, int);
00346                         redirects[num_redirects].src_fd = -1;
00347                         redirects[num_redirects].file = NULL;
00348                         num_redirects++;
00349                         break;
00350                 case ((int) SF_SIGNAL):
00351                         signals[num_signals].which = va_arg(va, int);
00352                         signals[num_signals].action = va_arg(va, int);
00353                         signals[num_signals].signum = va_arg(va, int);
00354                         signals[num_signals].valid = 0;;
00355                         num_signals++;
00356                         break;
00357                 case ((int) SF_VARIABLE):
00358                         var = va_arg(va, char *);
00359                         val = getenv(var);
00360                         args[num_args++] = val ? val : "";
00361                         break;
00362                 case ((int) SF_BINDING):
00363                         bindings[num_bindings].var = va_arg(va, char *);
00364                         bindings[num_bindings].val = va_arg(va, char *);
00365                         num_bindings++;
00366                         break;
00367                 case ((int) SF_BACKGROUND):
00368                         background = 1;
00369                         break;
00370                 case ((int) SF_DIRECTORY):
00371                         directory = va_arg(va, char *);
00372                         break;
00373                 default:
00374                         args[num_args++] = arg;
00375                         break;
00376                 }
00377 
00378                 if (!arg)
00379                         break;
00380         }
00381 
00382         va_end(va);
00383 
00384         if (!do_signals(signals, num_signals, SST_PRE))
00385                 goto error_1;
00386 
00387         pid = fork();
00388         if (pid < 0)
00389         {
00390                 G_warning(_("unable to create a new process"));
00391                 goto error_2;
00392         }
00393 
00394         if (pid == 0)
00395         {
00396                 if (!undo_signals(signals, num_signals, SST_PRE))
00397                         _exit(127);
00398 
00399                 if (!do_signals(signals, num_signals, SST_CHILD))
00400                         _exit(127);
00401 
00402                 if (directory)
00403                         if (chdir(directory) < 0)
00404                         {
00405                                 G_warning(_("unable to change directory to %s"), directory);
00406                                 _exit(127);
00407                         }
00408 
00409                 do_redirects(redirects, num_redirects);
00410                 do_bindings(bindings, num_bindings);
00411 
00412                 execvp(command, args);
00413                 G_warning(_("unable to execute command"));
00414                 _exit(127);
00415         }
00416 
00417         do_signals(signals, num_signals, SST_POST);
00418 
00419         if (background)
00420                 status = (int) pid;
00421         else
00422         {
00423                 pid_t n;
00424 
00425                 do n = waitpid(pid, &status, 0);
00426                 while (n == (pid_t) -1 && errno == EINTR);
00427 
00428                 if (n != pid)
00429                         status = -1;
00430         }
00431 
00432         undo_signals(signals, num_signals, SST_POST);
00433 error_2:
00434         undo_signals(signals, num_signals, SST_PRE);
00435 error_1:
00436 
00437         return status;
00438 }
00439 

Generated on Mon Jan 1 19:49:26 2007 for GRASS by  doxygen 1.5.1