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

Generated on Wed Dec 19 14:59:06 2007 for GRASS by  doxygen 1.5.4