Greetings all.

Attached is a replacement patch for the one in the previous email, with a minor fix. This replacement fixes a bug in the --sleep switch, and a similar bug in the --signal switch. (The switches were producing an 'unknown option' message after completing their actions. This probably didn't matter for the --signal switch, as the flow usually wouldn't reach that point, but it did matter for the --sleep switch)

--Andrew Black

Andrew Black wrote:
Greetings all

One thing that is needed is a method of testing the exec utility. This patch provides a step towards this goal by adding several command line switches. The majority of these switches allow the utility to function as a test target which will produce certain (controlled) behavior. The behavior needed includes exiting with a particular status or signal, masking (ignoring) a particular signal, and sleeping a certain amount of time.

The -v switch increases the value of the verbosity counter variable
The -q switch sets the value of the verbosity counter variable to 0
The --exit switch causes the utility to exit with the value specified. The --sleep switch causes the utility to sleep for the number of seconds specified. The --signal switch causes the utility to send itself the signal name/number specified. The --ignore switch causes the utility to ignore the signal name/number specified.

--Andrew Black

Log:
2006-08-01 Andrew Black <[EMAIL PROTECTED]>
    * exec.h (get_signo): Define macro for signal name to number conversion
    * exec.cpp (get_signo): Implementation
* exec.cpp (signal_names): moved lookup table out of get_signame to file scope
    * cmdopt.h (verbose): Define global
    * cmdopt.cpp (verbose): Instantiate global
* cmdopt.cpp (get_short_val, get_long_val, bad_option): Add helper functions for option parsing * cmdopt.cpp (eval_options): Use new functions, add -v, -q, --exit, --sleep, --signal, --ignore switches
Index: exec.cpp
===================================================================
--- exec.cpp	(revision 427578)
+++ exec.cpp	(working copy)
@@ -58,192 +58,251 @@
 static int alarm_timeout;
 
 /**
-   Translates a signal number into a human understandable name
-
-   @param signo a signal number
-   @returns the human understandable name for the signal (minus the SIG 
-   prefix), or "#n" if the the name for the number is unknown to the 
-   function, where n is signo
+    Utility macro to generate a signal number/name pair.
+    
+    @parm val 'short' signal name (no leading SIG) to generate pair for.
+    @see signal_names []
 */
-const char* 
-get_signame (int signo)
-{
-    static const struct {
-        int         val;
-        const char* str;
-    } names [] = {
-
 #undef SIGNAL
 #define SIGNAL(val)   { SIG ## val, #val }
 
+/**
+    Signal name/number translation table.
+    
+    This table is populated using the SIGNAL helper macro to translate system SIG* macro values into name/value pairs.
+    
+    @see SIGNAL ()
+*/
+static const struct {
+    int         val; /**< Signal value for lookup pair */
+    const char* str; /**< Signal name for lookup pair */
+} signal_names [] = {
 #ifdef SIGABRT
-        SIGNAL (ABRT),
+    SIGNAL (ABRT),
 #endif   /* SIGABRT */
 #ifdef SIGALRM
-        SIGNAL (ALRM),
+    SIGNAL (ALRM),
 #endif   /* SIGALRM */
 #ifdef SIGBUS
-        SIGNAL (BUS),
+    SIGNAL (BUS),
 #endif   /* SIGBUS */
 #ifdef SIGCANCEL
-        SIGNAL (CANCEL),
+    SIGNAL (CANCEL),
 #endif   /* SIGCANCEL */
 #ifdef SIGCHLD
-        SIGNAL (CHLD),
+    SIGNAL (CHLD),
 #endif   /* SIGCHLD */
 #ifdef SIGCKPT
-        SIGNAL (CKPT),
+    SIGNAL (CKPT),
 #endif   /* SIGCKPT */
 #ifdef SIGCLD
-        SIGNAL (CLD),
+    SIGNAL (CLD),
 #endif   /* SIGCLD */
 #ifdef SIGCONT
-        SIGNAL (CONT),
+    SIGNAL (CONT),
 #endif   /* SIGCONT */
 #ifdef SIGDIL
-        SIGNAL (DIL),
+    SIGNAL (DIL),
 #endif   /* SIGDIL */
 #ifdef SIGEMT
-        SIGNAL (EMT),
+    SIGNAL (EMT),
 #endif   /* SIGEMT */
 #ifdef SIGFPE
-        SIGNAL (FPE),
+    SIGNAL (FPE),
 #endif   /* SIGFPE */
 #ifdef SIGFREEZE
-        SIGNAL (FREEZE),
+    SIGNAL (FREEZE),
 #endif   /* SIGFREEZE */
 #ifdef SIGGFAULT
-        SIGNAL (GFAULT),
+    SIGNAL (GFAULT),
 #endif   /* SIGGFAULT */
 #ifdef SIGHUP
-        SIGNAL (HUP),
+    SIGNAL (HUP),
 #endif   /* SIGHUP */
 #ifdef SIGILL
-        SIGNAL (ILL),
+    SIGNAL (ILL),
 #endif   /* SIGILL */
 #ifdef SIGINFO
-        SIGNAL (INFO),
+    SIGNAL (INFO),
 #endif   /* SIGINFO */
 #ifdef SIGINT
-        SIGNAL (INT),
+    SIGNAL (INT),
 #endif   /* SIGINT */
 #ifdef SIGIO
-        SIGNAL (IO),
+    SIGNAL (IO),
 #endif   /* SIGIO */
 #ifdef SIGIOT
-        SIGNAL (IOT),
+    SIGNAL (IOT),
 #endif   /* SIGIOT */
 #ifdef SIGK32
-        SIGNAL (K32),
+    SIGNAL (K32),
 #endif   /* SIGK32 */
 #ifdef SIGKILL
-        SIGNAL (KILL),
+    SIGNAL (KILL),
 #endif   /* SIGKILL */
 #ifdef SIGLOST
-        SIGNAL (LOST),
+    SIGNAL (LOST),
 #endif   /* SIGLOST */
 #ifdef SIGLWP
-        SIGNAL (LWP),
+    SIGNAL (LWP),
 #endif   /* SIGLWP */
 #ifdef SIGPIPE
-        SIGNAL (PIPE),
+    SIGNAL (PIPE),
 #endif   /* SIGPIPE */
 #ifdef SIGPOLL
-        SIGNAL (POLL),
+    SIGNAL (POLL),
 #endif   /* SIGPOLL */
 #ifdef SIGPROF
-        SIGNAL (PROF),
+    SIGNAL (PROF),
 #endif   /* SIGPROF */
 #ifdef SIGPTINTR
-        SIGNAL (PTINTR),
+    SIGNAL (PTINTR),
 #endif   /* SIGPTINTR */
 #ifdef SIGPTRESCHED
-        SIGNAL (PTRESCHED),
+    SIGNAL (PTRESCHED),
 #endif   /* SIGPTRESCHED */
 #ifdef SIGPWR
-        SIGNAL (PWR),
+    SIGNAL (PWR),
 #endif   /* SIGPWR */
 #ifdef SIGQUIT
-        SIGNAL (QUIT),
+    SIGNAL (QUIT),
 #endif   /* SIGQUIT */
 #ifdef SIGRESTART
-        SIGNAL (RESTART),
+    SIGNAL (RESTART),
 #endif   /* SIGRESTART */
 #ifdef SIGRESV
-        SIGNAL (RESV),
+    SIGNAL (RESV),
 #endif   /* SIGRESV */
 #ifdef SIGSEGV
-        SIGNAL (SEGV),
+    SIGNAL (SEGV),
 #endif   /* SIGSEGV */
 #ifdef SIGSTKFLT
-        SIGNAL (STKFLT),
+    SIGNAL (STKFLT),
 #endif   /* SIGSTKFLT */
 #ifdef SIGSTOP
-        SIGNAL (STOP),
+    SIGNAL (STOP),
 #endif   /* SIGSTOP */
 #ifdef SIGSYS
-        SIGNAL (SYS),
+    SIGNAL (SYS),
 #endif   /* SIGSYS */
 #ifdef SIGTERM
-        SIGNAL (TERM),
+    SIGNAL (TERM),
 #endif   /* SIGTERM */
 #ifdef SIGTHAW
-        SIGNAL (THAW),
+    SIGNAL (THAW),
 #endif   /* SIGTHAW */
 #ifdef SIGTRAP
-        SIGNAL (TRAP),
+    SIGNAL (TRAP),
 #endif   /* SIGTRAP */
 #ifdef SIGTSTP
-        SIGNAL (TSTP),
+    SIGNAL (TSTP),
 #endif   /* SIGTSTP */
 #ifdef SIGTTIN
-        SIGNAL (TTIN),
+    SIGNAL (TTIN),
 #endif   /* SIGTTIN */
 #ifdef SIGTTOU
-        SIGNAL (TTOU),
+    SIGNAL (TTOU),
 #endif   /* SIGTTOU */
 #ifdef SIGUNUSED
-        SIGNAL (UNUSED),
+    SIGNAL (UNUSED),
 #endif   /* SIGUNUSED */
 #ifdef SIGURG
-        SIGNAL (URG),
+    SIGNAL (URG),
 #endif   /* SIGURG */
 #ifdef SIGUSR1
-        SIGNAL (USR1),
+    SIGNAL (USR1),
 #endif   /* SIGUSR1 */
 #ifdef SIGUSR2
-        SIGNAL (USR2),
+    SIGNAL (USR2),
 #endif   /* SIGUSR2 */
 #ifdef SIGVTALRM
-        SIGNAL (VTALRM),
+    SIGNAL (VTALRM),
 #endif   /* SIGVTALRM */
 #ifdef SIGWAITING
-        SIGNAL (WAITING),
+    SIGNAL (WAITING),
 #endif   /* SIGWAITING */
 #ifdef SIGWINCH
-        SIGNAL (WINCH),
+    SIGNAL (WINCH),
 #endif   /* SIGWINCH */
 #ifdef SIGWINDOW
-        SIGNAL (WINDOW),
+    SIGNAL (WINDOW),
 #endif   /* SIGWINDOW */
 #ifdef SIGXCPU
-        SIGNAL (XCPU),
+    SIGNAL (XCPU),
 #endif   /* SIGXCPU */
 #ifdef SIGXFSZ
-        SIGNAL (XFSZ),
+    SIGNAL (XFSZ),
 #endif   /* SIGXFSZ */
 #ifdef SIGXRES
-        SIGNAL (XRES),
+    SIGNAL (XRES),
 #endif   /* SIGXRES */
-        { -1, 0 }
-    };
+    { -1, 0 }
+};
 
+/**
+   Index size for the signal_names array.
+   
+   This array is semi-redundant, as the same check could be made looking for a null pointer in the str element of the structure.
+*/
+static const size_t name_count = sizeof signal_names / sizeof *signal_names;
+
+/**
+   Translates a human understandable signal name into a number usable by kill ().
+
+   This method understands several formats for signal names.  They are as follows:
+   - SIG#n
+   - SIGn
+   - SIGFOO
+   - #n
+   - n
+   - FOO
+   In this list, n denotes a number and FOO denotes a short signal name.
+
+   @param signame a signal name to decode
+   @returns the signal number or -1 if a number couldn't be determined
+   @see signal_names []
+*/
+const int
+get_signo (char* signame)
+{
     size_t i;
+
+    if ('S' == signame [0] && 'I' == signame [1] && 'G' == signame [2])
+        signame += 3;
+    
+    if ('#' == signame [0])
+        ++signame;
+    
+    if ('0' <= signame [0] && '9' >= signame [0])
+        return atoi (signame);
+
+    for (i = 0; i < name_count; ++i) {
+        if (0 == strcmp (signal_names [i].str, signame)) {
+            return signal_names [i].val;
+        }
+    }
+    
+    return -1;
+}
+
+/**
+   Translates a signal number into a human understandable name
+
+   @param signo a signal number
+   @returns the human understandable name for the signal (minus the SIG 
+   prefix), or "#n" if the the name for the number is unknown to the 
+   function, where n is signo
+   @see signal_names []
+*/
+const char* 
+get_signame (int signo)
+{
+    size_t i;
     static char def [16];
 
-    for (i = 0; i < sizeof names / sizeof *names; ++i) {
-        if (names [i].val == signo) {
-            return names [i].str;
+    for (i = 0; i < name_count; ++i) {
+        if (signal_names [i].val == signo) {
+            return signal_names [i].str;
         }
     }
 
Index: exec.h
===================================================================
--- exec.h	(revision 427578)
+++ exec.h	(working copy)
@@ -32,6 +32,8 @@
     int killed;
 };
 
+const int get_signo (char* const signame);
+
 const char* get_signame (int signo);
 
 struct exec_attrs exec_file (char** argv);
Index: cmdopt.cpp
===================================================================
--- cmdopt.cpp	(revision 427578)
+++ cmdopt.cpp	(working copy)
@@ -26,16 +26,20 @@
 
 #include <assert.h>
 #include <ctype.h> /* for isspace */
+#include <signal.h> /* for kill, SIG_IGN */
 #include <stdio.h> /* for *printf, fputs */
 #include <stdlib.h> /* for atoi, exit */
 #include <string.h> /* for str* */
+#include <unistd.h> /* for getpid */
 
+#include "exec.h"
 #include "util.h"
 
 #include "cmdopt.h"
 
 int timeout = 10; /**< Child process timeout.  Default 10. */
 int compat = 0; /**< Test compatability mode switch.  Defaults to 0 (off). */
+unsigned verbose = 0;
 const char* exe_opts = ""; /**< Global command line switches for child 
                                 processes. */
 const char* in_root = ""; /**< Root directory for input/reference files. */
@@ -58,17 +62,80 @@
            "processed after termination.  If\n  execution takes longer "
            "than a certain (configurable) period of time, the\n  process is "
            "killed\n\n", stderr);
-    fputs ("    -d dir      Root directory for output reference files\n"
-           "    -h, -?      Display usage information and exit\n"
-           "    -t seconds  Watchdog timeout before killing target (default is "
-"10 \n                seconds)\n"
-           "    -x opts     Command line options to pass to targets\n"
-           "    --          Terminate option processing and treat all "
-           "following arguments\n                as targets\n", stderr);
+    fputs ("    -d dir       Root directory for output reference files\n"
+           "    -h, -?       Display usage information and exit\n"
+           "    -t seconds   Watchdog timeout before killing target (default "
+           "is 10 \n                 seconds)\n"
+           "    -q           Set verbosity level to 0 (default)\n"
+           "    -v           Increase verbosity of output\n"
+           "    -x opts      Command line options to pass to targets\n", 
+           stderr);
+    fputs ("    --           Terminate option processing and treat all "
+           "following arguments\n                 as targets\n"
+           "    --compat     Use compatability mode test output parsing\n"
+           "    --nocompat   Use standard test output parsing (default)\n"
+           "    --exit=val   Exit (now) with a return code of val\n"
+           "    --sleep=sec  sleep() (now) for sec seconds\n"
+           "    --signal=sig Send self signal sig (now)\n"
+           "    --ignore=sig Ignore signal sig\n", stderr);
+    fputs ("\n"
+           "  All short (single dash) options must be specified seperately.\n"
+           "  If a short option takes a value, it may either be provided like"
+           "\n  '-sval' or  '-s val'\n"
+           "  If a long option take a value, it may either be provided like\n"
+           "  '--option=value' or '--option value'\n", stderr);
+          
     exit (status);
 }
 
 /**
+    Helper function to read the value for a short option
+    
+    @param argv argument array
+    @param idx reference to index for option
+*/
+static char*
+get_short_val (char* const* argv, int* idx)
+{
+    if ('\0' == argv [*idx][2])
+        return argv [++(*idx)];
+    else
+        return argv [*idx] + 2;
+}
+
+/**
+    Helper function to read the value for a long option
+    
+    @param argv argument array
+    @param idx reference to index for option
+    @param offset length of option name (including leading --)
+*/
+static char*
+get_long_val (char* const * argv, int* idx, unsigned offset)
+{
+    if ('\0' == argv [*idx][offset])
+        return argv [++(*idx)];
+    else if ('=' == argv [*idx][offset])
+        return argv [*idx] + offset + 1;
+    else
+        return (char*)0;
+}
+
+/**
+    Helper function to produce 'Unknown option' error message.
+    
+    Terminates via show_usage.
+    
+    @param opt name of option encountered
+*/
+static void
+bad_option (char* const opt)
+{
+    printf ("Unknown option: %s\n", opt);
+    show_usage (1);
+}
+
+/**
    Parses command line arguments for switches and options.
 
    @param argc number of command line arguments
@@ -101,29 +168,21 @@
             ++i; /* Ignore -r option (makefile compat) */
             break;
         case 't':
-            if ('\0' == argv [i][2])
-                val = argv [++i];
-            else
-                val = argv [i] + 2;
-
+            val = get_short_val (argv, &i);
             timeout = atoi (val);
             break;
         case 'd':
-            if ('\0' == argv [i][2])
-                val = argv [++i];
-            else
-                val = argv [i] + 2;
-
-            in_root = val;
+            in_root = get_short_val (argv, &i);
             break;
         case 'x':
-            if ('\0' == argv [i][2])
-                val = argv [++i];
-            else
-                val = argv [i] + 2;
-
-            exe_opts = val;
+            exe_opts = get_short_val (argv, &i);
             break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'q':
+            verbose = 0;
+            break;
         case '-':
         {
             const size_t arglen = strlen (argv [i]);
@@ -132,19 +191,73 @@
             if ('\0' == argv [i][2])
                 return i+1;
 
-            if (8 == arglen && 0 == memcmp ("--compat", argv [i], 8)) {
+            if (8 == arglen && 0 == memcmp ("--compat\0", argv [i], 9)) {
                 compat = 1;
                 break;
             }
-            else if (10 == arglen && 0 == memcmp ("--nocompat", argv [i], 10)) {
+            else if (10 == arglen && 0 == memcmp ("--nocompat\0", argv [i], 
+                                                  11)) {
                 compat = 0;
                 break;
             }
+            else if (6 <= arglen && 0 == memcmp ("--exit", argv [i], 6)) {
+                val = get_long_val (argv, &i, 6);
+                if (val)
+                    exit (atoi (val));
+                else
+                    bad_option (argv [i]);
+            }
+            else if (7 <= arglen && 0 == memcmp ("--sleep", argv [i], 7)) {
+                val = get_long_val (argv, &i, 7);
+                if (val) {
+                    sleep (atoi (val));
+                    break;
+                }
+                else
+                    bad_option (argv [i]);
+            }
+            else if (8 <= arglen && 0 == memcmp ("--signal", argv [i], 8)) {
+                val = get_long_val (argv, &i, 8);
+                if (val) {
+                    int sig = get_signo (val);
+                    if (0 > sig)
+                        terminate (1, "Unknown signal name for --signal: "
+                                   "%s\n", val);
+                    
+                    /* Ignore kill errors (what should we do with them?) */
+                    (void)kill (getpid (), sig);
+
+                    /* Not certain what we should do if we don't terminate by 
+                       signal */
+                    break;
+                }
+                else
+                    bad_option (argv [i]);
+            }
+            else if (8 <= arglen && 0 == memcmp ("--ignore", argv [i], 8)) {
+                val = get_long_val (argv, &i, 8);
+                if (val) {
+                    struct sigaction act;
+                    int sig = get_signo (val);
+                    if (0 > sig)
+                        terminate (1, "Unknown signal name for --ignore: "
+                                   "%s\n", val);
+                    
+                    memset (&act, 0, sizeof act);
+                    act.sa_handler = SIG_IGN;
+                    (void)sigaction (sig, &act, 0);
+                    /* Ignore sigaction errors (what should we do with them?) 
+                     */
+                    break;
+                }
+                else
+                    bad_option (argv [i]);
+            }
+            else
+                bad_option (argv [i]);
         }
-            /* Intentionally falling through */
         default:
-            fprintf (stderr, "Unknown option: %s\n", argv [i]);
-            show_usage (1);
+            bad_option (argv [i]);
         }
     }
 
Index: cmdopt.h
===================================================================
--- cmdopt.h	(revision 427578)
+++ cmdopt.h	(working copy)
@@ -29,6 +29,7 @@
 
 extern int timeout;
 extern int compat;
+extern unsigned verbose; /**< Verbose output mode switch.  Defaults to 0 (off) */
 extern const char* exe_opts;
 extern const char* in_root;
 extern const char* exe_name;

Reply via email to