2

This is a follow up question to IPC::Open3 and determining if child is waiting for input though they admittedly are not related.

Take the following sample code:

use strict;
use warnings;
use IPC::Run3;

sub foo
{
    my $cmd = shift;
    my $CH_IN = "foo\n";
    run3($cmd, \$CH_IN, my $CH_OUT, my $CH_ERR);
    return 1;
}
my @LIST = ( 'command','arg1','arg2','arg3' );
foo \@LIST;

For the above code command is another perl script. The child script calls who -m to find who owns stdin from the terminal, which is intended to be used in case the child or parent script is called using root or another generic system user.

The error I get from the child when called by the parent is as follows.

Use of uninitialized value in split at child.pl line 354.
WHOAMI is undef

Child code below.

Code around line 354:

# Function Name:        'determine_user'
# Function Inputs:      'Optional Scalar: Username to login as'
# Function Returns:     'If username is provided: Scalar: the username provided, If username is not provided: The output of who -m'
# Function Description: 'Figures out the user to login as'
sub determine_user
{
    my $USER = shift;
    if (!defined $USER)
    {
        my $WHOAMI;
        open my $WHOPROC, q{-|}, 'who -m'                   or die "Unable to open 'who -m' for output.\n";
        $WHOAMI=<$WHOPROC>;
        close $WHOPROC                                      or die "Unable to close 'who -m'.\n";
        $USER       = split m{[\s]+}xms, $WHOAMI          or die "WHOAMI is undef\n";    # This is line 354
    }

    chomp $USER;

    return $USER;
}

The code above works just fine when I call the child script from the command line, but since who -m is trying to see who owns stdin of the terminal and the terminal has been replaced by the parent script, I believe I need to find a way to call who -m with an emulated pty when running from the parent. Whether that's by modifying the child or the parent or both doesn't matter, as I can modify both.

So my question is: how can I run the child inside of the parent and still rely on the output of who -m being fed to the child?

EDIT: ikegami has provided the best solution in the comments of his answer. It does not answer the question, but it does solve my problem.

My ssh daemon sets env var SSH_TTY, so you could use perl -MFile::stat -E'say scalar(getpwuid(stat($ENV{SSH_TTY})->uid))'. Default to scalar(getpwuid($>)) if it's not set.

Community
  • 1
  • 1
Speeddymon
  • 496
  • 2
  • 20
  • When I try running `who -m` from the terminal window it gives no output, but when I `ssh` into another server and then run `who -m`, it gives me the user name. I am not sure if this is related to your question though.. – Håkon Hægland Jan 06 '17 at 21:39
  • That's strange! What distribution and version? – Speeddymon Jan 06 '17 at 21:41
  • I am using Ubuntu 16.10. And I ran `who -m` from the `gnome-terminal` window – Håkon Hægland Jan 06 '17 at 21:43
  • Interesting, when I do that on CentOS7 in gnome-terminal it works fine. I am running gnome-terminal through a remote x session tho, let me try on a physical session. – Speeddymon Jan 06 '17 at 22:55
  • 1
    I finally got this working via `scalar(getpwuid(stat(readlink("/proc/$PID/fd/1"))->uid))` which removes the dependency on `who -m`, `ENV{$SSH_TTY}` and `ENV{$USER}` and prevents any need to emulate a pty to the called child process. – Speeddymon Jan 11 '17 at 20:23

1 Answers1

1

who -m specifically gives information about the terminal connected to its stdin.

-m     only hostname and user associated with stdin

You've replaced the terminal, so you can't obtain information from it.

$ who -m
ikegami  pts/1        ...

$ who -m </dev/null

$

You could drop the -m and use just who.

$ who </dev/null
ikegami  pts/1        ...

Alternatively, you could use $ENV{USER} or getpwuid($>) (the name of the user as which the process is executing) instead.

ikegami
  • 367,544
  • 15
  • 269
  • 518
  • Thanks for this suggestion but it unfortunately won't cover the cases where the parent is run as a generic system user like `root` or `oracle`. I've updated the question again to clarify this. – Speeddymon Jan 06 '17 at 13:21
  • Why wouldn't it? I take it you mean it's a setuid script, because `who -m` wouldn't work for a daemon, in which case `$ENV{USER}` and either `getpwuid($>)` or `getpwuid($<)` should work. – ikegami Jan 06 '17 at 22:57
  • What I'm saying is that the alternatives `$ENV{USER}` and `getpwuid($>)` output `root` and `who` without any option flags output all users, so on a multiuser server, this won't give the desired output. I realize that the terminal has been replaced. I saw something about emulating a pty for stdin with `open3` back when I was researching my first question, but as you rightly pointed out, `run3` should be used instead of `open3` so I am hoping to find a way to do that with `run3`. – Speeddymon Jan 06 '17 at 23:01
  • It is not a setuid script. It is intended to be run as a standard user who has their own unique UID. However, if any given SA or DBA switches user to a generic user such as oracle or root after the person opens an ssh session to the server, I want the script to still work without trying to login to the webapp as root or oracle. Therefore, I want it to find out who started the ssh session without some `ps|grep` hackery. – Speeddymon Jan 06 '17 at 23:05
  • My ssh daemon sets env var `SSH_TTY`, so you could use `perl -MFile::stat -E'say scalar(getpwuid(stat($ENV{SSH_TTY})->uid))'`. Default to `scalar(getpwuid($>))` if it's not set. – ikegami Jan 07 '17 at 00:01
  • Thanks, I'll look into that when I'm back in the office and let you know if it exists. – Speeddymon Jan 07 '17 at 00:02
  • That works perfectly and it eliminates the dependency on the external command which may or may not work. Thank you SO much! – Speeddymon Jan 10 '17 at 20:39
  • it works great for when I login, but after I switch user, the `$SSH_TTY` variable goes away. The `-` flag to `su` resets the environment. So for example `su -` or `su - oracle` will cause `$SSH_TTY` to be unset until I exit back to the account which the ssh session first logged into. – Speeddymon Jan 10 '17 at 20:50
  • You're still left with `who` without `-m`. – ikegami Jan 10 '17 at 21:12
  • No I agree, this is way better. I am probably going to have to do some hackery to get it to work with generic users unfortunately, but this is still much better than using who -m. – Speeddymon Jan 10 '17 at 21:14
  • You can tell `su` to not clear certain vars, I think. But it's cleared because it would be unsafe to trust the less-privileged user's `$SSH_TTY`. – ikegami Jan 10 '17 at 21:18
  • Yep. `su -m` but as you rightly pointed out, that's less secure. I'll look into the various Perl terminal modules and see if anything jumps out that might work. I know that the noecho mode of `Term::Readkey` works in the child script so there's possibly still some way from one of those modules. Anyway, thanks again. – Speeddymon Jan 10 '17 at 21:21
  • i thought you could do it more selectively using a config file, but maybe not. – ikegami Jan 10 '17 at 21:23
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/132855/discussion-between-speeddymon-and-ikegami). – Speeddymon Jan 10 '17 at 22:59