[MGPG-59] GPG Plugin: "gpg: signing failed: Inappropriate ioctl for device"
diff --git a/Jenkinsfile b/Jenkinsfile
index e9f05f7..059dc22 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -17,4 +17,4 @@
  * under the License.
  */
 
-asfMavenTlpPlgnBuild()
+asfMavenTlpPlgnBuild([tmpWs:true])
diff --git a/pom.xml b/pom.xml
index b5dfceb..2afc642 100644
--- a/pom.xml
+++ b/pom.xml
@@ -106,6 +106,13 @@
       <artifactId>plexus-sec-dispatcher</artifactId>
       <version>1.4</version>
     </dependency>
+    
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.12</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>
@@ -129,24 +136,28 @@
     <profile>
       <id>run-its</id>
       <build>
-        <pluginManagement>
-          <plugins>
-            <plugin>
-              <groupId>org.apache.maven.plugins</groupId>
-              <artifactId>maven-invoker-plugin</artifactId>
-              <configuration>
-                <settingsFile>src/it/settings.xml</settingsFile>
-                <goals>
-                  <goal>clean</goal>
-                  <goal>install</goal>
-                </goals>
-                <properties>
-                  <gpg.homedir>${project.build.testOutputDirectory}/gnupg</gpg.homedir>
-                </properties>
-              </configuration>
-            </plugin>
-          </plugins>
-        </pluginManagement>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-invoker-plugin</artifactId>
+            <configuration>
+              <settingsFile>src/it/settings.xml</settingsFile>
+              <pomIncludes>
+                <pomInclude>*</pomInclude>
+              </pomIncludes>
+              <pomExcludes>
+                <pomExclude>alternative-secret-keyring</pomExclude> <!-- not supported with gpg 2.1+ -->
+              </pomExcludes>
+              <goals>
+                <goal>clean</goal>
+                <goal>install</goal>
+              </goals>
+              <properties>
+                <gpg.homedir>${project.build.testOutputDirectory}/gnupg</gpg.homedir>
+              </properties>
+            </configuration>
+          </plugin>
+        </plugins>
       </build>
     </profile>
   </profiles>
diff --git a/src/main/java/org/apache/maven/plugin/gpg/AbstractGpgMojo.java b/src/main/java/org/apache/maven/plugin/gpg/AbstractGpgMojo.java
index b46aff1..cb61615 100644
--- a/src/main/java/org/apache/maven/plugin/gpg/AbstractGpgMojo.java
+++ b/src/main/java/org/apache/maven/plugin/gpg/AbstractGpgMojo.java
@@ -102,10 +102,12 @@
     private boolean defaultKeyring;
 
     /**
-     * The path to a secret keyring to add to the list of keyrings. By default, only the {@code secring.gpg} from gpg's
-     * home directory is considered. Use this option (in combination with {@link #publicKeyring} and
+     * <p>The path to a secret keyring to add to the list of keyrings. By default, only the {@code secring.gpg} from 
+     * gpg's home directory is considered. Use this option (in combination with {@link #publicKeyring} and
      * {@link #defaultKeyring} if required) to use a different secret key. <em>Note:</em> Relative paths are resolved
-     * against gpg's home directory, not the project base directory.
+     * against gpg's home directory, not the project base directory.</p>
+     * <strong>NOTE: </strong>As of gpg 2.1 this is an obsolete option and ignored. All secret keys are stored in the
+     * ‘private-keys-v1.d’ directory below the GnuPG home directory.
      *
      * @since 1.2
      */
diff --git a/src/main/java/org/apache/maven/plugin/gpg/GpgSigner.java b/src/main/java/org/apache/maven/plugin/gpg/GpgSigner.java
index 39d5d83..0fb63a8 100644
--- a/src/main/java/org/apache/maven/plugin/gpg/GpgSigner.java
+++ b/src/main/java/org/apache/maven/plugin/gpg/GpgSigner.java
@@ -65,6 +65,12 @@
         {
             cmd.setExecutable( "gpg" + ( Os.isFamily( Os.FAMILY_WINDOWS ) ? ".exe" : "" ) );
         }
+        
+        GpgVersionParser versionParser = GpgVersionParser.parse( executable );
+        
+        GpgVersion gpgVersion = versionParser.getGpgVersion();
+        
+        getLog().debug( gpgVersion.toString() );
 
         if ( args != null )
         {
@@ -80,13 +86,21 @@
             cmd.createArg().setFile( homeDir );
         }
 
