Index: /trunk/ippScripts/Build.PL
===================================================================
--- /trunk/ippScripts/Build.PL	(revision 24000)
+++ /trunk/ippScripts/Build.PL	(revision 24001)
@@ -83,4 +83,5 @@
         scripts/dist_advancerun.pl
         scripts/dist_make_fileset.pl
+        scripts/dist_queue_runs.pl
         scripts/receive_source.pl
         scripts/receive_fileset.pl
Index: /trunk/ippScripts/scripts/dist_queue_runs.pl
===================================================================
--- /trunk/ippScripts/scripts/dist_queue_runs.pl	(revision 24001)
+++ /trunk/ippScripts/scripts/dist_queue_runs.pl	(revision 24001)
@@ -0,0 +1,111 @@
+#!/usr/bin/env perl
+
+use Carp;
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use File::Temp qw( tempfile );
+use File::Basename qw( basename );
+use Digest::MD5::File qw( file_md5_hex );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::List qw( parse_md_list );
+
+use PS::IPP::Config 1.01 qw( :standard );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+
+# Look for programs we need
+my $missing_tools;
+my $disttool   = can_run('disttool') or (warn "Can't find disttool" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+# Parse the command-line arguments
+my ($stage, $stage_limit, $dist_root, $no_magic);
+my ($dbname, $save_temps, $verbose, $no_update, $logfile);
+
+GetOptions(
+           'dist_root=s'    => \$dist_root,  # root of distribution work area
+           'stage=s'        => \$stage,      # stage to queue
+           'stage_limit=s'  => \$stage_limit,# maximum number of runs queued for each stage
+           'no_magic'       => \$no_magic,   # queue runs without requiring magic
+           'dbname=s'       => \$dbname,     # Database name
+           'verbose'        => \$verbose,    # Print stuff?
+           'no-update'      => \$no_update,  # Don't update the database
+           'save-temps'     => \$save_temps, # Save temporary files?
+           'logfile=s'      => \$logfile,
+           ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+if (0) {
+pod2usage( -msg => "Required options: --stage",
+           -exitval => 3) unless
+    defined $stage;
+}
+
+$ipprc->redirect_output($logfile) if $logfile;
+
+if (!$dist_root) {
+    $dist_root = metadataLookupStr($ipprc->{_siteConfig}, "DISTRIBUTION_ROOT");
+    &my_die("failed to find DISTRIBUTION_ROOT in site configuration", $PS_EXIT_CONFIG_ERROR) if !$dist_root;
+}
+
+my ($day, $month, $year) = (localtime)[3,4,5];
+my $datestr = sprintf "%04d%02d%02d", $year+1900, $month + 1, $day;
+
+my $workdir = "$dist_root/$datestr";
+
+print "workdir is $workdir\n";
+
+# if stage is not supplied as an argument, loop over all stages
+my @stages;
+if ($stage) {
+    push @stages, $stage;
+} else {
+    @stages = qw( raw chip camera fake warp diff stack);
+}
+
+foreach my $stage (@stages) {
+    my $command = "$disttool -definebyquery -stage $stage -workdir $workdir";
+    $command .= " -no_magic" if $no_magic;
+    $command .= " -dry_run" if $no_update;
+    $command .= " -limit $stage_limit" if $stage_limit;
+    $command .= " -dbname $dbname" if defined $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform $command error_code: $error_code", $error_code);
+    }
+    # display the output from the command
+    print STDERR join "", @$stdout_buf if $verbose;
+}
+
+exit 0;
+
+sub my_die {
+    my $msg = shift;
+    my $fault = shift;
+
+    print STDERR "$msg\n";
+
+    exit $fault;
+}
+
+__END__
Index: /trunk/ippTasks/dist.pro
===================================================================
--- /trunk/ippTasks/dist.pro	(revision 24000)
+++ /trunk/ippTasks/dist.pro	(revision 24001)
@@ -14,4 +14,5 @@
 $distToProcess_DB = 0
 $distToAdvance_DB = 0
+$distQueue_DB = 0
 
 ### Check status of tasks
@@ -293,2 +294,61 @@
 end
 
+task	       dist.queueruns
+#  host         local
+
+  periods      -poll $RUNPOLL
+  periods      -exec 10
+  periods      -timeout 45
+  npending     1
+
+#  stdout $LOGDIR/dist.queuruns
+#  stderr $LOGDIR/dist.queueruns
+
+  task.exec
+    $MYARGS = ""
+    # assume that we need magic unless we are running simtest
+    if ($?SIMTEST_CAMERA != 0)
+        $MYARGS = --no_magic
+    end
+
+    $run = dist_queue_runs.pl $MYARGS --stage_limit 16 --logfile $LOGDIR/dist.queueruns
+
+    if ($DB:n == 0)
+       $DBNAME = DEFAULT
+    else
+      # save the DB name for add_standard_args
+      $DBNAME =  $DB:$distQueue_DB
+      $distQueue_DB ++
+      if ($distQueue_DB >= $DB:n) set distQueue_DB = 0
+    end
+
+    host anyhost
+
+    add_standard_args run
+
+    # create the command line
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+  end
+
+  task.exit     $EXIT_SUCCESS
+    # nothing to do
+  end
+
+  # default exit status
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+  end
+
+  # operation timed out?
+  task.exit    crash
+    showcommand crash
+  end
+end
Index: /trunk/ippTools/src/disttool.c
===================================================================
--- /trunk/ippTools/src/disttool.c	(revision 24000)
+++ /trunk/ippTools/src/disttool.c	(revision 24001)
@@ -47,4 +47,17 @@
 static bool pendingdestMode(pxConfig *config);
 
+static bool definetargetMode(pxConfig *config);
+static bool updatetargetMode(pxConfig *config);
+static bool listtargetMode(pxConfig *config);
+
+static bool definedsproductMode(pxConfig *config);
+static bool updatedsproductMode(pxConfig *config);
+
+static bool definedestinationMode(pxConfig *config);
+static bool updatedestinationMode(pxConfig *config);
+
+static bool defineinterestMode(pxConfig *config);
+static bool updateinterestMode(pxConfig *config);
+
 # define MODECASE(caseName, func) \
     case caseName: \
@@ -81,4 +94,13 @@
         MODECASE(DISTTOOL_MODE_REVERTRCRUN, revertrcrunMode);
         MODECASE(DISTTOOL_MODE_PENDINGDEST, pendingdestMode);
+        MODECASE(DISTTOOL_MODE_DEFINETARGET, definetargetMode);
+        MODECASE(DISTTOOL_MODE_UPDATETARGET, updatetargetMode);
+        MODECASE(DISTTOOL_MODE_LISTTARGET, listtargetMode);
+        MODECASE(DISTTOOL_MODE_DEFINEDSPRODUCT, definedsproductMode);
+        MODECASE(DISTTOOL_MODE_UPDATEDSPRODUCT, updatedsproductMode);
+        MODECASE(DISTTOOL_MODE_DEFINEDESTINATION, definedestinationMode);
+        MODECASE(DISTTOOL_MODE_UPDATEDESTINATION, updatedestinationMode);
+        MODECASE(DISTTOOL_MODE_DEFINEINTEREST, defineinterestMode);
+        MODECASE(DISTTOOL_MODE_UPDATEINTEREST, updateinterestMode);
         default:
             psAbort("invalid option (this should not happen)");
@@ -157,4 +179,5 @@
     PXOPT_LOOKUP_STR(set_label, config->args, "-set_label", false, false);
     PXOPT_LOOKUP_S64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
     
     PXOPT_LOOKUP_BOOL(dry_run, config->args, "-dry_run", false);
@@ -323,20 +346,26 @@
     }
     if (!psArrayLength(output)) {
-        psTrace("warptool", PS_LOG_INFO, "no rows found");
+        psTrace("disttool", PS_LOG_INFO, "no rows found");
         psFree(output);
         return true;
     }
 
