newspaint

Documenting Problems That Were Difficult To Find The Answer To

Category Archives: Programming

Perl WWW::Mechanize or LWP::UserAgent and AWS SSL Handshake Failure

TL;DR (too long, didn’t read) summary:

Force protocol and cipher:

my $ua = WWW::Mechanize->new();
$ua->ssl_opts(
  'SSL_version' => 'TLSv1_2',
  'SSL_cipher_list' => 'ECDHE-RSA-AES256-GCM-SHA384',
);

I had a script using WWW::Mechanize (which uses LWP::UserAgent under the hood) which used to work with a website. But then the website was moved and hosted on Amazon Web Services.

Suddenly my script started reporting:

SSL connect attempt failed error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure at /usr/share/perl5/LWP/Protocol/http.pm line 47.

I tried debugging using a technique I found elsewhere and that was providing a command line switch to enable SSL debugging:

$ perl -MIO::Socket::SSL=debug4 myscript.pl
DEBUG: .../IO/Socket/SSL.pm:2700: new ctx 53819024
DEBUG: .../IO/Socket/SSL.pm:612: socket not yet connected
DEBUG: .../IO/Socket/SSL.pm:614: socket connected
DEBUG: .../IO/Socket/SSL.pm:636: ssl handshake not started
DEBUG: .../IO/Socket/SSL.pm:669: using SNI with hostname www.thetarget.domain
DEBUG: .../IO/Socket/SSL.pm:704: request OCSP stapling
DEBUG: .../IO/Socket/SSL.pm:723: set socket to non-blocking to enforce timeout=15
DEBUG: .../IO/Socket/SSL.pm:736: call Net::SSLeay::connect
DEBUG: .../IO/Socket/SSL.pm:739: done Net::SSLeay::connect -> -1
DEBUG: .../IO/Socket/SSL.pm:749: ssl handshake in progress
DEBUG: .../IO/Socket/SSL.pm:759: waiting for fd to become ready: SSL wants a read first
DEBUG: .../IO/Socket/SSL.pm:779: socket ready, retrying connect
DEBUG: .../IO/Socket/SSL.pm:736: call Net::SSLeay::connect
DEBUG: .../IO/Socket/SSL.pm:739: done Net::SSLeay::connect -> -1
DEBUG: .../IO/Socket/SSL.pm:742: SSL connect attempt failed

DEBUG: .../IO/Socket/SSL.pm:742: local error: SSL connect attempt failed error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure
DEBUG: .../IO/Socket/SSL.pm:745: fatal SSL error: SSL connect attempt failed error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure
DEBUG: ...erl5/Net/HTTPS.pm:69: ignoring less severe local error 'IO::Socket::IP configuration failed', keep 'SSL connect attempt failed error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure'
DEBUG: .../IO/Socket/SSL.pm:2733: free ctx 53819024 open=53819024
DEBUG: .../IO/Socket/SSL.pm:2738: free ctx 53819024 callback
DEBUG: .../IO/Socket/SSL.pm:2745: OK free ctx 53819024

I tried capturing the session with tcpdump and viewing the session in Wireshark to make sense of what was happening:

time=0.291136 client>server protocol=TLSv1.2 message=Client Hello
time=0.581841 server>client protocol=TLSv1.2 message=Alert (Level: Fatal, Description: Handshake Failure)

So basically AWS was outright rejecting the “Client Hello” packet without any negotiation at all. Neither was it citing a reason for the handshake failure.

At this point it became guesswork. Initially I considered forcing the protocol using the ssl_opts() function:

my $ua = WWW::Mechanize->new();
$ua->ssl_opts(
  'SSL_version' => 'TLSv1_2',
);

But this gave exactly the same result.

Then I thought about forcing the cipher. But to which cipher? I looked up Amazon’s list of accepted ciphers but that didn’t help much. You can try running the following but it gives too many options:

$ openssl ciphers
ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:SRP-DSS-AES-256-CBC-SHA:SRP-RSA-AES-256-CBC-SHA:SRP-AES-256-CBC-SHA:...

So then I tried doing a connect using openssl to see what cipher it chose:

$ # note you can also add the -servername <domain> option for SNI
$ openssl s_client -connect www.thetarget.domain:443
...
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
...

So I then tried my Perl script setting the cipher to that reported by openssl:

my $ua = WWW::Mechanize->new();
$ua->ssl_opts(
  'SSL_version' => 'TLSv1_2',
  'SSL_cipher_list' => 'ECDHE-RSA-AES256-GCM-SHA384',
);

This time my script worked!

$ perl -MIO::Socket::SSL=debug4 myscript.pl
DEBUG: .../IO/Socket/SSL.pm:2700: new ctx 38327392
DEBUG: .../IO/Socket/SSL.pm:612: socket not yet connected
DEBUG: .../IO/Socket/SSL.pm:614: socket connected
DEBUG: .../IO/Socket/SSL.pm:636: ssl handshake not started
DEBUG: .../IO/Socket/SSL.pm:669: using SNI with hostname www.thetarget.domain
DEBUG: .../IO/Socket/SSL.pm:704: request OCSP stapling
DEBUG: .../IO/Socket/SSL.pm:723: set socket to non-blocking to enforce timeout=15
DEBUG: .../IO/Socket/SSL.pm:736: call Net::SSLeay::connect
DEBUG: .../IO/Socket/SSL.pm:739: done Net::SSLeay::connect -> -1
DEBUG: .../IO/Socket/SSL.pm:749: ssl handshake in progress
DEBUG: .../IO/Socket/SSL.pm:759: waiting for fd to become ready: SSL wants a read first
DEBUG: .../IO/Socket/SSL.pm:779: socket ready, retrying connect
DEBUG: .../IO/Socket/SSL.pm:736: call Net::SSLeay::connect
DEBUG: .../IO/Socket/SSL.pm:2601: did not get stapled OCSP response
DEBUG: .../IO/Socket/SSL.pm:2554: ok=1 [2] /O=Digital Signature Trust Co./CN=DST Root CA X3/O=Digital Signature Trust Co./CN=DST Root CA X3
DEBUG: .../IO/Socket/SSL.pm:2554: ok=1 [1] /O=Digital Signature Trust Co./CN=DST Root CA X3/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
DEBUG: .../IO/Socket/SSL.pm:2554: ok=1 [0] /C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3/CN=www.thetarget.domain
DEBUG: .../IO/Socket/SSL.pm:1664: scheme=www cert=40017248
DEBUG: .../IO/Socket/SSL.pm:1674: identity=www.thetarget.domain cn=www.thetarget.domain alt=2 www.thetarget.domain
DEBUG: .../IO/Socket/SSL.pm:739: done Net::SSLeay::connect -> -1
DEBUG: .../IO/Socket/SSL.pm:749: ssl handshake in progress
DEBUG: .../IO/Socket/SSL.pm:759: waiting for fd to become ready: SSL wants a read first
DEBUG: .../IO/Socket/SSL.pm:779: socket ready, retrying connect
DEBUG: .../IO/Socket/SSL.pm:736: call Net::SSLeay::connect
DEBUG: .../IO/Socket/SSL.pm:739: done Net::SSLeay::connect -> 1
DEBUG: .../IO/Socket/SSL.pm:794: ssl handshake done

PowerShell XOR Strings

PowerShell lacks the ability to XOR strings together. In Perl this is trivial:

$ perl -e "print( '1234' ^ 'aaaa' )"
PSRU

$ perl -e "print( 'PSRU' ^ 'aaaa' )"
1234

$ perl -e "print( 'PSRU' ^ '1234' )"
aaaa

A custom C# .Net class can be in-line compiled into PowerShell, however, which will perform a similar function:

Add-Type @'
  public class MyXORString {
    public static System.String XORString(
      System.String source
      ,System.String key
    ) {
      System.Text.StringBuilder sb = new System.Text.StringBuilder();
      System.Int32 sourceLen = source.Length;
      System.Int32 keyLen = key.Length;
      System.Int32 sourceIdx = 0;
      System.Int32 keyIdx = 0;
      System.Char workingChar;
      while ( sourceIdx < sourceLen ) {
        workingChar = (System.Char)( source[sourceIdx] ^ key[keyIdx] );
        sb.Append( workingChar );

        sourceIdx += 1;
        keyIdx += 1;
        if ( keyIdx >= keyLen ) {
          keyIdx = 0;
        }
      }

      return sb.ToString();
    }
  }
'@

[MyXORString]::XORString( "1234", "aaaa" ); # outputs PSRU
[MyXORString]::XORString( "PSRU", "aaaa" ); # outputs 1234
[MyXORString]::XORString( "PSRU", "1234" ); # outputs aaaa

