# @(#)92 1.6 src/bos/usr/bin/cdat/cdat.pm, cdat, bos720 7/14/11 17:56:29 # IBM_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # # bos720 src/bos/usr/bin/cdat/cdat.pm 1.6 # # Licensed Materials - Property of IBM # # COPYRIGHT International Business Machines Corp. 2010,2011 # All Rights Reserved # # US Government Users Restricted Rights - Use, duplication or # disclosure restricted by GSA ADP Schedule Contract with IBM Corp. # # IBM_PROLOG_END_TAG package cdat; require Exporter; @ISA = qw(Exporter); @EXPORT = qw(catgets DEBUG); use strict; use warnings; use POSIX; # needed for isatty, Termios use Fcntl ':mode'; # for S_IRWXG/S_IRWXO use Net::Ping; use Net::FTP; use MIME::Base64; # for encode_base64 use XML::LibXML; use messages; # # Globals. # # Retrieve the value of the CDAT_DEBUG environment variable. my $debug = $ENV{CDAT_DEBUG}; # # Constants. # my $ODMGET = 'ODMDIR=/etc/objrepos /usr/bin/odmget'; my $ODMDEL = 'ODMDIR=/etc/objrepos /usr/bin/odmdelete'; my $ODMADD = 'ODMDIR=/etc/objrepos /usr/bin/odmadd'; my $ODMCHG = 'ODMDIR=/etc/objrepos /usr/bin/odmchange'; my $LOCALE = '/usr/bin/locale'; my $EXPECT = '/usr/bin/expect'; my $SSH = '/usr/bin/ssh'; my $EXPECT_TELNET = '/usr/lib/cdat/telnet.exp'; my @CDAT_SCRIPTS = ('/usr/lib/cdat/types', '/var/adm/ras/cdat'); my $MANIFEST = 'manifest.xml'; my $DEF_PATH = '/cdat'; my $DEF_USER = 'cdat'; my $HOST_DB = ".cdatrc"; # localization my $DSPCAT = '/usr/bin/dspcat'; my $CATALOG = 'cdat.cat'; my $PING_TIMEOUT = 2; our $REMOTE_AUTO = 0; our $REMOTE_DONTASKUSER = 1; our $REMOTE_ASKUSER = 2; my $REMOTE_UIMASK = 3; our $REMOTE_PROBE = 4; ###################################################################### # Function: cdat::catgets # Purpose: Return the specified message from the message catalog. # Tasks: Call /usr/bin/dspcat to retrieve the message. # Input: Message number, default message # Output: Message from the message catalog or default message # if not found ###################################################################### sub catgets { my ($id, $default) = @_; my $str; $str= `$DSPCAT $CATALOG 1 $id 2>/dev/null`; $str = $default if ($? != 0); return $str; } ###################################################################### # Function: cdat::yes_no # Purpose: Ask a question (yes or no) to the user. # Tasks: Display a prompt and read an answer from standard input. # Input: Default answer (1=yes, 0=no) # Output: 1 if user answers [yY] # 0 if user answers [nN] # Default answer otherwise ###################################################################### sub yes_no { my ($rc) = @_; if ($rc) { # default answer is "yes" printf(catgets(MSG_YES_NO_YES, " (y/n) [y]: ")); } else { # default answer is "no" printf(catgets(MSG_YES_NO_NO, " (y/n) [n]: ")); } my $choice = ; chomp($choice); my $yes = catgets(MSG_YES, "y"); my $no = catgets(MSG_NO, "n"); $choice = lc($choice); if ($choice eq $yes) { $rc = 1; } elsif ($choice eq $no) { $rc = 0; } return $rc; } ###################################################################### # Function: cdat::get_password # Purpose: Read a password from the terminal. # Tasks: Disable echo from the terminal, read password and # re-enable echo. # Input: Prompt asking for a password # Output: Password or undef if standard input is not a TTY ###################################################################### sub get_password { my ($prompt) = @_; # Check that standard input is a tty return undef unless (isatty(STDIN_FILENO)); my $termios = POSIX::Termios->new(); return undef unless ($termios->getattr(STDIN_FILENO)); my $lflag = $termios->getlflag(); # Disable echoing $termios->setlflag($lflag & ~ECHO); return undef unless ($termios->setattr(STDIN_FILENO, TCSANOW)); print("$prompt"); my $passwd = ; print("\n"); # Restore terminal parameters $termios->setlflag($lflag); $termios->setattr(STDIN_FILENO, TCSANOW); chomp($passwd); return $passwd; } ###################################################################### # Function: cdat::define_password # Purpose: Ask user to chose a password and to confirm it. # Tasks: Read the password two times. # Input: Prompt asking for a password and prompt asking to # confirm the password # Output: Password or undef if standard input is not a TTY # or if passwords don't match ###################################################################### sub define_password { my ($prompt1, $prompt2) = @_; # Ask for password a first time my $passwd1 = get_password($prompt1); return undef unless (defined($passwd1)); # Ask to confirm password my $passwd2 = get_password($prompt2); return undef unless (defined($passwd2)); # Check that both inputs match return undef unless ($passwd1 eq $passwd2); return $passwd1; } ###################################################################### # Function: do_telnet # Purpose: Execute a command on a remote node using telnet. # Tasks: telnet to the remote node and automatically enter # user name, password and command using expect. # Input: user name, host name, node type, command to execute # Output: None ###################################################################### sub do_telnet { my ($hosts, $user, $host, $cmd, $mode) = @_; $mode &= $REMOTE_UIMASK; my $passwd = $hosts->{$host}{users}{$user}; if ($mode != $REMOTE_DONTASKUSER && (!defined($passwd) || $mode == $REMOTE_ASKUSER)) { # Ask for password now; it is needed for both FTP (to retrieve # login and password prompts) and telnet. my $prompt = sprintf(catgets(MSG_ENTER_PASSWD, "Enter %s@%s\'s password: "), $user, $host); $passwd = cdat::get_password($prompt); } if (!defined($passwd)) { $? = 1; return undef; } # Check if we have login and password prompts in host database. my $login_pattern = $hosts->{$host}{login_pattern}; my $password_pattern = $hosts->{$host}{password_pattern}; if (!defined($login_pattern) || !defined($password_pattern)) { # Either login or password prompt is unknown, try to retrieve # both. If it fails, we will get the default (C locale) values. ($login_pattern, $password_pattern) = cdat::get_telnet_patterns($host, $user, $passwd); } # Pass parameters to expect through environment variables. $ENV{EXPECT_PATTERN1} = $login_pattern; $ENV{EXPECT_PATTERN2} = $password_pattern; $ENV{EXPECT_PASSWORD} = $passwd; my @lines = `$EXPECT_TELNET '$user' '$host' '$cmd' 2>/dev/null`; $? = WEXITSTATUS($?); shift @lines; # remove echo'ed command pop @lines; # remove last prompt # telnet uses DOS-style EOL CR-LF, so convert to UNIX foreach (@lines) { s/\r$//; } return @lines; } ###################################################################### # Function: do_ssh # Purpose: Execute a command on a remote node using ssh. # Tasks: ssh to the remote node and execute command. # Input: user name, host name, and command to execute # Output: None ###################################################################### sub do_ssh { my ($hosts, $user, $host, $cmd, $mode) = @_; my @lines; $mode &= $REMOTE_UIMASK; if ($mode == $REMOTE_AUTO) { @lines = `$SSH -i $ENV{HOME}/.ssh/id_rsa -o UserKnownHostsFile=$ENV{HOME}/.ssh/known_hosts $user\@$host '$cmd'`; } elsif ($mode == $REMOTE_DONTASKUSER) { @lines = `$SSH -i $ENV{HOME}/.ssh/id_rsa -o UserKnownHostsFile=$ENV{HOME}/.ssh/known_hosts -o StrictHostKeyChecking=yes -o PasswordAuthentication=no -o NumberOfPasswordPrompts=0 $user\@$host '$cmd'`; } elsif ($mode == $REMOTE_ASKUSER) { @lines = `$SSH -i $ENV{HOME}/.ssh/id_rsa -o UserKnownHostsFile=$ENV{HOME}/.ssh/known_hosts -o PubkeyAuthentication=no $user\@$host '$cmd'`; } $? = WEXITSTATUS($?); return @lines; } ###################################################################### # Function: rexec # Purpose: implements rexec (port 512) protocol # Tasks: send a command to a remote host using rexec protocol # Input: user name, password, host name, and command to execute # Output: result of the command ###################################################################### sub rexec { my ($user, $passwd, $host, $cmd) = @_; my ($result, $output); my $sock = IO::Socket::INET->new(PeerAddr => $host, PeerPort => 'exec(512)', Proto => 'tcp'); $sock->syswrite("0\0", 2); $sock->syswrite($user."\0", length($user) + 1); $sock->syswrite($passwd."\0", length($passwd) + 1); $sock->syswrite($cmd."\0", length($cmd) + 1); $result = $sock->sysread($output, 1); if ($output ne chr(0)) { $? = 1; } else { $? = 0; } return $sock->getlines; } ###################################################################### # Function: do_rexec # Purpose: execute a command on a remote host using rexec protocol # Tasks: send a command to a remote host using rexec protocol # read password from DB and retrieve the exit code number # Input: user name, host name, and command to execute # Output: result of the command and exit code ###################################################################### sub do_rexec { my ($hosts, $user, $host, $cmd, $mode) = @_; $mode &= $REMOTE_UIMASK; my $passwd = $hosts->{$host}{users}{$user}; if ($mode != $REMOTE_DONTASKUSER && (!defined($passwd) || $mode == $REMOTE_ASKUSER)) { my $prompt = sprintf(catgets(MSG_ENTER_PASSWD, "Enter %s@%s\'s password: "), $user, $host); $passwd = get_password($prompt); } if (!defined($passwd)) { $? = 1; return undef; } my @lines = rexec($user, $passwd, $host, " ( ".$cmd." ) ; echo \$?"); if ($? == 0) { # exit status is printed in the last line $? = pop(@lines); } return @lines; } ###################################################################### # Function: cdat::remote_cmd # Purpose: Execute a shell command on a remote node. # Tasks: Execute the specified command on the remote node. # Input: Node (user, host), command to execute and flags # Output: Status code returned by the remote command ###################################################################### sub remote_cmd { my ($user, $host, $cmd, $mode) = @_; my $rc = 0; my @lines; my %hosts; my $protocol; read_host_db(\%hosts); $protocol = $hosts{$host}{protocol}; if (!defined($protocol) || ($mode & $REMOTE_PROBE)) { if ( -x $SSH && cdat::check_remote_protocol($host, 'ssh')) { $protocol = "ssh"; } elsif (cdat::check_remote_protocol($host, 'exec')) { $protocol = "exec"; } elsif (cdat::check_remote_protocol($host, 'telnet')) { $protocol = "telnet"; } else { return undef; } } if ( -x $SSH && $protocol eq 'ssh') { @lines = do_ssh(\%hosts, $user, $host, $cmd, $mode); return @lines; } elsif ($protocol eq 'exec') { @lines = do_rexec(\%hosts, $user, $host, $cmd, $mode); return @lines; } elsif ($protocol eq 'telnet') { @lines = do_telnet(\%hosts, $user, $host, $cmd, $mode); return @lines; } else { return undef; } } ###################################################################### # Function: cdat::get_node_info # Purpose: Parse raw node (in the form "type:user@name"). # Tasks: Split components of raw node string (type, user and # name) and return them. # Input: Raw node string # Output: Node name, type and user name (or undef) ###################################################################### sub get_node_info { my ($item) = @_; my %info; my ($type, $node) = split(/:/, $item, 2); if ($type ne 'HMC' && $type ne 'VIOS' && $type ne 'LPAR' && $type ne 'PSCALE') { return undef; } my ($user, $name) = split(/@/, $node, 2); if (!defined($name)) { $name = $user; $user = undef; } $info{user} = $user; $info{type} = $type; return ($name, %info); } ###################################################################### # Function: cdat::read_nodes_from_array # Purpose: Parse raw nodes (in the form "type:user@name"). # Tasks: Split components of raw nodes (type, user and name) # and store them into a hash of nodes. # Input: Array of raw nodes # Output: Hash of nodes ###################################################################### sub read_nodes_from_array { my (@rawnodes) = @_; my %nodes; # Each entry in the array is of the form: # :[@] foreach my $rawnode (@rawnodes) { my ($type, $node) = split(/:/, $rawnode, 2); if (!defined($node) || length($type) == 0 || length($node) == 0) { printf(STDERR catgets(MSG_SKIPPING_INVALID_NODE, "Skipping invalid node %s.\n"), $rawnode); next; } my ($user, $name) = split(/@/, $node, 2); if (!defined($name)) { # no 'user@' part found $nodes{$user}{type} = $type; } else { $nodes{$name}{type} = $type; $nodes{$name}{user} = $user; } } return %nodes; } ###################################################################### # Function: cdat::read_nodes_from_files # Purpose: Parse a set of nodes.txt files. # Tasks: For each file, parse each line of the file and build # a hash of nodes. # Input: Array of filenames # Output: Hash of nodes or undef ###################################################################### sub read_nodes_from_files { my (@filenames) = @_; my %nodes; my $rc; foreach (@filenames) { # Each line of the file is of the form: # :[@] $rc = open(IN, $_); if (!$rc) { printf(STDERR catgets(MSG_CANNOT_OPEN_FILE, "Cannot open file %s.\n"), $_); next; # skip this file } while () { next if /^\s*#/; # skip single-line comments from file # ignore trailing comments s/#.*$//; s/\s+$//; chomp; my ($name, %info) = get_node_info($_); $nodes{$name}{type} = $info{type}; $nodes{$name}{user} = $info{user}; } close(IN); } return %nodes; } ###################################################################### # Function: cdat::collect_node_info # Purpose: Retrieve information from a node. # Tasks: Connect to the node and retrieve the machine id, # the LPAR id (if any) and the timezone. # Input: Node (type, user and name) # Output: Information about the node (machine id, LPAR id, TZ) ###################################################################### sub collect_node_info { my ($type, $user, $name) = @_; my %info; if ($type eq 'HMC' || $type eq 'LPAR' || $type eq 'VIOS') { my @tz = remote_cmd($user, $name, 'date +%Z', $cdat::REMOTE_DONTASKUSER); return %info if ($? != 0 || !defined($tz[0])); chomp($tz[0]); $info{tz} = $tz[0]; } else { # this is for nodes of type PSCALE for instance $info{tz} = 'unknown'; } if ($type eq 'LPAR' || $type eq 'VIOS') { my @uname = remote_cmd($user, $name, 'uname -mL', $cdat::REMOTE_DONTASKUSER); return %info if ($? != 0 || !defined($uname[0])); ($info{machine_id}, $info{lpar_id}) = split(/\W/, $uname[0]); } return %info; } ###################################################################### # Function: cdat::odm_set_user # Purpose: Store username inside ODM. # Tasks: Store the username in the SWServAt ODM database, # under the "cdat_user" attribute. # Input: Username # Output: None ###################################################################### sub odm_set_user { my ($user) = @_; my %attributes; odm_del('SWservAt', 'attribute=cdat_user'); $attributes{attribute} = 'cdat_user'; $attributes{value} = "$user"; $attributes{deflt} = "$DEF_USER"; odm_add("SWservAt", %attributes); } ###################################################################### # Function: cdat::odm_get_user # Purpose: Retrieve username from ODM. # Tasks: Retrieve the username from the SWServAt ODM database, # that is stored under the "cdat_user" attribute. # Input: None # Output: Username or undef ###################################################################### sub odm_get_user { my @list = odm_get('SWservAt', 'attribute=cdat_user'); return undef if (!@list); my %item = %{$list[0]}; return $item{value}; } ###################################################################### # Function: cdat::odm_set_path # Purpose: Store path to repository inside ODM. # Tasks: Store the path to the repository in the SWServAt ODM # database, under the "cdat_directory" attribute. # Input: Path # Output: None ###################################################################### sub odm_set_path { my ($path) = @_; my %attributes; odm_del('SWservAt', 'attribute=cdat_directory'); $attributes{attribute} = 'cdat_directory'; $attributes{value} = "$path"; $attributes{deflt} = "$DEF_PATH"; odm_add('SWservAt', %attributes); } ###################################################################### # Function: cdat::odm_get_path # Purpose: Retrieve path to repository from ODM. # Tasks: Retrieve the path to the repository from the SWServAt # ODM database, that is stored under the "cdat_directory" # attribute. # Input: None # Output: Path or undef ###################################################################### sub odm_get_path { my @list = odm_get('SWservAt', 'attribute=cdat_directory'); return undef if (!@list); my %item = %{$list[0]}; # remove trailing "/" from path $item{value} =~ s/\/+$//; return $item{value}; } ###################################################################### # Function: cdat::locale_charmap # Purpose: Retrieve the current character mapping. # Tasks: Retrieve the current character mapping. # Input: None # Output: Character mapping (e.g "ISO8859-1") ###################################################################### sub locale_charmap { my $charmap = `$LOCALE charmap`; if (!defined($charmap) || $charmap eq '') { $charmap = "ISO8859-1"; } else { chomp($charmap); } return $charmap; } ###################################################################### # Function: cdat::DEBUG # Purpose: Print diagnostic message. # Tasks: Print diagnostic message to stderr if the environment # variable CDAT_DEBUG is set and its value is greater # or equal to the specified debug level. # Input: debug level, diagnostic message # Output: None ###################################################################### sub DEBUG { return unless defined($debug); my $level = shift; print(STDERR @_) if ($debug >= $level); } ###################################################################### # Function: cdat::check_remote_protocol # Purpose: Check remote system available service # Tasks: Check if remote system is running a given service # by testing if the service port is open # Input: remote hostname and protocol to test ("ssh", "ftp", ...) # Output: 1 on success, 0 on error ###################################################################### sub check_remote_protocol { my ($hostname, $protocol) = @_; my $rc; # probe the remote port my $p = Net::Ping->new('tcp', $PING_TIMEOUT); $p->{'port_num'} = getservbyname($protocol, 'tcp'); $p->{'econnrefused'} = 1; $rc = $p->ping($hostname); $p->close(); return $rc; } ###################################################################### # Function: cdat::get_telnet_patterns # Purpose: get from remote system login and password prompts # Tasks: Connect to a given remote system and, using ftp, # retrieve used language (from /etc/environment, LANG) # and extract login and password promptes from # /usr/lib/nls/msg/$LANG/tsm.cat # Input: remote hostname, user login and password # Output: an array with two entries, the login and password # patterns ###################################################################### sub get_telnet_patterns { my ($hostname, $user, $passwd) = @_; my ($login_pattern, $password_pattern) = ("*ogin: ", "*assword:"); my $cdat_environment = "/tmp/cdat_environment.$$"; my $cdat_tsm_cat = "/tmp/cdat_tsm_cat.$$"; my ($rc, $line); my $ftp = Net::FTP->new($hostname); if (!defined($ftp)) { return ($login_pattern, $password_pattern); } $rc = $ftp->login($user, $passwd); if (!$rc) { return ($login_pattern, $password_pattern); } if (!$ftp->cwd("/etc")) { $ftp->quit(); return ($login_pattern, $password_pattern); } $rc = $ftp->get("environment", $cdat_environment); if (!$rc) { $ftp->quit(); return ($login_pattern, $password_pattern); } open(ENVFILE, $cdat_environment); unlink($cdat_environment); my $LANG = "C"; while (defined($line = )) { if ($line =~ m/^LANG=/) { $LANG = $line; $LANG =~ s/^LANG=//; chomp($LANG); } } close(ENVFILE); if ($LANG eq "C") { $ftp->quit(); return ($login_pattern, $password_pattern); } $rc = $ftp->cwd("/usr/lib/nls/msg/$LANG"); if (!$rc) { $ftp->quit(); return ($login_pattern, $password_pattern); } $rc = $ftp->get("tsm.cat", $cdat_tsm_cat); if (!$rc) { $ftp->quit(); return ($login_pattern, $password_pattern); } my @lines = `$DSPCAT $cdat_tsm_cat 1 50 2>/dev/null`; if ($? == 0) { $login_pattern = $lines[-1]; } @lines = `$DSPCAT $cdat_tsm_cat 1 2 2>/dev/null`; if ($? == 0) { $password_pattern = $lines[-1]; $password_pattern =~ s/%s/*/g; } unlink($cdat_tsm_cat); return ($login_pattern, $password_pattern); } ###################################################################### # Function: cdat::encode # Purpose: Encode passwords. # Tasks: Symetric function to encode passwords. # Input: Password in clear text # Output: Password encoded ###################################################################### sub encode { my ($passwd) = @_; my $key = 'ClusterDataAggregationTool'; my $hidden = ''; my $pos = 0; for (my $i = 0; $i < length($passwd); $i++) { vec($hidden, $i, 8) = vec($passwd, $i, 8) ^ vec($key, $pos, 8); $pos = ($pos + 1) % length($key); } return encode_base64($hidden); } ###################################################################### # Function: cdat::decode # Purpose: Decode passwords. # Tasks: Symetric function to decode passwords. # Input: Password encoded # Output: Password in clear text ###################################################################### sub decode { my ($hidden) = @_; my $key = 'ClusterDataAggregationTool'; my $passwd = ''; my $pos = 0; $hidden = decode_base64($hidden); for (my $i = 0; $i < length($hidden); $i++) { vec($passwd, $i, 8) = vec($hidden, $i, 8) ^ vec($key, $pos, 8); $pos = ($pos + 1) % length($key); } return $passwd; } ###################################################################### # Function: cdat::get_host_db # Purpose: Get path to the user host database # Tasks: Compute the home directory # Compute path of the host database # Input: None # Output: Path to the host database ###################################################################### sub get_host_db { my $user = odm_get_user(); return undef unless defined($user); my ($name, $pass, $uid, $gid, $quota, $comment, $gcos, $dir, $shell, $expire) = getpwnam($user); return undef unless defined($dir); return "$dir/$HOST_DB"; } ###################################################################### # Function: cdat::read_host_db # Purpose: Read database storing remote host access data # Tasks: Read users password for remote access # Read host login and password patterns for telnet # Input: A hashtable reference to store data # Output: None ###################################################################### sub read_host_db { my ($hosts) = @_; my $host_db = get_host_db(); return unless (defined($host_db) && -r "$host_db"); my $mode = (stat(_))[2]; if ($mode & (S_IRWXG | S_IRWXO)) { printf(catgets(MSG_INVALID_PERM, "File %s has invalid permissions. Please fix it. It should be readable/writable by owner only.\n"), $host_db); exit(1); } my $parser = XML::LibXML->new(); my $tree = $parser->parse_file("$host_db"); my $root = $tree->getDocumentElement; foreach my $host_elt ($root->getElementsByTagName('host')) { my $name = $host_elt->getAttribute('name'); next if !defined($name); my $protocol = $host_elt->getAttribute('protocol'); next if !defined($protocol); $hosts->{$name}{protocol} = $protocol; if ($protocol eq 'telnet') { my @telnet_elts = $host_elt->getElementsByTagName('telnet'); if (@telnet_elts) { $hosts->{$name}{login_pattern} = $telnet_elts[0]->getAttribute('login_pattern'); $hosts->{$name}{password_pattern} = $telnet_elts[0]->getAttribute('password_pattern'); } } foreach my $user_elt ($host_elt->getElementsByTagName('user')) { my $username = $user_elt->getAttribute('name'); next if !defined($username); my $password = $user_elt->getAttribute('password'); next if !defined($password); $hosts->{$name}{users}{$username} = decode($password); } } } ###################################################################### # Function: cdat::write_host_db # Purpose: write database storing remote server data # Tasks: Store users password for remote access # Store host login and password patterns for expect # Input: A hashtable reference to stored data # Output: None ###################################################################### sub write_host_db { my (%hosts) = @_; my $host_db = get_host_db(); return unless defined($host_db); # always overwrite existing file chmod(0600, $host_db); my $tree = XML::LibXML->createDocument('1.0', cdat::locale_charmap); my $root_elt = $tree->createElement('host-list'); return if !defined($root_elt); $tree->setDocumentElement($root_elt); foreach my $name (keys(%hosts)) { my $host_elt = $tree->createElement('host'); next if !defined($host_elt); $host_elt->setAttribute('name', $name); my $protocol = $hosts{$name}{protocol}; $host_elt->setAttribute('protocol', $protocol); if ($protocol eq 'telnet') { my $telnet_elt = $tree->createElement('telnet'); next if !defined($telnet_elt); $telnet_elt->setAttribute('login_pattern', $hosts{$name}{login_pattern}); $telnet_elt->setAttribute('password_pattern', $hosts{$name}{password_pattern}); $host_elt->appendChild($telnet_elt); } my $user_elt; my $users = $hosts{$name}{users}; foreach my $user (keys(%$users)) { $user_elt = $tree->createElement('user'); next if !defined($user_elt); $user_elt->setAttribute('name', $user); my $password = encode($users->{$user}); $user_elt->setAttribute('password', $password); $host_elt->appendChild($user_elt); } $root_elt->appendChild($host_elt) if defined($user_elt); } $tree->toFile($host_db, 2); chmod(0600, $host_db); } ###################################################################### # Function: cdat::get_collecttypes # Purpose: Get the list of supported collect types and paths. # Tasks: Search for collect types definitions in default dir # and in environment variable CDAT_TYPES. # Input: None # Output: Hash of collect type name -> full path to scripts ###################################################################### sub get_collecttypes { my %collecttypes; my @paths; # check if the CDAT_TYPES environnement variable is set my $env = $ENV{CDAT_TYPES}; @paths = split(/:/, $env) if defined($env); # always prepend "system" scripts @paths = (@CDAT_SCRIPTS, @paths); foreach my $path (@paths) { next if (!opendir(DIR, $path)); while (defined(my $type = readdir(DIR))) { next if ($type eq '.' || $type eq '..'); # it is a collect type if we have a manifest.xml file # and a collect script next if (! -f "$path/$type/$MANIFEST" || ! -x "$path/$type/$type"); # keep only the first occurence next if (defined($collecttypes{$type})); $collecttypes{$type} = "$path/$type"; } closedir(DIR); } return %collecttypes; } ###################################################################### # Function: cdat::switch_user # Purpose: Switch to the collect user id # Tasks: Compute the collect user id # Set the effective user id to this id # Set environment variable according to this id # Input: None # Output: None ###################################################################### sub switch_user { my $user = odm_get_user(); if (!defined($user) || length($user) == 0) { printf(STDERR catgets(MSG_LOCAL_USER_NOT_DEF, "The cdat local user is not defined.\n". "Please, run 'cdat init' first.\n")); exit(1); } my ($name, $pass, $uid, $gid, $quota, $comment, $gcos, $dir, $shell, $expire) = getpwnam($user); if (!defined($uid)) { printf(STDERR catgets(MSG_CANNOT_SWITCH_TO_USER, "Cannot switch to user %s.\n"), $user); exit(1); } $> = $uid; if ($! != 0) { printf(STDERR catgets(MSG_CANNOT_SWITCH_TO_USER, "Cannot switch to user %s.\n"), $user); exit(1); } $ENV{HOME} = $dir; $ENV{USER} = $user; $ENV{LOGIN} = $user; $ENV{LOGNAME} = $user; } ###################################################################### # Function: cdat::odm_get # Purpose: Perform an ODM query. # Tasks: Call odmget and extract the results. # Input: Object class, Criteria # Output: Array of attribute values or undef ###################################################################### sub odm_get { my ($class, $criteria) = @_; my @result; my %current = (); if (!defined($class)) { return undef; } open(OUTPUT, "$ODMGET -q \"$criteria\" $class |") or return undef; while () { chomp; if ($_ eq "$class:") { if (%current ne 0) { my %item = %current; %current = (); push(@result, \%item); } } elsif ($_ =~ m/ = /) { my ($name, $value) = split( / = / ); $name =~ s/^[ \t]*//; $value =~ s/"//g; next if length($name) == 0; $current{$name} = $value; } } if (%current ne 0) { my %item = %current; push(@result, \%item); } close OUTPUT; return @result; } ###################################################################### # Function: cdat::odm_del # Purpose: Delete an object from ODM. # Tasks: Call odmdel to delete the object. # Input: Object class, Criteria # Output: Code returned by odmdel or undef ###################################################################### sub odm_del { my ($class, $criteria) = @_; if (!defined($criteria) || !defined($class)) { return undef; } my $result = `$ODMDEL -o "$class" -q "$criteria"`; $result =~ s/^0518-307 odmdelete: //; $result =~ s/ objects deleted//; return $result; } ###################################################################### # Function: cdat::odm_add # Purpose: Add an object into ODM. # Tasks: Call odmadd to add the object. # Input: Object class, Attribute # Output: None ###################################################################### sub odm_add { my ($class, %attributes) = @_; open ODMDB, "|$ODMADD"; print(ODMDB "$class:\n"); while (my($name, $value) = each(%attributes)) { print(ODMDB "$name=\"$value\"\n"); } close ODMDB; } ###################################################################### # Function: cdat::odm_change # Purpose: Change an object in ODM. # Tasks: Call odmchange to change the object. # Input: Object class, Criteria, Attribute # Output: None ###################################################################### sub change { my ($class, $criteria, %attributes) = @_; open ODMDB, "|$ODMCHG -o \"$class\" -q \"$criteria\""; while (my($name, $value) = each(%attributes)) { print(ODMDB "$name=\"$value\"\n"); } close ODMDB; } 1;