#include <sys/types.h>
#include <sys/time.h>
#include <assert.h>
+#include <grp.h>
+#include <pwd.h>
#include "error.h"
+#include "xstrtol.h"
#include "helper.h"
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"
"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
/* 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);
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;
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");