Improved exit status handling.
authorMicah Cowan <micah@cowan.name>
Fri, 28 Aug 2009 06:08:58 +0000 (23:08 -0700)
committerMicah Cowan <micah@cowan.name>
Fri, 28 Aug 2009 06:08:58 +0000 (23:08 -0700)
23 files changed:
ChangeLog
NEWS
src/ChangeLog
src/Makefile.am
src/exits.c [new file with mode: 0644]
src/exits.h [new file with mode: 0644]
src/http.c
src/main.c
src/recur.c
src/res.c
src/retr.c
src/retr.h
src/wget.h
tests/ChangeLog
tests/Test--spider-fail.px
tests/Test--spider-r--no-content-disposition-trivial.px
tests/Test--spider-r--no-content-disposition.px
tests/Test--spider-r-HTTP-Content-Disposition.px
tests/Test--spider-r.px
tests/Test-O-nonexisting.px
tests/Test-cookies-401.px
tests/Test-nonexisting-quiet.px
tests/WgetTest.pm.in

index dc0f6cd4bb4f4553a758ef33d380e8a7f5599c80..0af829302af52a22f246fcb0c19f4c4fcb7d173f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2009-08-27  Micah Cowan  <micah@cowan.name>
+
+       * NEWS: Mention the changes to exit codes.
+
 2009-08-27  Micah Cowan  <micah@cowan.name>
 
        * NEWS: Add mention of the NUL characters SSL security fix.
diff --git a/NEWS b/NEWS
index 87bd21c07e55e3e242bb39bf4722e1332f548013..4467817c68cf58ebc9737ca625d01c843c08f015 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -32,6 +32,9 @@ are translated from their source encoding to UTF-8 before percent-encoding.
    IRI support was added by Saint Xavier <wget@sxav.eu>, as his
    project for the Google Summer of Code.
 
+** Wget now provides more sensible exit status codes when downloads
+don't proceed as expected (see the manual).
+
 ** --default-page option (and associated wgetrc command) added to
 support alternative default names for index.html.
 
index 03795ef72b07ce76cf6d2b3389b356e6c63b7fd9..7a096ef32138edf49aa78cfa778fb76b6eee7c71 100644 (file)
@@ -1,3 +1,28 @@
+2009-08-27  Micah Cowan  <micah@cowan.name>
+
+       * wget.h (uerr_t): added new VERIFCERTERR code for SSL certificate
+       problems. Marked exit codes that are defined but never used (at
+       least, the ones I could find).
+
+       * retr.c, retr.h (retrieve_url): Added a new boolean argument to
+       determine whether an exit status should be recorded.
+       (retrieve_from_file): Adjust to new retrieve_url signature.
+
+       * res.c (res_retrieve_file): Don't have retrieve_url record an
+       exit status for robots.txt.
+
+       * recur.c (retrieve_tree): Adjust to new retrieve_url signature.
+
+       * main.c (main): Use the exit status stored by retrieve_url.
+
+       * http.c (gethttp): Distinguish certificate verification problems
+       from SSL connection issues.
+       (http_loop): Handle newly-created VERIFCERTERR error code.
+
+       * exits.c, exits.h: Newly added.
+       
+       * Makefile.am (wget_SOURCES): Add exits.c and exits.h.
+
 2009-08-27  Micah Cowan  <micah@cowan.name>
 
        * http.c (gethttp): Make sure Wget heeds cookies when they
index f0da4eee925a73f35bbba4c4fec8e55b6625f794..026ff1c8da221676873d590201ebbd904951092c 100644 (file)
@@ -49,7 +49,8 @@ wget_SOURCES = cmpt.c connect.c convert.c cookies.c ftp.c               \
               ftp.h gen-md5.h hash.h host.h html-parse.h html-url.h      \
               http.h http-ntlm.h init.h log.h mswindows.h netrc.h        \
               options.h progress.h ptimer.h recur.h res.h retr.h         \