# Note that the key will repeat if shorter than the source
[MyXORString]::XORString( "1234", "a" ); # outputs PSRU
[MyXORString]::XORString( "PSRU", "a" ); # outputs 1234

If With Multiple Conditions in Splunk Eval

A common task one desires to do with the if() command in Splunk is to perform multiple tests. Unfortunately this is very poorly documented on the Splunk website.

You can use the AND and OR keywords (as opposed to the && or || you might have expected).

e.g.:

index=os sourcetype=ps host=myhost01
  |rex field=CPUTIME "^((?\d+)-)?(?\d\d):(?\d\d):(?\d\d)$"
  |eval cpusec=if( isnum(c_days), c_days, 0)*24*3600 + (c_hours*3600) + (c_mins*60) + c_secs
  |eval aa_inrange=if( (cpusec>1000) AND (cpusec<2000), 1, 0 )
  |search aa_inrange=1

Converting Raw Bytes Into Int32, Int16, UInt32, UInt16

If you find yourself processing raw bytes and want to decode signed and unsigned integers then the following routines will help.

Network-Ordered (Big Endian) Encoded Numbers

Note that the following code is for network-ordered (big endian) numbers.

In order to verify the routines coded are correct we can specify tests (test-driven development) to check they do what we expect – particularly at the boundary conditions:

use Test::More;

is( str_to_uint32( "\x00\x00\x00\x01" ),          1, "uint32 1" );
is( str_to_uint32( "\xFF\xFF\xFF\xFF" ), 4294967295, "uint32 4294967295" );
is( str_to_uint16( "\x00\x01" ),                  1, "uint16 1" );
is( str_to_uint16( "\xFF\xFF" ),              65535, "uint16 65535" );
is( str_to_int32( "\x00\x00\x00\x01" ),           1, "int32 1" );
is( str_to_int32( "\xFF\xFF\xFF\xFF" ),          -1, "int32 -1" );
is( str_to_int32( "\x7F\xFF\xFF\xFF" ),  2147483647, "int32 2147483647" );
is( str_to_int32( "\x80\x00\x00\x00" ), -2147483648, "int32 -2147483648" );
is( str_to_int16( "\x00\x01" ),                   1, "int16 1" );
is( str_to_int16( "\xFF\xFF" ),                  -1, "int16 -1" );
is( str_to_int16( "\x7F\xFF" ),               32767, "int16 32767" );
is( str_to_int16( "\x80\x00" ),              -32768, "int16 -32768" );

Decoding network-ordered unsigned integers is very easy in Perl with the unpack() function. For documentation see the pack() function and perlpacktut tutorial pages.

sub str_to_uint32 {
  return unpack( "N", $_[0] ); # "N" for "Network" order (big-endian)
}

sub str_to_uint16 {
  return unpack( "n", $_[0] ); # "n" for "Network" order (big-endian)
}

Decoding network-ordered signed integers is slightly more difficult as the pack() function does not appear to directly support such encodings. Instead we can convert a decoded unsigned representation and, if negative, apply twos’ compliment. From the Wikipedia page:

Conveniently, another way of finding the two’s complement of a number is to take its ones’ complement and add one.

The corollary is that to decode a negative number we take the ones’ compliment and subtract one. We use the pragma use integer; to ensure that bit flipping is done on integral values, not floating-point.

sub str_to_int32 {
  use integer;

  my $num = str_to_uint32( $_[0] );
  if ( $num & 0x80000000 ) {
    $num = 0 - ( ( ( ~ $num ) & 0xFFFFFFFF ) + 1 );
  }

  return $num;
}

sub str_to_int16 {
  use integer;

  my $num = str_to_uint16( $_[0] );
  if ( $num & 0x8000 ) {
    $num = 0 - ( ( ( ~ $num ) & 0xFFFF ) + 1 );
  }

  return $num;
}

Upon running our tests we get the following output:

me@myhost:~ $ perl -w test_functions.pl
ok 1 - uint32 1
ok 2 - uint32 4294967295
ok 3 - uint16 1
ok 4 - uint16 65535
ok 5 - int32 1
ok 6 - int32 -1
ok 7 - int32 2147483647
ok 8 - int32 -2147483648
ok 9 - int16 1
ok 10 - int16 -1
ok 11 - int16 32767
ok 12 - int16 -32768

Little Endian Encoded Numbers

The same routines can be used with a minor differences. Firstly setting up our tests:

use Test::More;

is( str_to_uint32( "\x01\x00\x00\x00" ),          1, "uint32 1" );
is( str_to_uint32( "\xFF\xFF\xFF\xFF" ), 4294967295, "uint32 4294967295" );
is( str_to_uint16( "\x01\x00" ),                  1, "uint16 1" );
is( str_to_uint16( "\xFF\xFF" ),              65535, "uint16 65535" );
is( str_to_int32( "\x01\x00\x00\x00" ),           1, "int32 1" );
is( str_to_int32( "\xFF\xFF\xFF\xFF" ),          -1, "int32 -1" );
is( str_to_int32( "\xFF\xFF\xFF\x7F" ),  2147483647, "int32 2147483647" );
is( str_to_int32( "\x00\x00\x00\x80" ), -2147483648, "int32 -2147483648" );
is( str_to_int16( "\x01\x00" ),                   1, "int16 1" );
is( str_to_int16( "\xFF\xFF" ),                  -1, "int16 -1" );
is( str_to_int16( "\xFF\x7F" ),               32767, "int16 32767" );
is( str_to_int16( "\x00\x80" ),              -32768, "int16 -32768" );

All we have to do is simply change two functions:

sub str_to_uint32 {
  return unpack( "V", $_[0] ); # "V" for "VAX" order (little-endian)
}

sub str_to_uint16 {
  return unpack( "v", $_[0] ); # "v" for "VAX" order (little-endian)
}

Upon running our tests we get the following output:

me@myhost:~ $ perl -w test_functions.pl
ok 1 - uint32 1
ok 2 - uint32 4294967295
ok 3 - uint16 1
ok 4 - uint16 65535
ok 5 - int32 1
ok 6 - int32 -1
ok 7 - int32 2147483647
ok 8 - int32 -2147483648
ok 9 - int16 1
ok 10 - int16 -1
ok 11 - int16 32767
ok 12 - int16 -32768

Debugging LWP::UserAgent in Perl 5

Over the years the way of dumping the sent and received headers of HTTP requests changed. It used to be the following line added to your script would result in headers being dumped:

use LWP::Debug qw(+);

But more recently the approach has been to add handlers to the user agent, e.g.:

$ua->add_handler(
  "request_send",
  sub {
    my $msg = shift;              # HTTP::Message
    $msg->dump( maxlength => 0 ); # dump all/everything
    return;
  }
);

$ua->add_handler(
  "response_done",
  sub {
    my $msg = shift;                # HTTP::Message
    $msg->dump( maxlength => 512 ); # dump max 512 bytes (default is 512)
    return;
  }
);

The dump() function is documented in the HTTP::Message module.

The dump() function has a few options that can be provided:

  • maxlength – maximum number of bytes to display, zero for unrestricted (all)
  • no_content – a string to replace “(no content)” with
  • prefix – a string to prepend to each line dumped

The handlers available are documented in the LWP::UserAgent module and are:

  • request_preprepare
  • request_prepare
  • request_send
  • response_header
  • response_data
  • response_done
  • response_redirect

Adding More Details to Peers in Deluge Web Client

The “peers” tab for a given selected torrent in Deluge is rather scarce of information. By default it only displays:

  • Country
  • Address
  • Client
  • Progress
  • Download Speed
  • Upload Speed

I want more information – especially the flags (as uTorrent displays, or as close to).

So here are instructions on what to modify in order to get more information about peers displayed.

Firstly you may want to pin the deluge packages on your Linux/Ubuntu host to prevent them from being upgraded in the future (and wipe out your modifications):

me@host:~$ sudo apt-mark hold deluged
me@host:~$ sudo apt-mark hold deluge-web
me@host:~$ sudo apt-mark hold deluge-common

