Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask

Exec Fork Trick

by ginseng (Pilgrim)
on Jul 01, 2001 at 15:38 UTC ( #93012=perlquestion: print w/replies, xml ) Need Help??
ginseng has asked for the wisdom of the Perl Monks concerning the following question:

I wrote a program not too long ago that needed to run another program and slurp up the output. I used something I believe is called an "Exec Fork", like this:

open PROG, "-|" || exec './prog', 'arg'; @chunks = <PROG>; close PROG;

I'm assuming that I can do that in reverse, sending output from my script as input to the foreign program. Specifically, I'm trying to run passwd, as root, from a perl script.

My code looks like

open (PASSWD, "|-") || exec 'passwd', $name; print PASSWD "$password\n$password\n"; close PASSWD;

and it does launch passwd, but still prompts for the new password at the console, interactively. The script doesn't seem to be printing to it.

I assume I'm doing something wrong. Can you see what it is?

Before I get a (probably deserved) "don't reinvent the wheel; use X" response, I did find and try Unix::PasswdFiles, but it didn't seem to work with the shadow file on my OpenBSD installation. In fact, it replaced the entire file with the one line it was adding. That would have been very scary if I hadn't backed up first. I decided it was safer to use the supplied program...if I can get it to work. I'm still open to alternative suggestions, but I didn't want you to think I was ignoring all of the work that's come before me ;)

Replies are listed 'Best First'.
Re: Exec Fork Trick
by maverick (Curate) on Jul 01, 2001 at 17:46 UTC
    It's been a while since I've seen the exact explination for this, so I may not have it exactly right. Basically on most Unix systems, commands like passwd (which require paranoid security) do a little black magic and read their input directly from the tty not from STDIN. Purposely preventing exactly what you're trying to do.

    What you could do is write a small wrapper script in a language called 'Expect'. Expect was written to emulate the behavior of a person at the keyboard. This is a technique I have used.

    I would suggest trying first. I've never used it, so I can't say for sure if it will work.


Re: Exec Fork Trick
by bikeNomad (Priest) on Jul 01, 2001 at 19:18 UTC
    You may be able to get this to work without running Expect by using IO::Pty. Under Unix, the /dev/pty arrangement allows you to have pseudo-tty pairs that look like real tty lines, but you can do I/O to (this is what Expect uses, as well as xterm, etc.).

    You'd set up your child process (passwd) with the slave pty as the stdin, stdout, and stderr.

    Another CPAN module that may help in conjunction with IO::Pty is IPC::Run which allows you to run processes with pty connections. From its manpage:

    Refusing to accept input unless stdin is a tty. Some programs, for security reasons, will only accept certain types of input from a tty. su, notable, will not prompt for a password unless it's connected to a tty. If this is your situation, use a pseudo terminal ('<pty<' and '>pty>').
Re: Exec Fork Trick
by kschwab (Priest) on Jul 01, 2001 at 18:35 UTC
    The big problem is that /bin/passwd uses your TTY instead of STDIN/STDOUT to interact with the user. The other response(s) mentioned Expect, which is a good general purpose solution to the problem.

    Here's another idea:

    I'm not sure if OpenBSD supports PAM yet (FreeBSD does..). If it does, you can use Authen::PAM to change passwords. A while back, I'd submitted a a node showing how to use Authen::PAM to change a password.

           The big problem is that /bin/passwd uses your TTY instead of STDIN/STDOUT to interact with the user.
      This is absolutally true in most versions of passwd. However, some of the more recent versions of passwd provide you with an option to accept input from STDIN. This is the case with passwd that came with RedHat 7.1, and is packaged as version 0.64. It's described in the man page (man passwd), but in short all you have to say is

      passwd --stdin

      So ginseng, to modify the code you wrote to make use of this, you could do:
      # The runtime options to be passed to passwd my $passwd_options = "--stdin"; # All the arguments to be passed to passwd, including name my @args = qw($passwd_options $name); open (PASSWD, "|-") || exec 'passwd', @args; print PASSWD "$password\n$password\n"; close PASSWD;
      Again though, remember that you need a recent version of passwd. Just do a 'man passwd' and check to see if the --stdin option exists. Good luck,
Re: Exec Fork Trick
by marius (Hermit) on Jul 01, 2001 at 21:36 UTC
    I don't know about OpenBSD, but on FreeBSD boxen you can use the pw(8) command with the -h flag to pass it a filehandle. Using pw usermod <username> -h 0 will allow you to pass the password to STDIN. However, to keep it off STDIN, you could create another filehandle, snag it's FD#, and pass it through that FH.

    Hope that helps, lots more info in the pw(8) man page.

Re: Exec Fork Trick
by blakem (Monsignor) on Jul 02, 2001 at 12:42 UTC

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://93012]
Approved by root
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others taking refuge in the Monastery: (8)
As of 2017-06-23 10:47 GMT
Find Nodes?
    Voting Booth?
    How many monitors do you use while coding?

    Results (542 votes). Check out past polls.