Docker: hostname different from container name - perl

I am using an docker overlay network
I am starting a server, myserver.pl, in a container
use strict;
use warnings;
use POSIX qw(:sys_wait_h :errno_h :fcntl_h strftime);
use Getopt::Long;
use File::Basename;
use IO::Socket;
use IO::Select;
STDOUT->autoflush(1);
use Fcntl;
use Socket;
use Carp;
use FileHandle;
use Cwd qw{abs_path cwd};
use Sys::Hostname;
our $HOST = hostname();
our $PUBQDIR = '/x/eng/site/build/altload';
our $PWD = cwd();
our $EMPTY = q{};
our $NL = "\n";
our $SPACE = q{ };
our $ANY_RE = qr{.*};
our $REDIS_OS_RE = qr{^br-redis$};
our $HOST_SPLIT_RE = qr{[.][^:]*};
our $bash_and_list_op = '&&';
my $Request_File = $EMPTY;
# options from command line
my $Opt_Class = 'L';
my $Redis = $EMPTY;
my %Published_Requests = (); # Track published queue requests for cleanup
chomp $HOST;
$HOST =~ s/[.].*//g;
chomp $PWD;
my $Pool = $EMPTY;
sub sock_initialize {
my $sock = q{};
my $port = q{};
# Get a port for our server.
$sock = IO::Socket::INET->new(
Listen => SOMAXCONN, # listen queue depth
LocalPort => 0,
Reuse => 1
);
die "Unable to bind a port: $!" if !$sock;
$port = $sock->sockport();
# Determine and log the dispatcher queue id
#my $ip = inet_ntoa( scalar gethostbyname( $HOST ));
my $ip = "";
my $uid = (getpwuid( $> ))[2];
my $queue = join(":", $ip, $port, $$, $uid);
# Include in the published queue name:
# - job class, which must be the same for all jobs we submit
# - Our Hostname and Port
# Can't lower hostname to IP yet, it might give 127.0.0.1
print sprintf("Connect me at $HOST on port $port ($$), SOMAXCONN=%d\n", SOMAXCONN);
return $sock;
} ## end sub sock_initialize
my $listen_sock = sock_initialize();
while (1) {
#my $xsock = Accept();
my $xsock;
while (1) {
$! = 0;
# Accept can block. Need to use nonblocking poll (Stevens)
$xsock = $listen_sock->accept; # ACCEPT
last if defined $xsock;
next if $! == EINTR;
die "accept error: $!";
if ( defined $xsock ) {
$xsock->blocking(0); # mark executor socket nonblocking
$xsock->sockopt( SO_KEEPALIVE() => 1 ) or die "sockopt: $!";
}
}
my $buff = "";
while (1) {
my $nbytes = sysread $xsock, $buff, 32768, length($buff); # SYSCALL
if ( !defined $nbytes ) { # read error
next if $! == EINTR;
last if $! == EWOULDBLOCK; # normal
return;
}
last if $nbytes == 0; # EOF
}
print "received $buff\n";
last;
by docker run -v /home/:/home/ --name myhost01 --network test-net perl perl /home/myserver.pl
it outputs:
Connect me at ebcf3c65c3e1 on port 39580 (1), SOMAXCONN=128
I can connect to this server from another container on different host (docker daemon) attached to the same overlay network via : i.e. myhost01:39580
my $host = "myhost01";
my $port = 39850;
my $s = IO::Socket::INET->new (PeerAddr => $host,
PeerPort => $port,
Type => SOCK_STREAM,
Proto => 'tcp',
Timeout => 1);
So, thats fine but i was aware of the container name beforehand so i knew to connect to it, I want the myserver.pl to advertise its hostname as myhost01, not ebcf3c65c3e1
Connect me at myhost01 on port 39580 (1), SOMAXCONN=128
Is there a way to set the hostname inside the container that it thinks the hostname is the same as container name and that perl's Sys::Hostname:hostname() or python's socket.gethostname() or whatever ways to get hostname would think that and not using container id such as ebcf3c65c3e1 as hostname?
[EDIT]
i see the option --hostname for docker run but do i really need to supply both --name and --hostname? Is there other ways to automatically inherit container name as its hostname?

Personal opinion: you should leave the hostname alone. If you need to spawn multiple, similar containers with scale: <n>, any attempt to set the hostname would not work.
But you can obtain the container name from inside the container via docker:socket, and use that for many of your other purposes, such as for the code to identify itself or other containers.
See my example here

Related

how to have perl get https website links to trigger cron job

Currently I have a perl script to trigger a cron job on multiple websites however the script is only working for http and not for https. In other words when the script tries to trigger a link on a domain with an SSL certificate, the cron never gets triggered. What is required to trigger the https cron URL?
#!/usr/bin/perl
#
# Enter domains, one per line.
#
# They do not have to be indented.
#
# Do not use an '#' on the beginning of your domains.
#
# Enter domains below next line: (without #)
my #domains = qw/#############################
#www.example.com
#www.example.com
#www.example.com
###############_END_OF_DOMAINS_###############/;
my $num_domains = 200; # number of domains
my $cycle_time = 900; # seconds per cycle
# So if $num_domain=200 and $cycle_time=900,
# there will be 4.5 seconds between sites.
# Nothing below here to service.....
#############################################################################
#############################################################################
#############################################################################
#############################################################################
use strict;
use warnings;
use IO::Socket;
use constant DEBUG => 0;
my $time_wait = $cycle_time / $num_domains;
my #stdin = -t STDIN
? ()
: grep !/^$/, map { s/^\s+//; s/\s+$//; s/\#.*?$//; $_ } <STDIN>
;
for my $site ( grep !/^\s*\#/, #domains, #stdin ) {
chomp $site;
print "contacting: $site\n" if DEBUG;
my $sock = IO::Socket::INET->new(
PeerHost => $site,
PeerPort => 'http(80)',
Proto => 'tcp',
Type => SOCK_STREAM,
Timeout => 10,
);
unless ( defined $sock ) {
warn "Couldn't connect to $site: $!\n" if DEBUG;
next;
}
#Example target:
# http://www.example.com/index.php?option=com_acymailing&ctrl=cron
print $sock
"GET /index.php?option=com_acymailing&ctrl=cron HTTP/1.0\n" .
"Host: $site\n\n"
;
# wait for response...
while (defined( $_ = scalar <$sock> )) {
# we dont care what it said...
print 'recv: ', $_ if DEBUG;
}
print "\ndone\n" if DEBUG;
$sock->close;
}
continue {
print "sleeping $time_wait seconds\n" if DEBUG;
select( undef, undef, undef, $time_wait );
}
__END__
It seems likely that the problem is because you're always configuring your socket object to use the HTTP port (PeerPort => 'http(80)'). The HTTPS port is 443, not 80.
But I have no idea why you would write code like this using low-level socket programming. You would be far better advised to look at something like LWP::Simple (you will also want to install LWP::Protocol::https for HTTPS support).

perl socket: increment port if in use

I have the following code:
use IO::Socket::INET;
use Sys::Hostname;
use Socket;
my($addr)=inet_ntoa((gethostbyname(hostname))[4]);
my $port_to_use = 7777;
my $socket = new IO::Socket::INET (
LocalHost => $addr,
LocalPort => $port_to_use,
Proto => 'tcp',
Listen => 5,
Reuse => 1
);
die "cannot create socket $!\n" unless $socket;
my $client_socket = $socket->accept();
if i start this in one screen and start another one in the other screen, i get an error:
cannot create socket Address already in use
instead of dying, i would like to try using different port (increment by 1) until it can find the one to use.
I've try to convert the die line with eval but im not able to catch it
any suggestions?
Use a Loop:
use IO::Socket::INET;
use Sys::Hostname;
use Socket;
my($addr)=inet_ntoa((gethostbyname(hostname))[4]);
my $port_to_use = 7776;
my $fail =1;
my $socket;
while ($fail == 1){
$port_to_use++;
$fail = 0;
warn $port_to_use;
$socket = IO::Socket::INET->new (
LocalHost => $addr,
LocalPort => $port_to_use,
Proto => 'tcp',
Listen => 5,
Reuse => 0
) or $fail =1;
}
warn $socket->accept();
Here is a tidier alternative which actually checks to make sure the failure to bind to a given port was due to the port being in use. It also limits the port range to check. If you use the code in the other answer, and, if for some reason, the machine is not allowing your application to bind to any ports, you are going to get stuck in an infinite loop. It may also cause your application to bind to ports that should otherwise have been left alone etc.
#!/usr/bin/env perl
use strict;
use warnings;
use Carp qw( croak );
use Errno qw( EADDRINUSE );
use IO::Socket::INET;
use Sys::Hostname qw( hostname );
use Socket;
# These can come from a config file or command line
# See also https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers#Dynamic.2C_private_or_ephemeral_ports
# https://unix.stackexchange.com/a/39784/2371
my #port_range = (0xC000, 0xFFFF);
my $addr = inet_ntoa( (gethostbyname(hostname) )[4]);
my $socket;
TRY_PORT:
for my $port ($port_range[0] .. $port_range[1]) {
warn "Trying port $port\n";
$socket = IO::Socket::INET->new(
LocalHost => $addr,
LocalPort => $port,
Proto => 'tcp',
Listen => 7,
Reuse => 0,
);
if ($socket) {
warn "Bound to port $port\n";
last TRY_PORT;
}
if ( EADDRINUSE != $! ) {
croak "Cannot bind to port '$port': $!";
}
warn "Port in use, trying the next one\n";
}
$socket->accept
or croak "...";
# ...

Better way to handle perl sockets to read/write to active proccess

First of all I would thank you guys not offering a work around as a solution (although it would be cool to know other ways to do it). I was setting up tg-master project (telegram for cli) to be used by check_mk alert plugin. I found out that telegram runs on a stdin/stdout proccess so I tought it would be cool to "glue" it, so i wrote with a lot of building blocks from blogs and cpan the next 2 pieces of code. They already work (i need to handle broken pipes sometimes) but I was wondering if sharing this could come from some experts new ideas.
As you could see my code relies on a eval with a die reading from spawned process, and I know is not the best way to do it. Any suggestions? :D
Thank you guys
Server
use strict;
use IO::Socket::INET;
use IPC::Open2;
use POSIX;
our $pid;
use sigtrap qw/handler signal_handler normal-signals/;
sub signal_handler {
print "what a signal $!\nlets kill $pid\n";
kill 'SIGKILL', $pid;
#die "Caught a signal $!";
}
# auto-flush on socket
$| = 1;
# creating a listening socket
my $socket = new IO::Socket::INET(
LocalHost => '0.0.0.0',
LocalPort => '7777',
Proto => 'tcp',
Listen => 5,
Reuse => 1
);
die "cannot create socket $!\n" unless $socket;
print "server waiting for client connection on port 7777\n";
my ( $read_proc, $write_proc );
my ( $uid, $gid ) = ( getpwnam "nagios" )[ 2, 3 ];
POSIX::setgid($gid); # GID must be set before UID!
POSIX::setuid($uid);
$pid = open2( $read_proc, $write_proc, '/usr/bin/telegram' );
#flush first messages;
eval {
local $SIG{ALRM} = sub { die "Timeout" }; # alarm handler
alarm(1);
while (<$read_proc>) { }
};
while (1) {
my $client_socket = $socket->accept();
my $client_address = $client_socket->peerhost();
my $client_port = $client_socket->peerport();
print "connection from $client_address:$client_port\n";
# read until \n
my $data = "";
$data = $client_socket->getline();
# write to spawned process stdin the line we got on $data
print $write_proc $data;
$data = "";
eval {
local $SIG{ALRM} = sub { die "Timeout" }; # alarm handler
alarm(1);
while (<$read_proc>) {
$client_socket->send($_);
}
};
# notify client that response has been sent
shutdown( $client_socket, 1 );
}
$socket->close();
Client
echo "contact_list" | nc localhost 7777
or
echo "msg user#12345 NAGIOS ALERT ... etc" | nc localhost 7777
or
some other perl script =)
If you are going to implement a script that performs both reads and writes from/to different handles, consider using select (the one defined as select RBITS,WBITS,EBITS,TIMEOUT in the documentation). In this case you will totally avoid using alarm with a signal handler in eval to handle a timeout, and will only have one loop with all of the work happening inside it.
Here is an example of a program that reads from both a process opened with open2 and a network socket, not using alarm at all:
use strict;
use warnings;
use IO::Socket;
use IPC::Open2;
use constant MAXLENGTH => 1024;
my $socket = IO::Socket::INET->new(
Listen => SOMAXCONN,
LocalHost => '0.0.0.0',
LocalPort => 7777,
Reuse => 1,
);
# accepting just one connection
print "waiting for connection...\n";
my $remote = $socket->accept();
print "remote client connected\n";
# simple example of the program writing something
my $pid = open2(my $localread, my $localwrite, "sh -c 'while : ; do echo boom; sleep 1 ; done'");
for ( ; ; ) {
# cleanup vectors for select
my $rin = '';
my $win = '';
my $ein = '';
# will wait for a possibility to read from these two descriptors
vec($rin, fileno($localread), 1) = 1;
vec($rin, fileno($remote), 1) = 1;
# now wait
select($rin, $win, $ein, undef);
# check which one is ready. read with sysread, not <>, as select doc warns
if (vec($rin, fileno($localread), 1)) {
print "read from local process: ";
sysread($localread, my $data, MAXLENGTH);
print $data;
}
if (vec($rin, fileno($remote), 1)) {
print "read from remote client: ";
sysread($remote, my $data, MAXLENGTH);
print $data;
}
}
In the real production code you will need to carefully check for errors returned by various function (socket creation, open2, accept, and select).

Perl read from socket missing first character

I am trying to read from an instrument connected over network using TCP protocol from Perl.
The code I have used is below:
$socket = new IO::Socket::INET (
PeerHost => '210.232.14.204',
PeerPort => '23',
Proto => 'tcp',
) or die "ERROR in Socket Creation";
while(!($data=~m/"ABC"/))
{
$temp = <$socket>;
$data = $data + $temp;
print $temp;
}
What happens is the first character of every line that is read over the TCP is not printed. Instead it is replace with a space. Why does this happen?
Example:
Expected output
Copyright (c) ACME Corporation
2009 - 2010
Actual Output
opyright (c) ACME Corporation
009 - 2010
Thanks...
The telnet protocol has a little bit of negotiation at its startup, something I sometimes jokingly refer to as a “secret handshake”. You should use a more straight-through service/port to get up to speed with sockets.
Also, you really need two different threads of control for this sort of thing; otherwise it’s too hard. Here’s a simple telnetish program from 1998:
use strict;
use IO::Socket;
my ($host, $port, $kidpid, $handle, $line);
unless (#ARGV == 2) { die "usage: $0 host port" }
($host, $port) = #ARGV;
# create a tcp connection to the specified host and port
$handle = IO::Socket::INET->new(Proto => "tcp",
PeerAddr => $host,
PeerPort => $port)
or die "can't connect to port $port on $host: $!";
$handle->autoflush(1); # so output gets there right away
print STDERR "[Connected to $host:$port]\n";
# split the program into two processes, identical twins
die "can't fork: $!" unless defined($kidpid = fork());
if ($kidpid) {
# parent copies the socket to standard output
while (defined ($line = <$handle>)) {
print STDOUT $line;
}
kill("TERM" => $kidpid); # send SIGTERM to child
}
else {
# child copies standard input to the socket
while (defined ($line = <STDIN>)) {
print $handle $line;
}
}
exit;
And here’s a more complete implementation, a program that sits on your firewall and waits for internal connections to some outside port:
#!/usr/bin/perl -w
# fwdport -- act as proxy forwarder for dedicated services
use strict; # require declarations
use Getopt::Long; # for option processing
use Net::hostent; # by-name interface for host info
use IO::Socket; # for creating server and client sockets
use POSIX ":sys_wait_h"; # for reaping our dead children
my (
%Children, # hash of outstanding child processes
$REMOTE, # whom we connect to on the outside
$LOCAL, # where we listen to on the inside
$SERVICE, # our service name or port number
$proxy_server, # the socket we accept() from
$ME, # basename of this program
);
($ME = $0) =~ s,.*/,,; # retain just basename of script name
check_args(); # processing switches
start_proxy(); # launch our own server
service_clients(); # wait for incoming
die "NOT REACHED"; # you can't get here from there
# process command line switches using the extended
# version of the getopts library.
sub check_args {
GetOptions(
"remote=s" => \$REMOTE,
"local=s" => \$LOCAL,
"service=s" => \$SERVICE,
) or die <<EOUSAGE;
usage: $0 [ --remote host ] [ --local interface ] [ --service service ]
EOUSAGE
die "Need remote" unless $REMOTE;
die "Need local or service" unless $LOCAL || $SERVICE;
}
# begin our server
sub start_proxy {
my #proxy_server_config = (
Proto => 'tcp',
Reuse => 1,
Listen => SOMAXCONN,
);
push #proxy_server_config, LocalPort => $SERVICE if $SERVICE;
push #proxy_server_config, LocalAddr => $LOCAL if $LOCAL;
$proxy_server = IO::Socket::INET->new(#proxy_server_config)
or die "can't create proxy server: $#";
print "[Proxy server on ", ($LOCAL || $SERVICE), " initialized.]\n";
}
sub service_clients {
my (
$local_client, # someone internal wanting out
$lc_info, # local client's name/port information
$remote_server, # the socket for escaping out
#rs_config, # temp array for remote socket options
$rs_info, # remote server's name/port information
$kidpid, # spawned child for each connection
);
$SIG{CHLD} = \&REAPER; # harvest the moribund
accepting();
# an accepted connection here means someone inside wants out
while ($local_client = $proxy_server->accept()) {
$lc_info = peerinfo($local_client);
set_state("servicing local $lc_info");
printf "[Connect from $lc_info]\n";
#rs_config = (
Proto => 'tcp',
PeerAddr => $REMOTE,
);
push(#rs_config, PeerPort => $SERVICE) if $SERVICE;
print "[Connecting to $REMOTE...";
set_state("connecting to $REMOTE"); # see below
$remote_server = IO::Socket::INET->new(#rs_config)
or die "remote server: $#";
print "done]\n";
$rs_info = peerinfo($remote_server);
set_state("connected to $rs_info");
$kidpid = fork();
die "Cannot fork" unless defined $kidpid;
if ($kidpid) {
$Children{$kidpid} = time(); # remember his start time
close $remote_server; # no use to master
close $local_client; # likewise
next; # go get another client
}
# at this point, we are the forked child process dedicated
# to the incoming client. but we want a twin to make i/o
# easier.
close $proxy_server; # no use to slave
$kidpid = fork();
die "Cannot fork" unless defined $kidpid;
# now each twin sits around and ferries lines of data.
# see how simple the algorithm is when you can have
# multiple threads of control?
# this is the fork's parent, the master's child
if ($kidpid) {
set_state("$rs_info --> $lc_info");
select($local_client); $| = 1;
print while <$remote_server>;
kill('TERM', $kidpid); # kill my twin cause we're done
}
# this is the fork's child, the master's grandchild
else {
set_state("$rs_info <-- $lc_info");
select($remote_server); $| = 1;
print while <$local_client>;
kill('TERM', getppid()); # kill my twin cause we're done
}
exit; # whoever's still alive bites it
} continue {
accepting();
}
}
# helper function to produce a nice string in the form HOST:PORT
sub peerinfo {
my $sock = shift;
my $hostinfo = gethostbyaddr($sock->peeraddr);
return sprintf("%s:%s",
$hostinfo->name || $sock->peerhost,
$sock->peerport);
}
# reset our $0, which on some systems make "ps" report
# something interesting: the string we set $0 to!
sub set_state { $0 = "$ME [#_]" }
# helper function to call set_state
sub accepting {
set_state("accepting proxy for " . ($REMOTE || $SERVICE));
}
# somebody just died. keep harvesting the dead until
# we run out of them. check how long they ran.
sub REAPER {
my $child;
my $start;
while (($child = waitpid(-1,WNOHANG)) > 0) {
if ($start = $Children{$child}) {
my $runtime = time() - $start;
printf "Child $child ran %dm%ss\n",
$runtime / 60, $runtime % 60;
delete $Children{$child};
} else {
print "Bizarre kid $child exited $?\n";
}
}
# If I had to choose between System V and 4.2, I'd resign. --Peter Honeyman
$SIG{CHLD} = \&REAPER;
};
As I said, that’s from 1998. These days I’d use warnings and possibly use autodie, but you still should be able to learn a good bit from it.

How do I save sockets in a hash and loop over them from another thread?

I am working on a mulithreaded TCP server. In the main thread, I listen on a socket and create a new thread for new incoming connections. I want to save all incoming connections in a hash so that I can access them from yet another thread.
From the monitor thread, I can not read any newly added connections. It seems a new clients hash is created when creating the monitor thread.
How do i keep list of all sockets and loop them from my monitor thread?
Current code:
#!/usr/bin/perl
use strict;
use IO::Socket;
use threads;
use Thread::Queue;
# init
my $clients = {};
my $queue = Thread::Queue->new;
# thread that monitors
threads->create("monitor");
# create the listen socket
my $listenSocket = IO::Socket::INET->new(LocalPort => 12345,
Listen => 10,
Proto => 'tcp',
Reuse => 1);
# make sure we are bound to the port
die "Cant't create a listening socket: $#" unless $listenSocket;
print "Server ready. Waiting for connections on 34567 ... \n";
# wait for connections at the accept call
while (my $connection = $listenSocket->accept) {
# set client socket to non blocking
my $nonblocking = 1;
ioctl($connection, 0x8004667e, \\$nonblocking);
# autoflush
$connection->autoflush(1);
# debug
print "Accepted new connection\n";
# add to list
$clients->{time()} = $connection;
# start new thread and listen on the socket
threads->create("readData", $connection);
}
sub readData {
# socket parameter
my ($client) = #_;
# read client
while (<$client>) {
# remove newline
chomp $_;
# add to queue
$queue->enqueue($_);
}
close $client;
}
sub monitor {
# endless loop
while (1) {
# loop while there is something in the queue
while ($queue->pending) {
# get data from a queue
my $data = $queue->dequeue;
# loop all sockets
while ( my ($key, $value) = each(%$clients) ) {
# send to socket
print $value "$data\n";
}
}
# wait 0,25 seconds
select(undef, undef, undef, 0.25);
}
}
close $listenSocket;
You need to share $clients via share from threads::shared:
my $clients = &share({});
The old-fashioned syntax is due to a documented issue with Perl’s prototypes. If you have at least Perl 5.8.9, use the nicer
my $clients = shared_clone({});
instead.
You also want to protect $clients with a lock, e.g.,
my $clients_lock : shared;
{
lock $clients_lock;
$clients->{time()} = fileno $connection;
}
Finally, because IO::Socket::INET instances are Perl typeglobs, you can’t share them, so instead add their socket descriptors (from fileno) to $clients and then fdopen the socket when necessary with
open my $fh, ">&=", $sockdesc or warn ...
The program below repeats inbound data to the other connected sockets:
#!/usr/bin/perl
use strict;
use IO::Socket;
use threads;
use threads::shared;
use Thread::Queue;
# init
my $clients = &share({});
my $clients_lock : shared;
my $queue = Thread::Queue->new;
# thread that monitors
threads->create("monitor");
# create the listen socket
my $port = 12345;
my $listenSocket = IO::Socket::INET->new(
LocalPort => $port,
Listen => 10,
Proto => 'tcp',
Reuse => 1
);
# make sure we are bound to the port
die "Can't create a listening socket: $#" unless $listenSocket;
print "Server ready. Waiting for connections on $port ... \n";
# wait for connections at the accept call
while (my $connection = $listenSocket->accept) {
# set client socket to non blocking
my $nonblocking = 1;
ioctl($connection, 0x8004667e, \\$nonblocking);
# autoflush
$connection->autoflush(1);
# debug
print "Accepted new connection\n";
# add to list
{
lock $clients_lock;
$clients->{time()} = fileno $connection;
}
# start new thread and listen on the socket
threads->create("readData", $connection);
}
sub readData {
# socket parameter
my ($client) = #_;
# read client
while (<$client>) {
chomp;
$queue->enqueue($_);
}
close $client;
}
sub monitor {
# endless loop
while (1) {
# loop while there is something in the queue
while ($queue->pending) {
# get data from a queue
my $data = $queue->dequeue;
# loop all sockets
{
lock $clients_lock;
while ( my ($key, $value) = each(%$clients) ) {
# send to socket
if (open my $fh, ">&=", $value) {
print $fh "$data\n";
}
else {
warn "$0: fdopen $value: $!";
}
}
}
}
# wait 0,25 seconds
select(undef, undef, undef, 0.25);
}
}
close $listenSocket;
Don't have too much experience using threads in Perl, but I think you just want to share your client list:
use threads::shared;
my $clients : shared = {};
Update:
Perl complains about:
my $hash : shared = {};
but it seems to be ok with:
my $hash = {};
share($hash);
Also, this code:
my $hash = { key1 => "value1" };
share($hash);
seems to clear the hashtable, but
my $hash = {};
share($hash);
$hash->{key1} = "value1";
works like I'd expect.