Edit the file torrent.py so that it returns more fields in the AJAX response to the query from the browser for peer detail to modify the get_peers() function to add the following lines:

            peerflags = str()
            if peer.flags & peer.snubbed:
                peerflags += "S"
            if peer.flags & peer.interesting:
                if not peer.flags & peer.remote_choked:
                    peerflags += "D"
                else:
                    peerflags += "d"
            elif not peer.flags & peer.remote_choked:
                peerflags += "K"
            if peer.flags & peer.remote_interested:
                if not peer.flags & peer.choked:
                    peerflags += "U"
                else:
                    peerflags += "u"
            elif not peer.flags & peer.choked:
                peerflags += "?"
            if peer.flags & peer.rc4_encrypted:
                peerflags += "E"
            if peer.flags & peer.plaintext_encrypted:
                peerflags += "e"
            if peer.flags & peer.optimistic_unchoke:
                peerflags += "O"
            if not peer.flags & peer.local_connection:
                peerflags += "I"
            if peer.flags & peer.endgame_mode:
                peerflags += "g"
            if peer.flags & peer.holepunched:
                peerflags += "h"
            #if peer.flags & peer.utp_socket:
            #    peerflags += "P"
            if peer.num_hashfails > 0:
                peerflags += "F"
            if peer.source & peer.dht:
                peerflags += "H"
            if peer.source & peer.pex:
                peerflags += "X"
            if peer.source & peer.lsd:
                peerflags += "L"
            if peer.source & peer.tracker:
                peerflags += "T"

            ret.append({
                "client": client,
                "country": country,
                "down_speed": peer.payload_down_speed,
                "ip": "%s:%s" % (peer.ip[0], peer.ip[1]),
                "progress": peer.progress,
                "seed": peer.flags & peer.seed,
                "up_speed": peer.payload_up_speed,
                "flags": peerflags,
                "total_download": peer.total_download,
                "total_upload": peer.total_upload,
                "rtt": peer.rtt,
            })

Next edit line 224 of the JavaScript file deluge-all.js to add the highlighted lines (but you may want to keep it all squashed up in one line in the file, it doesn’t matter):

...
function c(e){...}
function ms(e){return String.format('{0}ms',e);}
Deluge.details.PeersTab=...
...
{header:"Up Speed",width:100,sortable:true,renderer:fspeed,dataIndex:"up_speed"},
{header:"Flags",width:70,sortable:true,renderer:fplain,dataIndex:"flags"},
{header:"Downloaded",width:100,sortable:true,renderer:fsize,dataIndex:"total_download"},
{header:"Uploaded",width:100,sortable:true,renderer:fsize,dataIndex:"total_upload"},
{header:"RTT",width:70,sortable:true,renderer:ms,dataIndex:"rtt"}
],

So what do the flags mean?

  • S – peer snubbed
  • D – peer has what we want and remote not choked (usually downloading)
  • d – peer has what we want but remote is choked (want to download but they won’t)
  • K – peer doesn’t have what we want but isn’t choking us
  • U – peer wants what we have and we are not choked (usually uploading)
  • u – peer wants what we have and we are choked (peer wants us to upload but we won’t)
  • ? – peer doesn’t want what we have but we’re not choking them
  • E – RC4 (connection) encrypted
  • e – plaintext (handshake) encrypted
  • O – optimistic unchoke
  • I – incoming connection (peer initiated connection to us)
  • g – endgame mode
  • h – peer holepunched
  • P – uTP socket (flag not present in older versions of libtorrent)
  • F – peer has participated in a piece that failed hash
  • H – peer found via DHT
  • X – peer found via PEX
  • L – peer found via local service discovery (on local network)
  • T – peer found via tracker

See the peer_info structure in libtorrent for more detail.

A Utility to Convert Date Formats

The following Perl script converts from various forms to various forms:

#!/usr/bin/perl -w

use Time::Local;
use Date::Parse;

use strict;

sub help {
  print( STDERR "parsedate.pl - Utility for converting date formats\n" );
  print( STDERR "\n" );
  print( STDERR "Enter a date in one of the following formats:\n" );
  print( STDERR "  epoch: 1526615052 - time in seconds past 1 Jan 1970 UTC\n" );
  print( STDERR "  javaepoch: 1526615092916 - time in ms past 1 Jan 1970 UTC\n" );
  print( STDERR "\n" );
}