-    // XXX Remove this debugging output at some point
-    if (!ippdbPrintMetadatas(stdout, output, "newdistRuns", true)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to print array");
-        psFree(output);
-        return false;
-    }
+
     if (dry_run) {
+        if (!ippdbPrintMetadatas(stdout, output, "newdistRuns", true)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
         psFree(output);
         return true;
     }
 
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psArray *list = psArrayAllocEmpty(limit);
     for (long i=0; i < psArrayLength(output); i++) {
         psMetadata *md = output->data[i];
@@ -350,5 +379,5 @@
         psStringAppend(&outroot, "%s/%s/%s", workdir, run_tag, stage); 
 
-        if (!distRunInsert(config->dbh,
+        distRunRow *row = distRunRowAlloc(
                 0,      // dist_id
                 target_id,
@@ -362,5 +391,13 @@
                 NULL,   // time_stamp
                 0       // fault
-        )) {
+                );
+
+        if (!row) {
+            psError(PS_ERR_UNKNOWN, false, "failed to allocate distRunRow");
+            psFree(outroot);
+            psFree(output);
+            return false;
+        }
+        if (!distRunInsertObject(config->dbh, row)) {
             psError(PS_ERR_UNKNOWN, false, "database error");
             psFree(outroot);
@@ -369,6 +406,21 @@
         }
         psFree(outroot);
-    }
-
+        psS64 dist_id = psDBLastInsertID(config->dbh);
+        row->dist_id = dist_id;
+        psArrayAdd(list, list->n, row);
+        psFree(row);
+    }
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!distRunPrintObjects(stdout, list, !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print object");
+        psFree(list);
+        return false;
+    }
+
+    psFree(list);
     psFree(output);
 
@@ -1109,5 +1161,5 @@
 
 
-    PXOPT_LOOKUP_STR(last_fileset, config->args, "-last_fileset", false, false);
+    PXOPT_LOOKUP_STR(last_fileset, config->args, "-set_last_fileset", false, false);
     PXOPT_LOOKUP_STR(state, config->args, "-set_state", false, false);
     PXOPT_LOOKUP_S16(fault, config->args, "-fault", false, false);
@@ -1213,2 +1265,439 @@
     return true;
 }
+
+static bool definetargetMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_STR(obs_mode, config->args, "-obs_mode", true, false);
+    PXOPT_LOOKUP_STR(stage, config->args, "-stage", true, false);
+
+    // optional
+    PXOPT_LOOKUP_BOOL(clean, config->args, "-clean", false);
+    PXOPT_LOOKUP_STR(state, config->args, "-set_state", false, false);
+    PXOPT_LOOKUP_STR(comment, config->args, "-comment", false, false);
+
+    distTargetRow *row = distTargetRowAlloc(
+            0,          // target_id
+            obs_mode,
+            stage,
+            clean,
+            state ? state : "enabled",
+            comment
+            );
+            
+    if (!row) {
+        psError(PS_ERR_UNKNOWN, false, "failed to allocate distTarget object");
+        return false;
+    }
+   if (!distTargetInsertObject(config->dbh, row)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(row);
+        return false;
+    }
+
+    // get the assigned target_id
+    row->target_id = psDBLastInsertID(config->dbh);
+
+    if (!distTargetPrintObject(stdout, row, true)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print object");
+            psFree(row);
+            return false;
+    }
+
+    psFree(row);
+
+    return true;
+}
+static bool updatetargetMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-target_id", "target_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-obs_mode", "obs_mode", "==");
+    PXOPT_COPY_STR(config->args, where, "-stage", "stage", "==");
+
+    PXOPT_LOOKUP_STR(state, config->args, "-set_state", true, false);
+
+    psString query = psStringCopy("UPDATE distTarget SET state = '%s'");
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s", whereClause);
+        psFree(whereClause);
+    } else {
+        psError(PS_ERR_UNKNOWN, true, "search parameters are required");
+        psFree(where);
+        psFree(query);
+        return false;
+    }
+    psFree(where);
+
+    if (!p_psDBRunQueryF(config->dbh, query, state)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    return true;
+}
+
+static bool listtargetMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-target_id", "target_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-obs_mode", "obs_mode", "==");
+    PXOPT_COPY_STR(config->args, where, "-stage", "stage", "==");
+    PXOPT_COPY_STR(config->args, where, "-state", "state", "==");
+
+    PXOPT_LOOKUP_BOOL(clean, config->args, "-clean", false);
+    PXOPT_LOOKUP_BOOL(full, config->args, "-full", false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    if (clean && full) {
+        psError(PS_ERR_UNKNOWN, false, "can't select both -clean and -full");
+        return false;
+    }
+
+    psString query = psStringCopy("SELECT * FROM distTarget");
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s", whereClause);
+        psFree(whereClause);
+        if (clean) {
+            psStringAppend(&query, " (AND clean)");
+        } else if (full) {
+            psStringAppend(&query, " (AND !clean)");
+        }
+    } else if (clean) {
+        psStringAppend(&query, " WHERE clean");
+    } else if (full) {
+        psStringAppend(&query, " WHERE !clean");
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("disttool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (!ippdbPrintMetadatas(stdout, output, "distTarget", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool definedsproductMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_STR(name, config->args,   "-name", true, false);
+    PXOPT_LOOKUP_STR(dbname, config->args, "-ds_dbname", true, false);
+    PXOPT_LOOKUP_STR(dbhost, config->args, "-ds_dbhost", true, false);
+
+    // XXX: should we insure that these names do not contatin any whitespace?
+
+    rcDSProductRow *row = rcDSProductRowAlloc(
+            0,          // prod_id
+            name,
+            dbname,
+            dbhost
+            );
+            
+    if (!row) {
+        psError(PS_ERR_UNKNOWN, false, "failed to allocate rcDSProduct object");
+        return false;
+    }
+   if (!rcDSProductInsertObject(config->dbh, row)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(row);
+        return false;
+    }
+
+    // get the assigned target_id
+    row->prod_id = psDBLastInsertID(config->dbh);
+
+    if (!rcDSProductPrintObject(stdout, row, true)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print object");
+        psFree(row);
+        return false;
+    }
+
+    psFree(row);
+
+    return true;
+}
+static bool updatedsproductMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-prod_id", "prod_id", "==");
+
+    PXOPT_LOOKUP_STR(dbname, config->args, "-ds_dbname", false, false);
+    PXOPT_LOOKUP_STR(dbhost, config->args, "-ds_dbhost", false, false);
+
+    if (!(dbname || dbhost)) {
+        psError(PS_ERR_UNKNOWN, true, "one or more of dbname or dbhost is required");
+        psFree(where);
+        return false;
+    }
+    psString query = psStringCopy("UPDATE rcDSProduct SET");
+    psString sep = "";
+    if (dbname) {
+        psStringAppend(&query, " dbname = '%s'", dbname);
+        sep = ",";
+    }
+    if (dbhost) {
+        psStringAppend(&query, " %s dbhost = '%s'", sep, dbhost);
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s", whereClause);
+        psFree(whereClause);
+    } else {
+        psError(PS_ERR_UNKNOWN, true, "search parameters are required");
+        psFree(where);
+        psFree(query);
+        return false;
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    return true;
+}
+
+static bool definedestinationMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_S64(prod_id, config->args,      "-prod_id", true, false);
+    PXOPT_LOOKUP_STR(name, config->args,         "-name", true, false);
+
+    // optional
+    PXOPT_LOOKUP_STR(status_uri, config->args,   "-status_uri", false, false);
+    PXOPT_LOOKUP_STR(comment, config->args,      "-comment", false, false);
+    PXOPT_LOOKUP_STR(last_fileset, config->args, "-last_fileset", false, false);
+    PXOPT_LOOKUP_STR(state, config->args,        "-set_state", false, false);
+
+    // XXX: should we insure that these names do not contatin any whitespace?
+
+    rcDestinationRow *row = rcDestinationRowAlloc(
+            0,          // dest_id
+            prod_id,
+            name,
+            status_uri,
+            comment,
+            last_fileset,
+            state ? state : "enabled"
+            );
+            
+    if (!row) {
+        psError(PS_ERR_UNKNOWN, false, "failed to allocate rcDestination object");
+        return false;
+    }
+   if (!rcDestinationInsertObject(config->dbh, row)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(row);
+        return false;
+    }
+
+    // get the assigned target_id
+    row->dest_id = psDBLastInsertID(config->dbh);
+
+    if (!rcDestinationPrintObject(stdout, row, true)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print object");
+        psFree(row);
+        return false;
+    }
+
+    psFree(row);
+
+    return true;
+}
+
+static bool updatedestinationMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-dest_id", "dest_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-prod_id", "prod_id", "==");
+
+    PXOPT_LOOKUP_STR(state, config->args, "-set_state", false, false);
+#ifdef ALLOW_UPDATE_LAST_FILESET
+    PXOPT_LOOKUP_STR(last_fileset, config->args, "-set_last_fileset", false, false);
+    if (!(state || last_fileset)) {
+        psError(PS_ERR_UNKNOWN, true, "one or more of -set_state or -set_last_fileset is required");
+# else 
+    if (!state) {
+#endif
+        psFree(where);
+        return false;
+    }
+    psString query = psStringCopy("UPDATE rcDestination SET");
+    psString sep = "";
+    if (state) {
+        psStringAppend(&query, " state = '%s'", state);
+        sep = ",";
+    }
+#ifdef ALLOW_UPDATE_LAST_FILESET
+    // last_fileset normally gets set by updatercrunMode
+    // Allowing it to be set here might cause problems
+    // especially since we are allowing selection by prod_id
+    if (last_fileset) {
+        psStringAppend(&query, " %s last_fileset = '%s'", sep, last_fileset);
+    }
+#endif
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s", whereClause);
+        psFree(whereClause);
+    } else {
+        psError(PS_ERR_UNKNOWN, true, "search parameters are required");
+        psFree(where);
+        psFree(query);
+        return false;
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    return true;
+}
+
+static bool defineinterestMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_S64(dest_id, config->args,      "-dest_id", true, false);
+    PXOPT_LOOKUP_S64(target_id, config->args,    "-target_id", true, false);
+
+    // optional
+    PXOPT_LOOKUP_STR(state, config->args,        "-set_state", false, false);
+
+    // XXX: should we insure that these names do not contatin any whitespace?
+
+    rcInterestRow *row = rcInterestRowAlloc(
+            0,          // int_id
+            dest_id,
+            target_id,
+            state ? state : "enabled"
+            );
+            
+    if (!row) {
+        psError(PS_ERR_UNKNOWN, false, "failed to allocate rcInterest object");
+        return false;
+    }
+   if (!rcInterestInsertObject(config->dbh, row)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(row);
+        return false;
+    }
+
+    // get the assigned target_id
+    row->int_id = psDBLastInsertID(config->dbh);
+
+    if (!rcInterestPrintObject(stdout, row, true)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print object");
+        psFree(row);
+        return false;
+    }
+
+    psFree(row);
+
+    return true;
+}
+
+static bool updateinterestMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-int_id",    "int_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-dest_id",   "dest_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-target_id", "target_id", "==");
+
+    PXOPT_LOOKUP_STR(state, config->args, "-set_state", false, false);
+    if (!state) {
+        psError(PS_ERR_UNKNOWN, true, "-set_state is required");
+        psFree(where);
+        return false;
+    }
+    psString query = NULL;
+    psStringAppend(&query, "UPDATE rcInterest SET state = '%s'", state);
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s", whereClause);
+        psFree(whereClause);
+    } else {
+        psError(PS_ERR_UNKNOWN, true, "search parameters are required");
+        psFree(where);
+        psFree(query);
+        return false;
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    return true;
+}
+
Index: /trunk/ippTools/src/disttool.h
===================================================================
--- /trunk/ippTools/src/disttool.h	(revision 24000)
+++ /trunk/ippTools/src/disttool.h	(revision 24001)
@@ -40,4 +40,13 @@
     DISTTOOL_MODE_REVERTRCRUN,
     DISTTOOL_MODE_PENDINGDEST,
+    DISTTOOL_MODE_DEFINEDESTINATION,
+    DISTTOOL_MODE_UPDATEDESTINATION,
+    DISTTOOL_MODE_DEFINEDSPRODUCT,
+    DISTTOOL_MODE_UPDATEDSPRODUCT,
+    DISTTOOL_MODE_DEFINETARGET,
+    DISTTOOL_MODE_UPDATETARGET,
+    DISTTOOL_MODE_LISTTARGET,
+    DISTTOOL_MODE_DEFINEINTEREST,
+    DISTTOOL_MODE_UPDATEINTEREST,
 } disttoolMode;
 
Index: /trunk/ippTools/src/disttoolConfig.c
===================================================================
--- /trunk/ippTools/src/disttoolConfig.c	(revision 24000)
+++ /trunk/ippTools/src/disttoolConfig.c	(revision 24001)
@@ -62,4 +62,5 @@
 
     psMetadataAddU64(definebyqueryArgs, PS_LIST_TAIL, "-limit",  0,  "limit result set to N items", 0);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
 
     // -definerun
@@ -178,5 +179,5 @@
     psMetadataAddStr(updatercrunArgs, PS_LIST_TAIL, "-status_fs_name",  0, "define status fileset name", NULL);
     psMetadataAddStr(updatercrunArgs, PS_LIST_TAIL, "-set_state",0, "new value for state", NULL);
