← Back to team overview

yellow team mailing list archive

Look! It's C! It's frightening!

 

For fun and profit:
- put this C file in the root of a Launchpad tree
- run `gcc -o run-lxc-tests run-lxc-tests.c`
- run `sudo chown root run-lxc-tests`
- run `sudo chmod u+s run-lxc-tests`
- run `./run-lxc-tests -h` and then `./run-lxc-tests -d` and then `./run-lxc-tests -d -o lpdev -- -t stories/gpg` to get a feel for what it will do. - run something like `./run-lxc-tests -o lpdev -- -t stories/gpg` to see it actually do what we want: run the tests in an ephemeral container, as you, fairly safely, without having to sudo/authenticate yourself.

I think setuplxc will need to write, compile, and configure both this and another very similar one that uses lxc-execute on the base lptests container to update all the sourcecode and download cache and then build the code and make schema.

Suggestions welcome! This is my first non-hello-world-level C code, as will be quite clear. Whee, fun with arrays of pointers! Thanks to Benji for helping me with the idea, but don't blame him for any of the code. ;-)

If you don't have any better ideas of what to do, and you think this is basically OK, you could help me write the "update Launchpad in the container" version, and integrate both into setuplxc.

Talk to you later

Gary
#include <errno.h> // For strerror
#include <stdio.h> // For asprintf
#include <stdlib.h> // For malloc
#include <string.h> // For memcpy
#include <unistd.h> // For execv and getopt

// XXX run-ephemeral -o lpdev -- xvfb-run -a /wherever/ --subunit -t

/* Please keep this as simple as possible so that occasional-to-never
   C hackers can still understand and even contribute.
 */

void show_usage() {
  printf("Usage: run-lxc-tests [options] [-- test options]\n");
  printf(" -h: Show this help message and exit.\n");
  printf(" -o: Specify the lxc container to overlay.  Defaults to lptests.\n");
  printf(" -b: The working directory, bound in the lxc container and \n"
         "     containing bin/test.  Defaults to the current working \n"
         "     directory.\n");
  printf(" -d: Do a dry run.  Show the command that would have run but do \n"
         "     not run it.\n");
}

int main(int argc, char **argv)
{
  int i;
  int ret = 0;
  /* Set up defaults */
  char *base_container_name = "lptests";
  char *working_directory = getcwd(NULL, 0);
  int dry_run = 0;
  char *uid;
  asprintf(&uid, "-u#%d", getuid());
  int option;
  opterr = 0;

  while ((option = getopt(argc, argv, "dho:b:")) != -1)
    switch (option)
      {
      case 'o':
        base_container_name = optarg;
         break;
      case 'b':
        working_directory = optarg;
        break;
      case 'd':
        dry_run = 1;
        break;
      case 'h':
        show_usage();
        return 0;
      case '?':
        if (isprint (optopt))
          fprintf (stderr, "Unknown option `-%c'.\n", optopt);
        else
          fprintf (stderr,
                   "Unknown option character `\\x%x'.\n",
                    optopt);
        show_usage();
        return 1;
      default:
        abort();
      };

  char *test_command;
  asprintf(&test_command, "%s/bin/test", working_directory);

  char *cmd[] = {
    "lxc-start-ephemeral",
    "-o", base_container_name,
    "-b", working_directory,
    "--",
    "sudo", "-i", uid,
    "xvfb-run", "-a", test_command, "--subunit",
  };

  // Now we want to construct a command which is the base cmd above plus any
  // additional arguments.  This means we need to join two arrays of string
  // (pointers).
  int len_of_base_cmd = 13;
  // "optind" comes from getopt.  See, for instance,
  // http://www.cs.utah.edu/dept/old/texinfo/glibc-manual-0.02/library_22.html
  int len_of_additional_arguments = (argc - optind);
  int len_of_new_array = (len_of_base_cmd + len_of_additional_arguments + 1);
  int size_of_string_pointer = sizeof(char *);
  char ** command = (char **) malloc(size_of_string_pointer * len_of_new_array);
  memcpy(command, cmd, len_of_base_cmd * size_of_string_pointer);
  // I tried this with a memcpy, trying to be clever with pointer
  // arithmetic, and couldn't get it to work.  This is clearer
  // anyway, I think.
  for (i = len_of_base_cmd; i < len_of_new_array - 1; i ++)
    command[i] = argv[optind + (i - len_of_base_cmd)];
  // Now command should be the concatenation we want.  We have to add (char *)0
  // to the end in order to signal to execv that it is at the end of the array.
  command[len_of_new_array - 1] = (char *)0;
  if (dry_run) {
    printf("\nDRY RUN. We would have run this:\n");
    printf("================================\n");
    for (i=0; i< (len_of_new_array -1); i++) printf("%s ", command[i]);
    printf("\n================================\n");
  } else {
    ret = execv("/usr/bin/lxc-start-ephemeral", command);
  }
  free(command);
  if (ret == -1) printf("Error: %s\n", strerror(errno));
  return ret;
}