-              spider.h ssl.h sysdep.h url.h utils.h wget.h iri.h
+              spider.h ssl.h sysdep.h url.h utils.h wget.h iri.h         \
+              exits.c exits.h
 nodist_wget_SOURCES = build_info.c version.c
 EXTRA_wget_SOURCES = mswindows.c iri.c
 LDADD = $(LIBOBJS) ../lib/libgnu.a @MD5_LDADD@
diff --git a/src/exits.c b/src/exits.c
new file mode 100644 (file)
index 0000000..254034f
--- /dev/null
@@ -0,0 +1,111 @@
+/* Command line parsing.
+   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+   2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+
+   This file is part of GNU Wget.
+
+   GNU Wget is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   GNU Wget is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with Wget.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "wget.h"
+#include "exits.h"
+
+/* Final exit code possibilities. Exit codes 1 and 2 are reserved
+ * for situations that lead to direct exits from Wget, not using the
+ * value of final_exit_status. */
+enum
+  {
+    WGET_EXIT_SUCCESS = 0,
+
+    WGET_EXIT_MINIMUM = 3,
+    WGET_EXIT_IO_FAIL = WGET_EXIT_MINIMUM,
+    WGET_EXIT_NETWORK_FAIL = 4,
+    WGET_EXIT_SSL_AUTH_FAIL = 5,
+    WGET_EXIT_SERVER_AUTH_FAIL = 6,
+    WGET_EXIT_PROTOCOL_ERROR = 7,
+    WGET_EXIT_SERVER_ERROR = 8,
+
+    WGET_EXIT_UNKNOWN
+  };
+
+static int final_exit_status = WGET_EXIT_SUCCESS;
+
+/* XXX: I don't like that newly-added uerr_t codes will doubtless fall
+   through the craccks, or the fact that we seem to have way more
+   codes than we know what to do with. Need to go through and sort
+   through the truly essential codes, and merge the rest with
+   those. Quite a few are never even used!
+
+   Quite a few of the codes below would have no business being
+   returned to retrieve_url's caller, but since it's very difficult to
+   determine which do and which don't, I grab virtually all of them to
+   be safe. */
+static int
+get_status_for_err (uerr_t err)
+{
+  switch (err)
+    {
+    case RETROK:
+      return WGET_EXIT_SUCCESS;
+    case FOPENERR: case FOPEN_EXCL_ERR: case FWRITEERR: case WRITEFAILED:
+      return WGET_EXIT_IO_FAIL;
+    case NOCONERROR: case HOSTERR: case CONSOCKERR: case CONERROR:
+    case CONSSLERR: case CONIMPOSSIBLE: case FTPRERR: case FTPINVPASV:
+    case READERR: case TRYLIMEXC:
+      return WGET_EXIT_NETWORK_FAIL;
+    case VERIFCERTERR:
+      return WGET_EXIT_SSL_AUTH_FAIL;
+    case FTPLOGINC: case FTPLOGREFUSED: case AUTHFAILED:
+      return WGET_EXIT_SERVER_AUTH_FAIL;
+    case HEOF: case HERR:
+      return WGET_EXIT_PROTOCOL_ERROR;
+    case WRONGCODE: case FTPPORTERR: case FTPSYSERR:
+    case FTPNSFOD: case FTPUNKNOWNTYPE: case FTPSRVERR:
+    case FTPRETRINT: case FTPRESTFAIL: case FTPNOPASV:
+    case CONTNOTSUPPORTED: case RANGEERR: case RETRBADPATTERN:
+    case PROXERR:
+      return WGET_EXIT_SERVER_ERROR;
+    case URLERROR: case QUOTEXC: case SSLINITFAILED:
+    default:
+      return WGET_EXIT_UNKNOWN;
+    }
+}
+
+/* inform_exit_status
+ *
+ * Ensure that Wget's exit status will reflect the problem indicated
+ * by ERR, unless the exit status has already been set to reflect a more
+ * important problem. */
+void
+inform_exit_status (uerr_t err)
+{
+  int new_status = get_status_for_err (err);
+
+  if (new_status != WGET_EXIT_SUCCESS
+      && (final_exit_status == WGET_EXIT_SUCCESS
+          || new_status < final_exit_status))
+    {
+      final_exit_status = new_status;
+    }
+}
+
+int
+get_exit_status (void)
+{
+  return
+    (final_exit_status == WGET_EXIT_UNKNOWN)
+      ? 1
+      : final_exit_status;
+}
+
diff --git a/src/exits.h b/src/exits.h
new file mode 100644 (file)
index 0000000..94fb76b
--- /dev/null
@@ -0,0 +1,30 @@
+/* Internationalization related declarations.
+   Copyright (C) 2008 Free Software Foundation, Inc.
+
+This file is part of GNU Wget.
+
+GNU Wget is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+
+GNU Wget is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Wget.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef WGET_EXITS_H
+#define WGET_EXITS_H
+
+#include "wget.h"
+
+
+void inform_exit_status (uerr_t err);
+
+int get_exit_status (void);
+
+
+#endif /* WGET_EXITS_H */
index a8705aa4b53f759aa95c9c42b12d4cf5e823c2f0..1b579bf194c9642e86b41c502ce1fcca8c53eb49 100644 (file)
@@ -1762,11 +1762,16 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy,
 
       if (conn->scheme == SCHEME_HTTPS)
         {
-          if (!ssl_connect_wget (sock) || !ssl_check_certificate (sock, u->host))
+          if (!ssl_connect_wget (sock))
             {
               fd_close (sock);
               return CONSSLERR;
             }
+          else if (!ssl_check_certificate (sock, u->host))
+            {
+              fd_close (sock);
+              return VERIFCERTERR;
+            }
           using_ssl = true;
         }
 #endif /* HAVE_SSL */