-    psMetadataAddStr(updatercrunArgs, PS_LIST_TAIL, "-last_fileset",0, "new value for last_fileset", NULL);
+    psMetadataAddStr(updatercrunArgs, PS_LIST_TAIL, "-set_last_fileset",0, "new value for last_fileset", NULL);
     psMetadataAddS16(updatercrunArgs, PS_LIST_TAIL, "-fault",    0, "define fault code", -1);
 
@@ -188,4 +189,77 @@
     psMetadataAddS16(revertrcrunArgs, PS_LIST_TAIL,  "-fault",   0, "define fault code", 0);
     psMetadataAddBool(revertrcrunArgs, PS_LIST_TAIL, "-all",     0, "revert all faulted runs", NULL);
+
+    // -definedsproduct
+    psMetadata *definedsproductArgs = psMetadataAlloc();
+    psMetadataAddStr(definedsproductArgs, PS_LIST_TAIL, "-name",  0, "define product name", NULL);
+    psMetadataAddStr(definedsproductArgs, PS_LIST_TAIL, "-ds_dbname",0, "define data store database name", NULL);
+    psMetadataAddStr(definedsproductArgs, PS_LIST_TAIL, "-ds_dbhost",0, "define data store database host", NULL);
+
+    // -updatedsproduct
+    // does this mode make sense?
+    psMetadata *updatedsproductArgs = psMetadataAlloc();
+    psMetadataAddS64(updatedsproductArgs, PS_LIST_TAIL, "-prod_id",   0, "select by prod_id", 0);
+    // can't select by name because it isn't necssarily unique
+    psMetadataAddStr(updatedsproductArgs, PS_LIST_TAIL, "-ds_dbname",0, "define data store database name", NULL);
+    psMetadataAddStr(updatedsproductArgs, PS_LIST_TAIL, "-ds_dbhost",0, "define data store database host", NULL);
+
+    // -definedestination
+    psMetadata *definedestinationArgs = psMetadataAlloc();
+    psMetadataAddS64(definedestinationArgs, PS_LIST_TAIL, "-prod_id",     0, "define prod_id (required)", 0);
+    psMetadataAddStr(definedestinationArgs, PS_LIST_TAIL, "-name",        0, "define destination name (required)", NULL);
+    psMetadataAddStr(definedestinationArgs, PS_LIST_TAIL, "-status_uri",  0, "define status_uri", NULL);
+    psMetadataAddStr(definedestinationArgs, PS_LIST_TAIL, "-comment",     0, "define comment", NULL);
+    psMetadataAddStr(definedestinationArgs, PS_LIST_TAIL, "-last_fileset",0, "define last_fileset", NULL);
+    psMetadataAddStr(definedestinationArgs, PS_LIST_TAIL, "-set_state",   0, "define state", NULL);
+
+    // -updatedestination
+    psMetadata *updatedestinationArgs = psMetadataAlloc();
+    psMetadataAddS64(updatedestinationArgs, PS_LIST_TAIL, "-dest_id",     0, "define dest_id", 0);
+    psMetadataAddS64(updatedestinationArgs, PS_LIST_TAIL, "-prod_id",     0, "define prod_id", 0);
+    psMetadataAddStr(updatedestinationArgs, PS_LIST_TAIL, "-name",        0, "define destination name", NULL);
+    psMetadataAddStr(updatedestinationArgs, PS_LIST_TAIL, "-status_uri",  0, "define status_uri", NULL);
+    psMetadataAddStr(updatedestinationArgs, PS_LIST_TAIL, "-comment",     0, "define comment", NULL);
+//  last_fileset gets updated by -updatercrun
+//  psMetadataAddStr(updatedestinationArgs, PS_LIST_TAIL, "-set_last_fileset",0, "define last_fileset", NULL);
+    psMetadataAddStr(updatedestinationArgs, PS_LIST_TAIL, "-set_state",   0, "define state", NULL);
+
+    // -definetarget
+    psMetadata *definetargetArgs = psMetadataAlloc();
+    psMetadataAddStr(definetargetArgs, PS_LIST_TAIL, "-obs_mode",  0, "define obs_mode (required)", NULL);
+    psMetadataAddStr(definetargetArgs, PS_LIST_TAIL, "-stage",     0, "define stage (required)", NULL);
+    psMetadataAddBool(definetargetArgs, PS_LIST_TAIL,"-clean",     0, "define clean", false);
+    psMetadataAddStr(definetargetArgs, PS_LIST_TAIL, "-set_state", 0, "define state", NULL);
+    psMetadataAddStr(definetargetArgs, PS_LIST_TAIL, "-comment",   0, "define comment", NULL);
+
+    // -updatetarget
+    psMetadata *updatetargetArgs = psMetadataAlloc();
+    psMetadataAddS64(updatetargetArgs, PS_LIST_TAIL, "-target_id", 0, "define target_id", 0);
+    psMetadataAddStr(updatetargetArgs, PS_LIST_TAIL, "-obs_mode",  0, "define obs_mode", NULL);
+    psMetadataAddStr(updatetargetArgs, PS_LIST_TAIL, "-stage",     0, "define stage", NULL);
+    psMetadataAddStr(updatetargetArgs, PS_LIST_TAIL, "-set_state", 0, "define state", NULL);
+
+    // -listtarget
+    psMetadata *listtargetArgs = psMetadataAlloc();
+    psMetadataAddS64(listtargetArgs, PS_LIST_TAIL, "-target_id", 0, "list target with target_id", 0);
+    psMetadataAddStr(listtargetArgs, PS_LIST_TAIL, "-obs_mode",  0, "list targets for obs_mode", NULL);
+    psMetadataAddStr(listtargetArgs, PS_LIST_TAIL, "-stage",     0, "list targets for stage", NULL);
+    psMetadataAddBool(listtargetArgs, PS_LIST_TAIL,"-clean",     0, "list clean targets", false);
+    psMetadataAddBool(listtargetArgs, PS_LIST_TAIL,"-full",      0, "list full targets", false);
+    psMetadataAddStr(listtargetArgs, PS_LIST_TAIL, "-state",     0, "list tarets in state", NULL);
+    psMetadataAddU64(listtargetArgs, PS_LIST_TAIL, "-limit",     0, "limit number of targets listed to N", 0);
+    psMetadataAddBool(listtargetArgs, PS_LIST_TAIL, "-simple",  0, "use the simple output format", false);
+
+    // -defineinterest
+    psMetadata *defineinterestArgs = psMetadataAlloc();
+    psMetadataAddS64(defineinterestArgs, PS_LIST_TAIL, "-dest_id",   0, "define dest_id (required)", 0);
+    psMetadataAddS64(defineinterestArgs, PS_LIST_TAIL, "-target_id", 0, "define target_id (required)", 0);
+    psMetadataAddStr(defineinterestArgs, PS_LIST_TAIL, "-set_state", 0, "define state", NULL);
+
+    // -updateinterest
+    psMetadata *updateinterestArgs = psMetadataAlloc();
+    psMetadataAddS64(updateinterestArgs, PS_LIST_TAIL, "-int_id",    0, "define int_id", 0);
+    psMetadataAddS64(updateinterestArgs, PS_LIST_TAIL, "-dest_id",   0, "define dest_id", 0);
+    psMetadataAddS64(updateinterestArgs, PS_LIST_TAIL, "-target_id", 0, "define target_id", 0);
+    psMetadataAddStr(updateinterestArgs, PS_LIST_TAIL, "-set_state", 0, "define state (required)", NULL);
 
     psMetadata *argSets = psMetadataAlloc();