sub display_time {
  my ( $epoch ) = @_;

  printf( "\n" );

  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst);

  my @ltime = localtime( $epoch );
  my @gtime = gmtime( $epoch );

  my $min_diff = ( $ltime[2] - $gtime[2] ) * 60;
  $min_diff += ( $ltime[1] - $gtime[1] );
  if ( $ltime[3] != $gtime[3] ) {
    if ( $ltime[3] == ( $gtime[3] + 1 ) ) {
      $min_diff += ( 24 * 60 );
    } elsif ( $gtime[3] == ( $ltime[3] + 1 ) ) {
      $min_diff -= ( 24 * 60 );
    } elsif ( $ltime[3] == 1 ) {
      $min_diff += ( 24 * 60 );
    } else {
      $min_diff -= ( 24 * 60 );
    }
  }

  my $sign = "+";
  if ( $min_diff <= 0 ) {
    $min_diff = ( 0 - $min_diff );
    $sign = "-";
  }

  my $tz_hour = int( $min_diff / 60 );
  my $tz_min = $min_diff - ( $tz_hour * 60 );

  ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = @ltime;

  my $iso8601;
  $iso8601 = sprintf(
    "%04d-%02d-%02dT%02d:%02d:%02d%s%02d:%02d",
    $year + 1900, $mon + 1, $mday,
    $hour, $min, $sec, $sign, $tz_hour, $tz_min
  );

  printf( "Local time:\n" );
  printf( "  %s\n", $iso8601 );
  printf( "  %s\n", scalar( localtime( $epoch ) ) );
  printf( "\n" );

  ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = @gtime;
  $iso8601 = sprintf(
    "%04d-%02d-%02dT%02d:%02d:%02dZ",
    $year + 1900, $mon + 1, $mday,
    $hour, $min, $sec
  );

  printf( "GMT/UTC:\n" );
  printf( "  %s\n", $iso8601 );
  printf( "  %s\n", scalar( gmtime( $epoch ) ) );
  printf( "  %d\n", $epoch );
  printf( "\n" );
}

sub main {
  # Join command line arguments into a single string
  my $input = join( " ", @ARGV );

  my $epoch = undef;

  if ( $input =~ m/^\s*$/ ) {
    help();

    print( "Using current time as no time string provided.\n\n" );
    $epoch = time();
  } elsif ( $input =~ m/^\d{1,10}$/ ) {
    printf( STDERR "Detected Unix epoch date format\n" );
    $epoch = int( $input );
  } elsif ( $input =~ m/^\d{13}$/ ) {
    printf( STDERR "Detected Java epoch (in ms) date format\n" );
    $epoch = ( int( $input ) / 1000.00 );
  } else {
    printf( STDERR "Trying to parse date string...\n" );
    $epoch = Date::Parse::str2time( $input );
  }

  if ( ! $epoch ) {
    die( "Failed to parse date" );
  }

  display_time( $epoch );
}

main();

Jstack Unable to Contact Target Process

In Java one of the most useful tools for diagnosing thread-related issues is jstack. It outputs a stack trace of every thread of a running process – and can also give an indication of the state of locks.

However I was getting the following error message when trying to attach jstack to a running process:

17840: Unable to open socket file: target process not responding or HotSpot VM not loaded
The -F option can be used when the target process is not responding

The solution, amazingly, was to run the jstack command as the same user as the running Java process.

The command to use for this is:

sudo -u <user> jstack -l <pid>

e.g. if the Java process is running as user “oracle” with process ID 45132 then you would execute:

sudo -u oracle jstack -l 45132

A Microsoft Word Macro to Neaten Paragraphs

When I’m constructing a list of items I often want to keep the lines together so that they are not split across a page.

By default a paragraph in Microsoft Word has trailing vertical whitespace. That means there’s a space below the last line of the paragraph that pushes the top of the next paragraph down the page, leaving a gap between paragraphs.

So when I have a list of items they look like the following when first imported:

- line 1
gap
- line 2
gap
- line 3
gap

But what I want is:

- line 1
- line 2
- line 3
gap

… and I want to ensure those three lines are not broken up over a page break.

To achieve this I highlight all the lines except the last line and run the following macro:

Sub ParagraphCutAndKeepWithNext()
'
' ParagraphCutAndKeepWithNext Macro
'
'
    With Selection.ParagraphFormat
        .SpaceAfter = 0
        .KeepWithNext = True
    End With
End Sub

Formatting a Splunk Time Field as ISO-8601

On occasion I will use the stats command in a Splunk search to identify the beginning and end of a particular set of events, e.g.:

search "Fatal Error"
|stats earliest(_time) as started, latest(_time) as ended by host

The problem is that this will display the “started” and “ended” fields as a number rather than a formatted time string. So to achieve this I use the fieldformat command with the strftime() function:

search "Fatal Error"
|stats earliest(_time) as started, latest(_time) as ended by host
|fieldformat started=strftime( started, "%Y-%m-%dT%H:%M:%S" )
|fieldformat ended=strftime( ended, "%Y-%m-%dT%H:%M:%S" )