tags:

views:

110

answers:

3

I'm working on a init for an initramfs in C++ for Linux. This script is used to unlock the DM-Crypt w/ LUKS encrypted drive, and set the LVM drives to be available.

Since I don't want to have to reimplement the functionality of cryptsetup and gpg I am using system calls to call the executables. Using a system call to call gpg works fine if I have the system fully brought up already (I already have a bash script based initramfs that works fine in bringing it up, and I use grub to edit the command line to bring it up using the old initramfs). However, in the initramfs it never even acts like it gets called. Even commands like system("echo BLAH"); fail.

So, does anyone have any input?


Edit: So I figured out what was causing my errors. I have no clue as to why it would cause errors, but I found it.

In order to allow hotplugging, I needed to write /sbin/mdev to /proc/sys/kernel/hotplug...however I ended up switching around the parameters (on a function I wrote myself no less) so I was writing /proc/sys/kernel/hotplug to /sbin/mdev.

I have no clue as to why that would cause the problem, however it did.

+6  A: 

I believe the system() function executes your command in a shell. Is the shell executable mounted and available that early in your startup process? You might want to look into using fork() and execve().

EDIT: Be sure your cryptography tools are also on a mounted volume.

Amardeep
A: 

what do you have in initramfs ? You could do the following :

int main() {
   return system("echo hello world");

}

And then strace it in an initscript like this :

strace -o myprog.log myprog

Look at the log once your system is booted

shodanex
+5  A: 

Amardeep is right, system() on POSIX type systems runs the command through /bin/sh.

I doubt you actually have a legitimate need to invoke these programs you speak of through a Bourne shell. A good reason would be if you needed them to have the default set of environment variables, but since /etc/profile is probably also unavailable so early in the boot process, I don't see how that can be the case here.

Instead, use the standard fork()/exec() pattern:

int system_alternative(const char* pgm, char *const argv[])
{
    pid_t pid = fork();
    if (pid > 0) {
        // We're the parent, so wait for child to finish
        int status;
        waitpid(pid, &status, 0);
        return status;
    }
    else if (pid == 0) {
        // We're the child, so run the specified program.  Our exit status will
        // be that of the child program unless the execv() syscall fails.
        return execv(pgm, argv);
    }
    else {
        // Something horrible happened, like system out of memory
        return -1;
    }
}

If you need to read stdout from the called process or send data to its stdin, you'll need to do some standard handle redirection via pipe() or dup2() in there.

You can learn all about this sort of thing in any good Unix programming book. I recommend Advanced Programming in the UNIX Environment by W. Richard Stevens. The second edition coauthored by Rago adds material to cover platforms that appeared since Stevens wrote the first edition, like Linux and OS X, but basics like this haven't changed since the original edition.

Warren Young
Thank you very much for your help. You actually answered a question I didn't ask as to why the hell it says in the manpages to use the `exec()` family of functions instead of `system()` when they have very different effects. If you wouldn't mind, do you have a helpful link for piping output? (Either way, I did order the book you suggested, so I'll eventually get a good resource :) )
Thomas
Stevens covers it in section 14.2 in the first edition. In the second edition, it moved to section 15.2.
Warren Young
Thanks. I'll be sure to look for it.
Thomas