@@ -2598,7 +2603,7 @@ Spider mode enabled. Check if remote file exists.\n"));
           logprintf (LOG_NOTQUIET, _("Cannot write to %s (%s).\n"),
                      quote (hstat.local_file), strerror (errno));
         case HOSTERR: case CONIMPOSSIBLE: case PROXERR: case AUTHFAILED: 
-        case SSLINITFAILED: case CONTNOTSUPPORTED:
+        case SSLINITFAILED: case CONTNOTSUPPORTED: case VERIFCERTERR:
           /* Fatal errors just return from the function.  */
           ret = err;
           goto exit;
index 5c0b3315013189970e178752c35ffc4c4933dfbc..0f059f76233950488dbe5460a65ef657346eee9f 100644 (file)
@@ -44,6 +44,7 @@ as that of the covered work.  */
 #include <errno.h>
 #include <time.h>
 
+#include "exits.h"
 #include "utils.h"
 #include "init.h"
 #include "retr.h"
@@ -1289,7 +1290,7 @@ WARNING: Can't reopen standard output in binary mode;\n\
           else
           {
             status = retrieve_url (url_parsed, *t, &filename, &redirected_URL,
-                                   NULL, &dt, opt.recursive, iri);
+                                   NULL, &dt, opt.recursive, iri, true);
           }
 
           if (opt.delete_after && file_exists_p(filename))
@@ -1354,10 +1355,7 @@ WARNING: Can't reopen standard output in binary mode;\n\
     xfree (url[i]);
   cleanup ();
 
-  if (status == RETROK)
-    return 0;
-  else
-    return 1;
+  return get_exit_status ();
 }
 #endif /* TESTING */
 \f
index 4e95e86915104d80fb018e2b636a5faab8dfd36e..66ef2e0fd9b566d0c9c809562daa2d4ee32ddb70 100644 (file)
@@ -283,7 +283,7 @@ retrieve_tree (struct url *start_url_parsed, struct iri *pi)
           struct url *url_parsed = url_parse (url, &url_err, i, true);
 
           status = retrieve_url (url_parsed, url, &file, &redirected, referer,
-                                 &dt, false, i);
+                                 &dt, false, i, true);
 
           if (html_allowed && file && status == RETROK
               && (dt & RETROKF) && (dt & TEXTHTML))
