X-Git-Url: http://git.annexia.org/?a=blobdiff_plain;f=helper%2Fmain.c;h=b769bc795d38a73848438c6f47250be5ec345efb;hb=2e1e2d686ca8f819c202d2ed4e8a5c5758451f26;hp=a30e02c9d469bc2cefcbe673e80bcd3dab6c76c4;hpb=e23a9c8f05e3646feb826d5db36d8656a80a27ab;p=febootstrap.git diff --git a/helper/main.c b/helper/main.c index a30e02c..b769bc7 100644 --- a/helper/main.c +++ b/helper/main.c @@ -28,8 +28,11 @@ #include #include #include +#include +#include #include "error.h" +#include "xstrtol.h" #include "helper.h" @@ -38,22 +41,28 @@ int verbose = 0; enum { HELP_OPTION = CHAR_MAX + 1 }; -static const char *options = "k:vV"; +static const char *options = "f:g:k:u:vV"; static const struct option long_options[] = { { "help", 0, 0, HELP_OPTION }, + { "format", required_argument, 0, 'f' }, + { "group", 0, 0, 'g' }, { "kmods", required_argument, 0, 'k' }, + { "user", 0, 0, 'u' }, { "verbose", 0, 0, 'v' }, { "version", 0, 0, 'V' }, { 0, 0, 0, 0 } }; static void -usage (const char *progname) +usage (FILE *f, const char *progname) { - printf ("%s: build the supermin appliance on the fly\n" + fprintf (f, + "%s: build the supermin appliance on the fly\n" "\n" "Usage:\n" - " %s [-options] inputs [...] whitelist host_cpu kernel initrd\n" + " %s [-options] inputs [...] host_cpu kernel initrd\n" + " %s -f ext2 inputs [...] host_cpu kernel initrd appliance\n" + " %s -f checksum inputs [...] host_cpu\n" " %s --help\n" " %s --version\n" "\n" @@ -68,13 +77,79 @@ usage (const char *progname) "Options:\n" " --help\n" " Display this help text and exit.\n" + " -f cpio|ext2|checksum | --format cpio|ext2|checksum\n" + " Specify output format (default: cpio).\n" + " -u user\n" + " The user name or uid the appliance will run as. Use of this\n" + " option requires root privileges.\n" + " -g group\n" + " The group name or gid the appliance will run as. Use of\n" + " this option requires root privileges.\n" " -k file | --kmods file\n" " Specify kernel module whitelist.\n" " --verbose | -v\n" " Enable verbose messages (give multiple times for more verbosity).\n" " --version | -V\n" " Display version number and exit.\n", - progname, progname, progname, progname); + progname, progname, progname, progname, progname, progname); +} + +static uid_t +parseuser (const char *id, const char *progname) +{ + + struct passwd *pwd; + + errno = 0; + pwd = getpwnam (id); + + if (NULL == pwd) { + if (errno != 0) { + fprintf (stderr, "Error looking up user: %m\n"); + exit (EXIT_FAILURE); + } + + long val; + int err = xstrtol (id, NULL, 10, &val, ""); + if (err != LONGINT_OK) { + fprintf (stderr, "%s is not a valid user name or uid\n", id); + usage (stderr, progname); + exit (EXIT_FAILURE); + } + + return (uid_t) val; + } + + return pwd->pw_uid; +} + +static gid_t +parsegroup (const char *id, const char *progname) +{ + + struct group *grp; + + errno = 0; + grp = getgrnam (id); + + if (NULL == grp) { + if (errno != 0) { + fprintf (stderr, "Error looking up group: %m\n"); + exit (EXIT_FAILURE); + } + + long val; + int err = xstrtol (id, NULL, 10, &val, ""); + if (err != LONGINT_OK) { + fprintf (stderr, "%s is not a valid group name or gid\n", id); + usage (stderr, progname); + exit (EXIT_FAILURE); + } + + return (gid_t) val; + } + + return grp->gr_gid; } int @@ -83,8 +158,12 @@ main (int argc, char *argv[]) /* First thing: start the clock. */ gettimeofday (&start_t, NULL); + const char *format = "cpio"; const char *whitelist = NULL; + uid_t euid = geteuid (); + gid_t egid = getegid (); + /* Command line arguments. */ for (;;) { int c = getopt_long (argc, argv, options, long_options, NULL); @@ -92,9 +171,21 @@ main (int argc, char *argv[]) switch (c) { case HELP_OPTION: - usage (argv[0]); + usage (stdout, argv[0]); exit (EXIT_SUCCESS); + case 'f': + format = optarg; + break; + + case 'u': + euid = parseuser (optarg, argv[0]); + break; + + case 'g': + egid = parsegroup (optarg, argv[0]); + break; + case 'k': whitelist = optarg; break; @@ -108,51 +199,122 @@ main (int argc, char *argv[]) exit (EXIT_SUCCESS); default: - usage (argv[0]); + usage (stderr, argv[0]); + exit (EXIT_FAILURE); + } + } + + /* We need to set the real, not effective, uid here to work round a + * misfeature in bash. bash will automatically reset euid to uid when + * invoked. As shell is used in places by febootstrap-supermin-helper, this + * results in code running with varying privilege. */ + uid_t uid = getuid (); + gid_t gid = getgid (); + + if (uid != euid || gid != egid) { + if (uid != 0) { + fprintf (stderr, "The -u and -g options require root privileges.\n"); + usage (stderr, argv[0]); + exit (EXIT_FAILURE); + } + + /* Need to become root first because setgid and setuid require it */ + if (seteuid (0) == -1) { + perror ("seteuid"); + exit (EXIT_FAILURE); + } + + /* Set gid and uid to command-line parameters */ + if (setgid (egid) == -1) { + perror ("setgid"); exit (EXIT_FAILURE); } + if (setuid (euid) == -1) { + perror ("setuid"); + exit (EXIT_FAILURE); + } + } + + /* Select the correct writer module. */ + struct writer *writer; + int nr_outputs; + + if (strcmp (format, "cpio") == 0) { + writer = &cpio_writer; + nr_outputs = 2; /* kernel and appliance (== initrd) */ + } + else if (strcmp (format, "ext2") == 0) { + writer = &ext2_writer; + nr_outputs = 3; /* kernel, initrd, appliance */ + } + else if (strcmp (format, "checksum") == 0) { + writer = &checksum_writer; + nr_outputs = 0; /* (none) */ + } + else { + fprintf (stderr, + "%s: incorrect output format (-f): must be cpio|ext2|checksum\n", + argv[0]); + exit (EXIT_FAILURE); } + /* [optind .. optind+nr_inputs-1] hostcpu [argc-nr_outputs-1 .. argc-1] + * <---- nr_inputs ----> 1 <---- nr_outputs ----> + */ char **inputs = &argv[optind]; - int nr_inputs = argc - optind - 3; + int nr_inputs = argc - nr_outputs - 1 - optind; + char **outputs = &argv[optind+nr_inputs+1]; + /*assert (outputs [nr_outputs] == NULL); + assert (inputs [nr_inputs + 1 + nr_outputs] == NULL);*/ if (nr_inputs < 1) { - usage (argv[0]); + fprintf (stderr, "%s: not enough files specified on the command line\n", + argv[0]); exit (EXIT_FAILURE); } /* See: https://bugzilla.redhat.com/show_bug.cgi?id=558593 */ - const char *hostcpu = argv[argc-3]; + const char *hostcpu = outputs[-1]; /* Output files. */ - const char *kernel = argv[argc-2]; - const char *initrd = argv[argc-1]; + const char *kernel = NULL, *initrd = NULL, *appliance = NULL; + if (nr_outputs > 0) + kernel = outputs[0]; + if (nr_outputs > 1) + initrd = appliance = outputs[1]; + if (nr_outputs > 2) + appliance = outputs[2]; if (verbose) { print_timestamped_message ("whitelist = %s, " "host_cpu = %s, " "kernel = %s, " - "initrd = %s", + "initrd = %s, " + "appliance = %s", whitelist ? : "(not specified)", - hostcpu, kernel, initrd); + hostcpu, kernel, initrd, appliance); int i; for (i = 0; i < nr_inputs; ++i) print_timestamped_message ("inputs[%d] = %s", i, inputs[i]); } /* Remove the output files if they exist. */ - unlink (kernel); - unlink (initrd); + if (kernel) + unlink (kernel); + if (initrd) + unlink (initrd); + if (appliance && initrd != appliance) + unlink (appliance); /* Create kernel output file. */ - const char *modpath; - modpath = create_kernel (hostcpu, kernel); + const char *modpath = create_kernel (hostcpu, kernel); if (verbose) print_timestamped_message ("finished creating kernel"); /* Create the appliance. */ - create_appliance (inputs, nr_inputs, whitelist, modpath, initrd); + create_appliance (hostcpu, inputs, nr_inputs, whitelist, modpath, + initrd, appliance, writer); if (verbose) print_timestamped_message ("finished creating appliance");