--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sbsv2/raptor/util/talon/talon.c Tue Oct 27 16:36:35 2009 +0000
@@ -0,0 +1,613 @@
+/*
+* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+
+
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdarg.h>
+
+#include "talon_process.h"
+#include "sema.h"
+#include "buffer.h"
+#include "../config.h"
+
+/* The output semaphore. */
+sbs_semaphore talon_sem;
+
+#define TALON_ATTEMPT_STRMAX 32
+#define RECIPETAG_STRMAX 2048
+#define STATUS_STRMAX 100
+
+#define TALONDELIMITER '|'
+#define VARNAMEMAX 100
+#define VARVALMAX 1024
+
+
+#include "log.h"
+
+#ifdef HAS_MSVCRT
+/* Make all output handling binary */
+unsigned int _CRT_fmode = _O_BINARY;
+#endif
+
+double getseconds(void)
+{
+ struct timeval tp;
+ gettimeofday(&tp, NULL);
+
+ return (double)tp.tv_sec + ((double)tp.tv_usec)/1000000.0L;
+}
+
+void talon_setenv(char name[], char val[])
+{
+#if defined(HAS_GETENVIRONMENTVARIABLE)
+ SetEnvironmentVariableA(name,val);
+#elif defined(HAS_GETENV)
+ setenv(name,val, 1);
+#else
+# error "Need a function for setting environment variables"
+#endif
+}
+
+
+#define TALON_MAXENV 4096
+char * talon_getenv(char name[])
+{
+#if defined(HAS_SETENV)
+ char *val = getenv(name);
+ char *dest = NULL;
+
+ if (val)
+ {
+ dest = malloc(strlen(val) + 1);
+ if (dest)
+ {
+ strcpy(dest,val);
+ }
+ }
+ return dest;
+#elif defined(HAS_SETENVIRONMENTVARIABLE)
+ char *val = malloc(TALON_MAXENV);
+ if (0 != GetEnvironmentVariableA(name,val,TALON_MAXENV-1))
+ return val;
+ else
+ return NULL;
+#else
+# error "Need a function for setting environment variables"
+#endif
+}
+
+void prependattributes(buffer *b, char *attributes)
+{
+ char recipetag[RECIPETAG_STRMAX];
+ char *rt;
+ char envvarname[VARNAMEMAX];
+ char *att;
+
+
+ strcpy(recipetag, "<recipe ");
+ rt = recipetag + 8;
+
+ att = attributes;
+ while (*att != '\0' && rt < &recipetag[RECIPETAG_STRMAX-1])
+ {
+ if ( *att == '$' )
+ {
+ int e;
+ char *v;
+ /* insert the value of an environent variable */
+ att++;
+ e = 0;
+ do {
+ envvarname[e++] = *att;
+ att++;
+ } while ( e < (VARNAMEMAX-1) && (isalnum(*att) || *att == '_'));
+ envvarname[e] = '\0';
+/* DEBUG(("envvarname: %s\n", envvarname)); */
+ v = talon_getenv(envvarname);
+ if (v)
+ {
+ /* DEBUG((" value: %s\n", v)); */
+ char *oldv=v;
+ while (*v != '\0' && rt < &recipetag[RECIPETAG_STRMAX-1])
+ {
+ *rt = *v;
+ rt++; v++;
+ }
+ free(oldv);
+ }
+ } else {
+ *rt = *att;
+ rt++; att++;
+ }
+ }
+
+ char *finish = ">\n<![CDATA[\n";
+
+ while (*finish != '\0' && rt < &recipetag[RECIPETAG_STRMAX-1])
+ {
+ *rt = *finish;
+ *rt++; finish++;
+ }
+
+ *rt = '\0';
+
+ buffer_prepend(b, recipetag, strlen(recipetag));
+}
+
+/* read a recipe string from a temporary file.
+ *
+ * We only expect to do this for very long command lines on
+ * Windows. So allocate the maximum size for CreateProcess on
+ * Win32 (32768 bytes) and error if the file is longer than that.
+ *
+ */
+char *read_recipe_from_file(const char *filename)
+{
+ FILE *fp = fopen(filename, "r");
+
+ if (!fp)
+ {
+ error("talon: error: could not read '%s'\n", filename);
+ return NULL;
+ }
+
+ int max_length = 32768;
+ char *recipe = (char*)malloc(max_length);
+
+ if (!recipe)
+ {
+ error("talon: error: could not allocate memory to read '%s'\n", filename);
+ return NULL;
+ }
+ int position = 0;
+
+ /* read the file one character at a time */
+ int c;
+ while ((c = getc(fp)) != EOF)
+ {
+ switch (c)
+ {
+ case '\r':
+ case '\n':
+ /* ignore newlines */
+ break;
+
+ case '"':
+ /* double quotes have to be escaped so they aren't lost later */
+ if (position < max_length)
+ recipe[position++] = '\\';
+ if (position < max_length)
+ recipe[position++] = c;
+ break;
+
+ default:
+ if (position < max_length)
+ recipe[position++] = c;
+ break;
+ }
+ }
+ fclose(fp);
+
+ /* add a terminating \0 */
+ if (position < max_length)
+ recipe[position] = '\0';
+ else
+ {
+ error("talon: error: command longer than 32768 in '%s'\n", filename);
+ return NULL;
+ }
+ return recipe;
+}
+
+int main(int argc, char *argv[])
+{
+ /* find the argument to -c then strip the talon related front section */
+
+ char *recipe = NULL;
+ int talon_returncode = 0;
+
+#ifdef HAS_GETCOMMANDLINE
+ char *commandline= GetCommandLine();
+ DEBUG(("talon: commandline: %s\n", commandline));
+ /*
+ * The command line should be either,
+ * talon -c "some shell commands"
+ * or
+ * talon shell_script_file
+ *
+ * talon could be an absolute path and may have a .exe extension.
+ */
+ recipe = strstr(commandline, "-c");
+ if (recipe)
+ {
+ /* there was a -c so extract the quoted commands */
+
+ while (*recipe != '"' && *recipe != '\0')
+ recipe++;
+
+ if (*recipe != '"') /* we found -c but no following quote */
+ {
+ error("talon: error: unquoted recipe in shell call '%s'\n", commandline);
+ return 1;
+ }
+ recipe++;
+
+ int recipelen = strlen(recipe);
+ if (recipelen > 0 && recipe[recipelen - 1] == '"')
+ recipe[recipelen - 1] = '\0'; /* remove trailing quote */
+ }
+ else
+ {
+ /* there was no -c so extract the argument as a filename */
+
+ recipe = strstr(commandline, "talon");
+ if (recipe)
+ {
+ /* find the first space */
+ while (!isspace(*recipe) && *recipe != '\0')
+ recipe++;
+ /* skip past the spaces */
+ while (isspace(*recipe))
+ recipe++;
+
+ recipe = read_recipe_from_file(recipe);
+
+ if (!recipe)
+ {
+ error("talon: error: bad script file in shell call '%s'\n", commandline);
+ return 1;
+ }
+ }
+ else
+ {
+ error("talon: error: no 'talon' in shell call '%s'\n", commandline);
+ return 1;
+ }
+ }
+#else
+ /*
+ * The command line should be either,
+ * talon -c "some shell commands"
+ * or
+ * talon shell_script_file
+ *
+ * talon could be an absolute path and may have a .exe extension.
+ */
+ switch (argc)
+ {
+ case 2:
+ recipe = read_recipe_from_file(argv[1]);
+ break;
+ case 3:
+ if (strcmp("-c", argv[1]) != 0)
+ {
+ error("talon: error: %s\n", "usage is 'talon -c command' or 'talon script_filename'");
+ return 1;
+ }
+ recipe = argv[2];
+ break;
+ default:
+ error("talon: error: %s\n", "usage is 'talon -c command' or 'talon script_filename'");
+ return 1;
+ }
+#endif
+
+ /* did we get a recipe at all? */
+ if (!recipe)
+ {
+ error("talon: error: %s", "no recipe supplied to the shell.\n");
+ return 1;
+ }
+
+ /* remove any leading white space on the recipe */
+ while (isspace(*recipe))
+ recipe++;
+
+ /* turn debugging on? */
+ char *debugstr=talon_getenv("TALON_DEBUG");
+
+ if (debugstr)
+ {
+ loglevel=LOGDEBUG;
+ free(debugstr); debugstr=NULL;
+ }
+
+ DEBUG(("talon: recipe: %s\n", recipe));
+
+
+ char varname[VARNAMEMAX];
+ char varval[VARVALMAX];
+ int dotagging = 0;
+ int force_descramble_off = 0;
+
+ char *rp = recipe;
+ if (*rp == TALONDELIMITER) {
+ dotagging = 1;
+
+ /* there are some talon-specific settings
+ * in the command which must be stripped */
+ rp++;
+ char *out = varname;
+ char *stopout = varname + VARNAMEMAX - 1;
+ DEBUG(("talon: parameters found\n"));
+ while (*rp != '\0')
+ {
+
+ switch (*rp) {
+ case '=':
+ *out = '\0';
+ DEBUG(("talon: varname: %s\n",varname));
+ out = varval;
+ stopout = varval + VARVALMAX - 1;
+ break;
+ case ';':
+ *out = '\0';
+ DEBUG(("talon: varval: %s\n",varval));
+ talon_setenv(varname, varval);
+ out = varname;
+ stopout = varname + VARNAMEMAX - 1;
+ break;
+ default:
+ *out = *rp;
+ if (out < stopout)
+ out++;
+ break;
+ }
+
+ if (*rp == TALONDELIMITER)
+ {
+ rp++;
+ break;
+ }
+
+ rp++;
+ }
+ } else {
+ /* This is probably a $(shell) statement
+ * in make so no descrambling needed and
+ * tags are definitely not wanted as they
+ * would corrupt the expected output*/
+ force_descramble_off = 1;
+ }
+
+
+ /* Now take settings from the environment (having potentially modified it) */
+ if (talon_getenv("TALON_DEBUG"))
+ loglevel=LOGDEBUG;
+
+
+ int enverrors = 0;
+
+ char *shell = talon_getenv("TALON_SHELL");
+ if (!shell)
+ {
+ error("error: %s", "TALON_SHELL not set in environment\n");
+ enverrors++;
+ }
+
+ int timeout = -1;
+ char *timeout_str = talon_getenv("TALON_TIMEOUT");
+ if (timeout_str)
+ {
+ timeout = atoi(timeout_str);
+ free(timeout_str); timeout_str = NULL;
+ }
+
+ char *buildid = talon_getenv("TALON_BUILDID");
+ if (!buildid)
+ {
+ error("error: %s", "TALON_BUILDID not set in environment\n");
+ enverrors++;
+ }
+
+ char *attributes = talon_getenv("TALON_RECIPEATTRIBUTES");
+ if (!attributes)
+ {
+ error("error: %s", "TALON_RECIPEATTRIBUTES not set in environment\n");
+ enverrors++;
+ }
+
+
+ int max_retries = 0;
+ char *retries_str = talon_getenv("TALON_RETRIES");
+ if (retries_str)
+ {
+ max_retries = atoi(retries_str);
+ free(retries_str); retries_str = NULL;
+ }
+
+
+ int descramble = 0;
+ if (! force_descramble_off )
+ {
+ char *descramblestr = talon_getenv("TALON_DESCRAMBLE");
+ if (descramblestr)
+ {
+ if (*descramblestr == '0')
+ descramble = 0;
+ else
+ descramble = 1;
+
+ free(descramblestr); descramblestr = NULL;
+ }
+ }
+
+
+
+ /* Talon can look in a flags variable to alter it's behaviour */
+ int force_success = 0;
+ char *flags_str = talon_getenv("TALON_FLAGS");
+ if (flags_str)
+ {
+ int c;
+ for (c=0; flags_str[c] !=0; c++)
+ flags_str[c] = tolower(flags_str[c]);
+
+ if (strstr(flags_str, "forcesuccess"))
+ force_success = 1;
+
+ /* don't put <recipe> or <CDATA<[[ tags around the output. e.g. if it's XML already*/
+ if (strstr(flags_str, "rawoutput"))
+ {
+ dotagging = 0;
+ }
+
+ free(flags_str); flags_str = NULL;
+ }
+
+ /* Talon subprocesses need to have the "correct" shell variable set. */
+ talon_setenv("SHELL", shell);
+
+ /* we have allowed some errors to build up so that the user
+ * can see all of them before we stop and force the user
+ * to fix them
+ */
+ if (enverrors)
+ {
+ return 1;
+ }
+
+
+ /* Run the recipe repeatedly until the retry count expires or
+ * it succeeds.
+ */
+ int attempt = 0, retries = max_retries;
+ proc *p = NULL;
+
+ char *args[5];
+
+ char *qrp=rp;
+
+#ifdef HAS_GETCOMMANDLINE
+ /* re-quote the argument to -c since this helps windows deal with it */
+ int qrpsize = strlen(rp) + 3;
+ qrp = malloc(qrpsize);
+ qrp[0] = '"';
+ strcpy(&qrp[1], rp);
+ qrp[qrpsize-2] = '"';
+ qrp[qrpsize-1] = '\0';
+#endif
+
+ int index = 0;
+ args[index++] = shell;
+
+ if (dotagging) /* don't do bash -x for non-tagged commands e.g. $(shell output) */
+ args[index++] = "-x";
+
+ args[index++] = "-c";
+ args[index++] = qrp;
+ args[index++] = NULL;
+
+ /* get the semaphore ready */
+ talon_sem.name = buildid;
+ talon_sem.timeout = timeout;
+ do
+ {
+ char talon_attempt[TALON_ATTEMPT_STRMAX];
+ double start_time = getseconds();
+
+ attempt++;
+
+ snprintf(talon_attempt, TALON_ATTEMPT_STRMAX-1, "%d", attempt);
+ talon_attempt[TALON_ATTEMPT_STRMAX - 1] = '\0';
+
+ talon_setenv("TALON_ATTEMPT", talon_attempt);
+
+ p = process_run(shell, args, timeout);
+
+ double end_time = getseconds();
+
+ if (p)
+ {
+ char status[STATUS_STRMAX];
+ char timestat[STATUS_STRMAX];
+
+ talon_returncode = p->returncode;
+
+ if (dotagging)
+ {
+ char *forcesuccessstr = force_success == 0 ? "" : " forcesuccess='FORCESUCCESS'";
+
+ if (p->returncode != 0)
+ {
+ char *exitstr = retries > 0 ? "retry" : "failed";
+ snprintf(status, STATUS_STRMAX - 1, "\n<status exit='%s' code='%d' attempt='%d'%s />", exitstr, p->returncode, attempt, forcesuccessstr );
+ } else {
+ snprintf(status, STATUS_STRMAX - 1, "\n<status exit='ok' attempt='%d'%s />", attempt, forcesuccessstr );
+ }
+ status[STATUS_STRMAX-1] = '\0';
+
+ snprintf(timestat, STATUS_STRMAX - 1, "<time start='%.5f' elapsed='%.3f' />",start_time, end_time-start_time );
+ timestat[STATUS_STRMAX-1] = '\0';
+
+ prependattributes(p->output, attributes);
+
+ if (dotagging) {
+ }
+ buffer_append(p->output, "\n]]>", 4);
+ buffer_append(p->output, timestat, strlen(timestat));
+ buffer_append(p->output, status, strlen(status));
+ buffer_append(p->output, "\n</recipe>\n", 11);
+ }
+
+ unsigned int iterator = 0;
+ byteblock *bb;
+
+ if (descramble)
+ sema_wait(&talon_sem);
+ while ((bb = buffer_getbytes(p->output, &iterator)))
+ {
+ write(STDOUT_FILENO, &bb->byte0, bb->fill);
+ }
+ if (descramble)
+ sema_release(&talon_sem);
+
+
+ if (p->returncode == 0 || force_success)
+ {
+ process_free(&p);
+ break;
+ }
+
+ process_free(&p);
+ } else {
+ error("error: failed to run shell: %s: check the SHELL environment variable.\n", args[0]);
+ return 1;
+ }
+
+ retries--;
+ }
+ while (retries >= 0);
+
+ if (buildid) free(buildid); buildid = NULL;
+ if (attributes) free(attributes); attributes = NULL;
+ if (shell) free(shell); shell = NULL;
+
+ if (force_success)
+ return 0;
+ else
+ return talon_returncode;
+}