index 4b0ff82ba5b5a15ca4cae87e607ea2ac37f016e6..eb4caf11c0b26d4a7de70ed1db55f9f334fb1f56 100644 (file)
--- a/src/res.c
+++ b/src/res.c
@@ -562,7 +562,7 @@ res_retrieve_file (const char *url, char **file, struct iri *iri)
   else
     {
       err = retrieve_url (url_parsed, robots_url, file, NULL, NULL, NULL,
-                          false, i);
+                          false, i, false);
       url_free(url_parsed);
     }
 
index b667ca2ff3cf6ecb4edca9c1148e856f68b0ecbe..f1b8f955a36f3f2361f7142b1366fd78aaba72c5 100644 (file)
@@ -39,6 +39,7 @@ as that of the covered work.  */
 #include <string.h>
 #include <assert.h>
 
+#include "exits.h"
 #include "utils.h"
 #include "retr.h"
 #include "progress.h"
@@ -611,7 +612,7 @@ static char *getproxy (struct url *);
 uerr_t
 retrieve_url (struct url * orig_parsed, const char *origurl, char **file,
               char **newloc, const char *refurl, int *dt, bool recursive,
-              struct iri *iri)
+              struct iri *iri, bool register_status)
 {
   uerr_t result;
   char *url;
@@ -668,7 +669,8 @@ retrieve_url (struct url * orig_parsed, const char *origurl, char **file,
           xfree (url);
           xfree (error);
           RESTORE_POST_DATA;
-          return PROXERR;
+          result = PROXERR;
+          goto bail;
         }
       if (proxy_url->scheme != SCHEME_HTTP && proxy_url->scheme != u->scheme)
         {
@@ -676,7 +678,8 @@ retrieve_url (struct url * orig_parsed, const char *origurl, char **file,
           url_free (proxy_url);
           xfree (url);
           RESTORE_POST_DATA;
-          return PROXERR;
+          result = PROXERR;
+          goto bail;
         }
     }
 
@@ -757,7 +760,7 @@ retrieve_url (struct url * orig_parsed, const char *origurl, char **file,
           xfree (mynewloc);
           xfree (error);
           RESTORE_POST_DATA;
-          return result;
+          goto bail;
         }
 
       /* Now mynewloc will become newloc_parsed->url, because if the
@@ -779,7 +782,8 @@ retrieve_url (struct url * orig_parsed, const char *origurl, char **file,
           xfree (url);
           xfree (mynewloc);
           RESTORE_POST_DATA;
-          return WRONGCODE;
+          result = WRONGCODE;
+          goto bail;
         }
 
       xfree (url);
@@ -866,6 +870,9 @@ retrieve_url (struct url * orig_parsed, const char *origurl, char **file,
 
   RESTORE_POST_DATA;
 
+bail:
+  if (register_status)
+    inform_exit_status (result);
   return result;
 }
 
@@ -910,7 +917,7 @@ retrieve_from_file (const char *file, bool html, int *count)
         opt.base_href = xstrdup (url);
 
       status = retrieve_url (url_parsed, url, &input_file, NULL, NULL, &dt,
-                             false, iri);
+                             false, iri, true);
       if (status != RETROK)
         return status;
 
@@ -970,7 +977,8 @@ retrieve_from_file (const char *file, bool html, int *count)
       else
         status = retrieve_url (parsed_url ? parsed_url : cur_url->url,
                                cur_url->url->url, &filename,
-                               &new_file, NULL, &dt, opt.recursive, tmpiri);
+                               &new_file, NULL, &dt, opt.recursive, tmpiri,
+                               true);
 
       if (parsed_url)
           url_free (parsed_url);
index 8854b68404179a252f4dca1ce165dec1fec26104..07c635935e20380bfc874b1d7b3405bdb8762808 100644 (file)
@@ -54,7 +54,7 @@ char *fd_read_hunk (int, hunk_terminator_t, long, long);
 char *fd_read_line (int);
 
 uerr_t retrieve_url (struct url *, const char *, char **, char **,
-                     const char *, int *, bool, struct iri *);
+                     const char *, int *, bool, struct iri *, bool);
 uerr_t retrieve_from_file (const char *, bool, int *);
 
 const char *retr_rate (wgint, double);
index 2c313bc8a6f8e9cce894ceb43ac8fe7402192466..42694c84191261b9b2999c31c98ec2eac7b8a282 100644 (file)
@@ -331,21 +331,23 @@ typedef enum
 {
   /*  0  */
   NOCONERROR, HOSTERR, CONSOCKERR, CONERROR, CONSSLERR,
-  CONIMPOSSIBLE, NEWLOCATION, NOTENOUGHMEM, CONPORTERR, CONCLOSED, 
+  CONIMPOSSIBLE, NEWLOCATION, NOTENOUGHMEM /* ! */,
+  CONPORTERR /* ! */, CONCLOSED /* ! */, 
   /* 10  */
   FTPOK, FTPLOGINC, FTPLOGREFUSED, FTPPORTERR, FTPSYSERR,
-  FTPNSFOD, FTPRETROK, FTPUNKNOWNTYPE, FTPRERR, FTPREXC
+  FTPNSFOD, FTPRETROK /* ! */, FTPUNKNOWNTYPE, FTPRERR, FTPREXC /* ! */
   /* 20  */
   FTPSRVERR, FTPRETRINT, FTPRESTFAIL, URLERROR, FOPENERR, 
-  FOPEN_EXCL_ERR, FWRITEERR, HOK, HLEXC, HEOF,
+  FOPEN_EXCL_ERR, FWRITEERR, HOK /* ! */, HLEXC /* ! */, HEOF,
   /* 30  */
-  HERR, RETROK, RECLEVELEXC, FTPACCDENIED, WRONGCODE,
+  HERR, RETROK, RECLEVELEXC, FTPACCDENIED /* ! */, WRONGCODE,
   FTPINVPASV, FTPNOPASV, CONTNOTSUPPORTED, RETRUNNEEDED, RETRFINISHED, 
   /* 40  */
-  READERR, TRYLIMEXC, URLBADPATTERN, FILEBADFILE, RANGEERR, 
-  RETRBADPATTERN, RETNOTSUP, ROBOTSOK, NOROBOTS, PROXERR, 
+  READERR, TRYLIMEXC, URLBADPATTERN /* ! */, FILEBADFILE /* ! */, RANGEERR, 
+  RETRBADPATTERN, RETNOTSUP /* ! */, ROBOTSOK /* ! */, NOROBOTS /* ! */,
+  PROXERR, 
   /* 50  */
-  AUTHFAILED, QUOTEXC, WRITEFAILED, SSLINITFAILED
+  AUTHFAILED, QUOTEXC, WRITEFAILED, SSLINITFAILED, VERIFCERTERR
 } uerr_t;
 
 /* 2005-02-19 SMS.
index 9d367e84048fb9766651436b4ebdd1f55223c911..f5e4f348029a00767426168513e531d14316e198 100644 (file)
@@ -1,3 +1,14 @@
+2009-08-27  Micah Cowan  <micah@cowan.name>
+
+       * WgetTest.pm.in (run): Shift the errcode right by 8 binary places.
+       
+       * Test--spider-fail.px, Test--spider-r--no-content-disposition.px,
+       Test--spider-r--no-content-disposition-trivial.px,
+       Test--spider-r-HTTP-Content-Disposition.px, Test--spider-r.px,
+       Test-O-nonexisting.px, Test-cookies-401.px,
+       Test-nonexisting-quiet.px: Adjusted "expected error code"; Wget's
+       exit codes have changed.
+
 2009-08-27  Micah Cowan  <micah@cowan.name>
 
        * run-px: Added Test-cookies.px, Test-cookies-401.px
index 6e5c976d3a22eb99a1ae197c50679339b8104f1c..ac8f5e6db81bc3282f7c240944887a0ae04f63a5 100755 (executable)
@@ -35,7 +35,7 @@ my %urls = (
 
 my $cmdline = $WgetTest::WGETPATH . " --spider http://localhost:{{port}}/nonexistent";
 
-my $expected_error_code = 256;
+my $expected_error_code = 8;
 
 my %expected_downloaded_files = (
 );
index 0bd7d29e6f421c569d1ed66d5c5a45b373fff77f..d18be9973a7d0e7ac786cf5b01d612c0af413d7b 100755 (executable)
@@ -92,7 +92,7 @@ my %urls = (
 
 my $cmdline = $WgetTest::WGETPATH . " --spider -r --no-content-disposition http://localhost:{{port}}/";
 
-my $expected_error_code = 0;
+my $expected_error_code = 8;
 
 my %expected_downloaded_files = (
 );
index 78beb18d706c2f3b10766a6938520de136be764b..b4c80ea0caa771ed8c57d823eb9ebb0eaf35447c 100755 (executable)
@@ -93,7 +93,7 @@ my %urls = (
 
 my $cmdline = $WgetTest::WGETPATH . " --spider -r --no-content-disposition http://localhost:{{port}}/";
 
-my $expected_error_code = 0;
+my $expected_error_code = 8;
 
 my %expected_downloaded_files = (
 );
index e79152f73bd3387a51915b97849cef7d86bf7443..a2cc5741dc464b33c80531c5ff251179d4285421 100755 (executable)
@@ -93,7 +93,7 @@ my %urls = (
 
 my $cmdline = $WgetTest::WGETPATH . " --spider -r http://localhost:{{port}}/";
 
-my $expected_error_code = 0;
+my $expected_error_code = 8;
 
 my %expected_downloaded_files = (
 );
index b32f792dd4e362e67a6ee7d4e02b832f2d44624a..b7ad76c9b1d86ac41616dd447f46888acb4d988d 100755 (executable)
@@ -92,7 +92,7 @@ my %urls = (
 
 my $cmdline = $WgetTest::WGETPATH . " --spider -r http://localhost:{{port}}/";
 
-my $expected_error_code = 0;
+my $expected_error_code = 8;
 
 my %expected_downloaded_files = (
 );
index 60ef7c70b1c7c8db2b0c2146771ffa9c9b98b663..c8df6d726ca2b1bec47d54e9d013259c828e06f1 100755 (executable)
@@ -26,7 +26,7 @@ my %urls = (
 
 my $cmdline = $WgetTest::WGETPATH . " --quiet -O out http://localhost:{{port}}/nonexistent";
 
-my $expected_error_code = 256;
+my $expected_error_code = 8;
 
 my %expected_downloaded_files = (
     'out' => {
index bb0d60e996fb9d863a652297042ced2fbceafefc..e1030c3b0a456f58c8d64f40a10904284c6f5066 100755 (executable)
@@ -32,7 +32,7 @@ my %urls = (
 my $cmdline = $WgetTest::WGETPATH . " -d http://localhost:{{port}}/one.txt"
     . " http://localhost:{{port}}/two.txt";
 
-my $expected_error_code = 0;
+my $expected_error_code = 6;
 
 my %expected_downloaded_files = (
     'two.txt' => {
index 04e11587ebdcafd21600851be4298cabf332a953..fa6fa0f2477dfc0afa8530fd9f4437088ac8a060 100755 (executable)
@@ -26,7 +26,7 @@ my %urls = (
 
 my $cmdline = $WgetTest::WGETPATH . " --quiet http://localhost:{{port}}/nonexistent";
 
-my $expected_error_code = 256;
+my $expected_error_code = 8;
 
 my %expected_downloaded_files = (
 );
index 5cd6769b8c0686d395c230ac9449fe341c7cca69..c4c0d4d94dad44ccf3d4756fd5da59faeb397c93 100644 (file)
@@ -88,6 +88,7 @@ sub run {
         ($cmdline =~ m{^/.*})
             ? system ($cmdline)
             : system ("$self->{_workdir}/../src/$cmdline");
+    $errcode >>= 8; # XXX: should handle abnormal error codes.
 
     # Shutdown server
     # if we didn't explicitely kill the server, we would have to call