-        if ( useAgent )
+        if ( gpgVersion.isBefore( GpgVersion.parse( "2.1" ) ) )
         {
-            cmd.createArg().setValue( "--use-agent" );
+            if ( useAgent )
+            {
+                cmd.createArg().setValue( "--use-agent" );
+            }
+            else
+            {
+                cmd.createArg().setValue( "--no-use-agent" );
+            }
         }
         else
         {
-            cmd.createArg().setValue( "--no-use-agent" );
+            cmd.createArg().setValue( "--pinentry-mode" );
+            cmd.createArg().setValue( "loopback" );
         }
 
         InputStream in = null;
@@ -126,8 +140,16 @@
 
         if ( StringUtils.isNotEmpty( secretKeyring ) )
         {
-            cmd.createArg().setValue( "--secret-keyring" );
-            cmd.createArg().setValue( secretKeyring );
+            if ( gpgVersion.isBefore( GpgVersion.parse( "2.1" ) ) ) 
+            {
+                cmd.createArg().setValue( "--secret-keyring" );
+                cmd.createArg().setValue( secretKeyring );
+            }
+            else
+            {
+                getLog().warn( "'secretKeyring' is an obsolete option and ignored. All secret keys "
+                    + "are stored in the ‘private-keys-v1.d’ directory below the GnuPG home directory." );
+            }
         }
 
         if ( StringUtils.isNotEmpty( publicKeyring ) )
