From worley@compass.com Fri Jan 17 18:43:06 1992 SUB: Re: yppasswd in perl? SUM: Tom Christiansen <tchrist@convex.COM>, tchrist@convex.COM (Tom Christiansen)->Perl-Users@fuggles.acc.Virginia.EDU >From the keyboard of jbryans@beach.csulb.edu (Jack Bryans): :Minor edits to the Camel Book's passwd.pl to remove typos & one :off-the-Wall-ism made it a working program. Now we'd like to make a :yppasswd out of it, but we're unsure what tack to take. : :Can RPC calls be made directly from perl? Or will we need to write a C :sub-routine to be called from perl to get the yppasswd(oldpass,newpw) :functionality? Sure they can. It's just not fun. Here's an old article that should point the way. These days I would rely on c2ph for the structure definitions though. --tom Date: 21 Aug 90 20:37:22 GMT From: worley@compass.com (Dale Worley) Subject: Querying rstatd from Perl Reply-to: worley@compass.com Organization: The Internet Newsgroups: comp.lang.perl Message-ID: <1990Aug21.203722.26933@uvaarpa.Virginia.EDU> Forwarded: Mon, 04 Mar 91 13:46:08 CST connolly This is to thank all the people who sent me information about rstatd and about using UDP connections in Perl: Larry Wall Carl Smith Vipin Samar Guy Harris Michael van Elst (and any I've forgotten!) My original idea was to have a program query all the hosts on our network to determine which were idle (according to some criterion). I want to know: load average, amount of virtual memory free, and keyboard idle time. Load average is available from rstatd, keyboard idle time is (probably) available from rusersd, and it seems that free virtual memory (printed by pstat -s) is probably not available from any daemon, unless I write my own. (While we're at it, is there any way to get the genuine keyboard idle time when SunTools is running? 'w' shows the console as being idle for a very long time, while input generated by a shelltool into its pty stimulated by '^[[11t' are recorded as if it is genuine input.) To interact with a UDP daemon in Perl, you need to open the connection with the UDP protocol, rather than TCP. Each print sends a UDP packet, and each read gets a UDP packet. You can also use send or recv without connecting the socket. RPC and XDR are described in Network Programming, chapters 5 and 6 (set I, vol. XI in the 4.0.3 documentation). Using the RPC protocol is much simpler than it appears from the manual: XDR is just a machine independent way to represent data structures. The basic rule is that integers are represented as four-byte network order integers (format N for pack and unpack). Also, don't forget that port 111 (a/k/a sunrpc) only handles portmapper requests -- you either have to ask the portmapper for the port of the service you want, or you have to ask the portmapper to forward the request for you (the approach taken by the code below). #!/usr/local/bin/perl ($host) = @ARGV; die "usage: $0 hostname\n" unless $host; $pat = 'S n C4 x8'; $stream = 1; $datagram = 2; $inet = 2; $tcp = 6; $udp = 17; ($name,$aliases,$port) = getservbyname('sunrpc','udp'); if ($host =~ /^\d+\./) { @bytes = split(/\./,$host); } else { ($name,$aliases,$addrtype,$length,@addrs) = gethostbyname($host); die "Can't lookup $host\n" unless $name; @bytes = unpack("C4",$addrs[0]); } $this = pack($pat,$inet,0, 0,0,0,0); $that = pack($pat,$inet,$port,@bytes); socket(S,2,$datagram,$udp) || die "socket: $!\n"; bind(S,$this) || die "bind: $!\n"; connect(S,$that) || die "connect: $!\n"; select(S); $| = 1; select(stdout); $| = 1; #while (1) { print S pack("N13", 1956, 0, 2, 100000, 2, 5, 0, 0, 0, 0, 100001, 3, 1); read(S, $_, 32767); @r = unpack("N" . length($_)/4, $_); #print join(' ', @r), "\n"; print $r[26]/256, ' ', $r[27]/256, ' ', $r[28]/256, "\n"; if ($r[0] != 1956) { die "xid error\n"; } elsif ($r[1] != 1) { die "Not a reply!\n"; } elsif ($r[2] == 1) { if ($r[3] == 0) { die "Rejected - RPC_MISMATCH\n"; } elsif ($r[3] == 1) { die "Rejected - AUTH_ERROR\n"; } else { die "Rejected - unknown code\n"; } } else { print '', (("SUCCESS", "PROG_UNAVAIL", "PROG_MISMATCH", "PROC_UNAVAIL", "GARBAGE_ARGS")[$r[5]]), "\n"; } Dale Worley Compass, Inc. worley@compass.com -- Hall's Laws of Politics: (1) The voters want fewer taxes and more spending. (2) Citizens want honest politicians until they want something fixed. (3) Constituency drives out consistency (i.e., liberals defend military spending, and conservatives social spending, in their own districts). -- "GUIs normally make it simple to accomplish simple actions and impossible to accomplish complex actions." --Doug Gwyn (22/Jun/91 in comp.unix.wizards) Tom Christiansen tchrist@convex.com convex!tchrist From me@anywhere.EBay.Sun.COM Sun Jan 19 16:45:47 1992 To: Perl-Users@fuggles.acc.Virginia.EDU From: me@anywhere.EBay.Sun.COM (Wayne Thompson) Subject: Re: yppasswd in perl? Date: 18 Jan 92 17:39:43 GMT SUB: Re: yppasswd in perl? SUM: me@anywhere.EBay.Sun.COM (Wayne Thompson)->Perl-Users@fuggles.acc.Virginia.EDU In article <DRW.92Jan17161416@kutta.mit.edu>, drw@kutta.mit.edu (Dale R. Worley) writes: |> In article <JBRYANS.92Jan16164859@beach.csulb.edu> jbryans@beach.csulb.edu (Jack Bryans) writes: |> Can RPC calls be made directly from perl? Or will we need to write a C |> sub-routine to be called from perl to get the yppasswd(oldpass,newpw) |> functionality? |> |> Well, you *can* do RPC calls from Perl, they're just packets sent to |> IP addresses. The trouble is that you have to figure out how to do |> all the XDR stuff manually, and perhaps even call the portmapper to |> find out what port to talk to. But, if you're going to do it, why not |> bundle it into a nice library for the rest of us? :-) |> |> Dale Worley Dept. of Math., MIT drw@math.mit.edu |> -- |> Mind you, not as bad as the night Archie Pettigrew ate some |> sheep's testicles for a bet... God, that bloody sheep kicked him... |> -- Ripping Yarns This is what I did when I had to RPC. In article <1991Apr9.051205.21448@milton.u.washington.edu>, wiml@milton.u.washington.edu (William Lewis) writes: | | Is it possible to do RPC from perl? I know little about rpc and | its implementation, but it seems that perl should be able to do | the necessary mechanics. Has anyone written a package to do perl | rpcs? | -- | wiml@milton.acs.washington.edu Seattle, Washington | (William Lewis) | 47 41' 15" N 122 42' 58" W | "Just remember, wherever you go ... you're stuck there." Yes, it's possible but not altogther easy. I had to go through documentation for RPC, TCP/IP, quite a few header files and even dumps of packet exchanges to come up with the following. Even after all that, I still couldn't come up with how to tag one field (@idontknow). It makes an rpc call to rstatd and determines if the client has a disk that's in use. The disk IOs are ORed together and if there are more than 16 you've got a disk and you're using it. It turns out that a diskless client that has a disk that's not in use will have 16 IOs (probably happens during device probing). I wish I had the time to document this (not even sure I could remember it all). I can answer specific questions. Any comments are welcomed. Wayne $MYNAME is the basename(1) of $0. i.e. ($MYNAME = $0) =~ s|.*/||; call using: &UsingDisk ($hostname); &UsingDisk ($ipaddr); # in a string i.e. "127.1.0.0" returns: 0: not using disk 1: using disk -1: timed out Such as it is... ## >> BEGIN subroutine: UsingDisk >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ## require ('signal.ph'); require ('sys/types.ph'); require ('netinet/in.ph'); require ('sys/socket.ph'); #require ('rpc/rpc.ph'); require ('rpcsvc/rstat.ph'); sub UsingDisk { package UsingDisk; $sub = 'UsingDisk'; eval ('$MYNAME = "${' . ($caller = (caller)[0]) . '\'MYNAME}"'); $MYNAME .= "'$sub" unless $caller eq $sub; ($host) = @_; socket (S , &'PF_INET , &'SOCK_DGRAM , &'IPPROTO_UDP ) || die "\n$MYNAME: error: socket: $!\n"; bind (S , pack ('S n C4 x8' , &'AF_INET , 0 , 0 , 0 , 0 , 0 ) ) || die "\n$MYNAME: error: bind: $!\n"; connect (S , pack ('S n C4 x8' , &'AF_INET , (getservbyname ('sunrpc', 'udp'))[2] , $host =~ /^\d+\./ ? @bytes = split(/\./, $host) : (@bytes = unpack ('C4', (gethostbyname ($host))[4])) ) ) || die "\n$MYNAME: error: connect: $!\n"; die "\n$MYNAME: error: $host: unknown host.\n" unless @bytes; select((select(S), $| = 1)[0]); print S pack ("N13" , @call = ($rm_xid = int (2**23*rand) , $msg_type = $CALL = 0 , $cb_rpcvers = 2 , $cb_prog = $PMAPPROG = 100000 , $cb_vers = $PMAPVERS = 2 , $cb_proc = $PMAPPROC_CALLIT = 5 , @cb_cred = ($flavor = 0 , $body = 0 ) , @cb_verf = @cb_cred , @call_args = ($prog = &'RSTATPROG , $vers = &'RSTATVERS , $proc = &'RSTATPROC_STATS ) ) ); vec ($rmask = "", fileno (S), 1) = 1; ($nfound, $rmask) = select ($rmask, undef, undef, 10); $nread = sysread (S, $_, 1024) if $nread = $nfound; close (S); return -1 unless $nread; ($r_xid, $r_msgtype, $r_reply_stat, @r) = unpack ("N" . length ($_) / 4, $_); $r_xid == $rm_xid || die "\n$MYNAME: error: rpc: xid error\n"; $r_msgtype == ($reply = 1) || die "\n$MYNAME: error: rpc: not a reply\n"; $r_reply_stat == ($rejected_reply = 1) && die "\n$MYNAME: error: rpc: call rejected\n"; $r_reply_stat != ($accepted_reply = 0) && die "\n$MYNAME: error: rpc: $r_reply_stat: unknown reply status\n"; (@verf[0..1], @r) = @r; $r_accept_stat = shift (@r); (@idontknow[0..1], @r) = @r; (@cp_time[0..3] , @dk_xfer[0..3] , $v_pgpgin , $v_pgpgout , $v_pswpin , $v_pswpout , $v_intr , $if_ipackets , $if_ierrors , $if_oerrors , $if_collisions , $v_swtch , @avenrun[0..2] , @boottime[0..1] , @curtime[0..1] , $if_opackets ) = @r; eval (join ('|', @dk_xfer)) > 16 ? 1 : 0; } ## << END subroutine: UsingDisk <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ##