+/* Try to reproduce RHBZ#1184405.
+ * Note: needs to be linked statically.
+ * By Richard W.M. Jones <rjones@redhat.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <linux/fs.h>
+
+extern long init_module (void *, unsigned long, const char *);
+
+/* NB! Must match the number of disks in Makefile. */
+#define NR_SCSI_DISKS 6
+
+/* NB! Must match the module list in Makefile. Also they must be
+ * in module dependency order.
+ */
+static const char *modules[] = {
+ "virtio.ko",
+ "virtio_ring.ko",
+ "virtio_mmio.ko",
+ "virtio_scsi.ko",
+ NULL
+};
+
+static void load_modules (void);
+static void make_devices (void);
+static void run_test (const char *dev);
+
+int
+main (int argc, char *argv[])
+{
+ int i;
+ char dev[] = "/dev/sda";
+ int child_pids[NR_SCSI_DISKS-1];
+
+ printf ("init: started\n");
+ load_modules ();
+
+ /* Really we should wait for the kernel to probe devices, but bleah. */
+ sleep (2);
+
+ make_devices ();
+
+ for (i = 0; i < NR_SCSI_DISKS-1; ++i) {
+ child_pids[i] = fork ();
+ if (child_pids[i] == -1) {
+ perror ("fork");
+ exit (EXIT_FAILURE);
+ }
+ if (child_pids[i] == 0) { /* child */
+ dev[7] = 'a' + i + 1; /* /dev/sd[b..] */
+ run_test (dev);
+ _exit (EXIT_SUCCESS);
+ }
+ }
+
+ run_test (dev); /* /dev/sda test in parent */
+
+ for (i = 0; i < NR_SCSI_DISKS-1; ++i) {
+ waitpid (child_pids[i], NULL, 0);
+ }
+
+ printf ("init: parent process exiting\n");
+ exit (EXIT_SUCCESS);
+}
+
+static char buffer[BUFSIZ];
+#define MIN(a,b) ((a)<(b)?(a):(b))
+
+static void
+run_test (const char *dev)
+{
+ int fd;
+ uint64_t size;
+ ssize_t r;
+
+ printf ("init: testing %s\n", dev);
+ fflush (stdout);
+
+ fd = open (dev, O_RDONLY);
+ if (fd == -1) {
+ perror (dev);
+ _exit (EXIT_FAILURE);
+ }
+ if (ioctl (fd, BLKGETSIZE64, &size) == -1) {
+ perror ("ioctl: BLKGETSIZE64");
+ _exit (EXIT_FAILURE);
+ }
+
+ while (size > 0) {
+ r = read (fd, buffer, MIN (BUFSIZ, size));
+ if (r == -1) {
+ perror ("read");
+ _exit (EXIT_FAILURE);
+ }
+ size -= r;
+ }
+
+ if (close (fd) == -1) {
+ perror ("close");
+ _exit (EXIT_FAILURE);
+ }
+
+ printf ("init: test of %s finished successfully\n", dev);
+ fflush (stdout);
+}
+
+/* Originally taken from supermin's init.c */
+static void
+load_modules (void)
+{
+ size_t i;
+ int fd;
+ struct stat statbuf;
+ size_t size;
+ ssize_t r;
+
+ for (i = 0; modules[i] != NULL; ++i) {
+ printf ("init: loading module %s\n", modules[i]);
+
+ fd = open (modules[i], O_RDONLY);
+ if (fd == -1) {
+ perror (modules[i]);
+ exit (EXIT_FAILURE);
+ }
+ if (fstat (fd, &statbuf) == -1) {
+ perror (modules[i]);
+ exit (EXIT_FAILURE);
+ }
+ char buf[size = statbuf.st_size];
+ while (size > 0) {
+ r = read (fd, buf, size);
+ if (r == -1) {
+ perror ("read");
+ exit (EXIT_FAILURE);
+ }
+ size -= r;
+ }
+ close (fd);
+
+ if (init_module (buf, statbuf.st_size, "") != 0) {
+ fprintf (stderr, "insmod: ");
+ perror (modules[i]);
+ exit (EXIT_FAILURE);
+ }
+ }
+}
+
+/* Much less trouble than udev! */
+static void
+make_devices (void)
+{
+ int i;
+ char dev[] = "/dev/sda";
+
+ printf ("init: creating device nodes\n");
+
+ for (i = 0; i < NR_SCSI_DISKS; ++i) {
+ dev[7] = 'a' + i;
+ if (mknod (dev, S_IFBLK, makedev (8, 16*i)) == -1) {
+ perror (dev);
+ exit (EXIT_FAILURE);
+ }
+ }
+}