diff --git a/src/main/java/org/apache/maven/plugin/gpg/GpgVersion.java b/src/main/java/org/apache/maven/plugin/gpg/GpgVersion.java
new file mode 100644
index 0000000..49010ab
--- /dev/null
+++ b/src/main/java/org/apache/maven/plugin/gpg/GpgVersion.java
@@ -0,0 +1,140 @@
+package org.apache.maven.plugin.gpg;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * 
+ * @author Robert Scholte
+ * @since 3.0.0
+ */
+public class GpgVersion implements Comparable<GpgVersion>
+{
+    private final String rawVersion;
+    
+    private GpgVersion( String rawVersion )
+    {
+        this.rawVersion = rawVersion;
+    }
+    
+    public static GpgVersion parse( String rawVersion )
+    {
+        return new GpgVersion( rawVersion );
+    }
+
+    @Override
+    public int compareTo( GpgVersion other )
+    {
+        Pattern p = Pattern.compile( "([.\\d]+)$" );
+        
+        String[] thisSegments;
+        Matcher m = p.matcher( rawVersion );
+        if ( m.find() )
+        {
+            thisSegments  = m.group( 1 ).split( "\\." );
+        }
+        else 
+        {
+          throw new IllegalArgumentException( "Can't parse version of " + this.rawVersion );   
+        }
+        
+        String[] otherSegments;
+        m = p.matcher( other.rawVersion );
+        if ( m.find() )
+        {
+            otherSegments  = m.group( 1 ).split( "\\." );
+        }
+        else 
+        {
+          throw new IllegalArgumentException( "Can't parse version of " + other.rawVersion );   
+        }
+        
+        int minSegments = Math.min( thisSegments.length, otherSegments.length );
+        
+        for ( int index = 0; index < minSegments; index++ )
+        {
+            int thisValue = Integer.parseInt( thisSegments[index] );
+            
+            int otherValue = Integer.parseInt( otherSegments[index] );
+            
+            int compareValue = Integer.compare( thisValue, otherValue );
+            
+            if ( compareValue != 0 )
+            {
+                return compareValue;
+            }
+        }
+        
+        return ( thisSegments.length - otherSegments.length );
+    }
+
+    /**
+     * Verify if this version is before some other version
+     * 
+     * @param other the version to compare with
+     * @return {@code true} is this is less than {@code other}, otherwise {@code false}
+     */
+    public boolean isBefore( GpgVersion other )
+    {
+        return this.compareTo( other ) < 0;
+    }
+
+    /**
+     * Verify if this version is before some other version
+     * 
+     * @param other the version to compare with
+     * @return {@code true}  is this is less than {@code other}, otherwise {@code false}
+     */
+    public boolean isBefore( String other )
+    {
+        return this.compareTo( parse( other ) ) < 0;
+    }
+
+    /**
+     * Verify if this version is at least some other version
+     * 
+     * @param other the version to compare with
+     * @return  {@code true}  is this is greater than or equal to {@code other}, otherwise {@code false}
+     */
+    public boolean isAtLeast( GpgVersion other )
+    {
+        return this.compareTo( other ) >= 0;
+    }
+
+    /**
+     * Verify if this version is at least some other version
+     * 
+     * @param other the version to compare with
+     * @return  {@code true} is this is greater than or equal to {@code other}, otherwise {@code false}
+     */
+    public boolean isAtLeast( String other )
+    {
+        return this.compareTo( parse( other ) ) >= 0;
+    }
+    
+    @Override
+    public String toString()
+    {
+        return rawVersion;
+    }
+    
+}
diff --git a/src/main/java/org/apache/maven/plugin/gpg/GpgVersionParser.java b/src/main/java/org/apache/maven/plugin/gpg/GpgVersionParser.java
new file mode 100644
index 0000000..7ec7b69
--- /dev/null
+++ b/src/main/java/org/apache/maven/plugin/gpg/GpgVersionParser.java
@@ -0,0 +1,109 @@
+package org.apache.maven.plugin.gpg;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.codehaus.plexus.util.Os;
+import org.codehaus.plexus.util.StringUtils;
+import org.codehaus.plexus.util.cli.CommandLineException;
+import org.codehaus.plexus.util.cli.CommandLineUtils;
+import org.codehaus.plexus.util.cli.Commandline;
+import org.codehaus.plexus.util.cli.StreamConsumer;
+
+/**
+ * 
+ * @author Robert Scholte
+ * @since 3.0.0
+ */
+public class GpgVersionParser
+{
+    private final GpgVersionConsumer consumer;
+    
+    private GpgVersionParser( GpgVersionConsumer consumer )
+    {
+        this.consumer = consumer;
+        
+    }
+    
+    public static GpgVersionParser parse( String executable )
+    {
+        Commandline cmd = new Commandline();
+
+        if ( StringUtils.isNotEmpty( executable ) )
+        {
+            cmd.setExecutable( executable );
+        }
+        else
+        {
+            cmd.setExecutable( "gpg" + ( Os.isFamily( Os.FAMILY_WINDOWS ) ? ".exe" : "" ) );
+        }
+
+        
+        cmd.createArg().setValue( "--version" );
+        
+        GpgVersionConsumer out = new GpgVersionConsumer();
+        
+        try
+        {
+           CommandLineUtils.executeCommandLine( cmd, null, out, null );
+        }
+        catch ( CommandLineException e )
+        {
+            // TODO probably a dedicated exception
+        }
+        
+        return new GpgVersionParser( out );
+    }
+    
+    public GpgVersion getGpgVersion()
+    {
+        return consumer.gpgVersion;
+
+    }
+
+    /**
+     * Consumes the output of {@code gpg --version}
+     * 
+     * @author Robert Scholte
+     * @since 3.0.0
+     */
+    static class GpgVersionConsumer
+        implements StreamConsumer
+    {
+        private final Pattern gpgVersionPattern = Pattern.compile( "gpg \\(GnuPG\\) .+" );
+
+        private GpgVersion gpgVersion;
+        
+        @Override
+        public void consumeLine( String line )
+            throws IOException
+        {
+            Matcher m = gpgVersionPattern.matcher( line );
+            if ( m.matches() )
+            {
+                gpgVersion = GpgVersion.parse( m.group() );
+            }
+        }
+    }
+
+}
diff --git a/src/test/java/org/apache/maven/plugin/gpg/GpgVersionTest.java b/src/test/java/org/apache/maven/plugin/gpg/GpgVersionTest.java
new file mode 100644
index 0000000..2204aca
--- /dev/null
+++ b/src/test/java/org/apache/maven/plugin/gpg/GpgVersionTest.java
@@ -0,0 +1,35 @@
+package org.apache.maven.plugin.gpg;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class GpgVersionTest
+{
+    @Test
+    public void test()
+    {
+        assertTrue( GpgVersion.parse( "gpg (GnuPG) 2.2.1" ).isAtLeast( GpgVersion.parse( "gpg (GnuPG) 2.2.1" ) ) );
+        assertTrue( GpgVersion.parse( "gpg (GnuPG) 2.2.1" ).isAtLeast( GpgVersion.parse( "2.1" ) ) );
+    }
+
+}