@@ -208,4 +282,20 @@
     PXOPT_ADD_MODE("-pendingdest",        "", DISTTOOL_MODE_PENDINGDEST, pendingdestArgs);
 
+    PXOPT_ADD_MODE("-definedsproduct",    "", DISTTOOL_MODE_DEFINEDSPRODUCT, definedsproductArgs);
+    PXOPT_ADD_MODE("-updatedsproduct",    "", DISTTOOL_MODE_UPDATEDSPRODUCT, updatedsproductArgs);
+//  PXOPT_ADD_MODE("-listdsproduct",      "", DISTTOOL_MODE_LISTDSPRODUCT, updatedsproductArgs);
+
+    PXOPT_ADD_MODE("-definedestination",  "", DISTTOOL_MODE_DEFINEDESTINATION, definedestinationArgs);
+    PXOPT_ADD_MODE("-updatedestination",  "", DISTTOOL_MODE_UPDATEDESTINATION, updatedestinationArgs);
+//  PXOPT_ADD_MODE("-listdestination",    "", DISTTOOL_MODE_LISTDESTINATION, listdestinationArgs);
+
+    PXOPT_ADD_MODE("-definetarget",       "", DISTTOOL_MODE_DEFINETARGET, definetargetArgs);
+    PXOPT_ADD_MODE("-updatetarget",       "", DISTTOOL_MODE_UPDATETARGET, updatetargetArgs);
+    PXOPT_ADD_MODE("-listtarget",         "", DISTTOOL_MODE_LISTTARGET, listtargetArgs);
+
+    PXOPT_ADD_MODE("-defineinterest",     "", DISTTOOL_MODE_DEFINEINTEREST, defineinterestArgs);
+    PXOPT_ADD_MODE("-updateinterest",     "", DISTTOOL_MODE_UPDATEINTEREST, updateinterestArgs);
+//  PXOPT_ADD_MODE("-listinterest",       "", DISTTOOL_MODE_LISTINTEREST, listinterestArgs);
+
     if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
         psError(PS_ERR_UNKNOWN, true, "option parsing failed");
