about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--doc/HISTORY3
-rw-r--r--other/pamexec.c49
2 files changed, 49 insertions, 3 deletions
diff --git a/doc/HISTORY b/doc/HISTORY
index f9daf84c..f1ed52ad 100644
--- a/doc/HISTORY
+++ b/doc/HISTORY
@@ -16,6 +16,9 @@ not yet  BJH  Release 10.94.00
               are used except ppmtoilbm, so seeded results are the same on
               all platforms.
 
+              pamexec: Issue message instead of being killed by a signal when
+              the exec'ed program does not read the whole image"
+
               pamscale: Fix bogus "bad magic number" or similar failure most
               of the time with -nomix.  Broken since Netpbm 10.49 (December
               2009).
diff --git a/other/pamexec.c b/other/pamexec.c
index 3d1f5086..cbde81ac 100644
--- a/other/pamexec.c
+++ b/other/pamexec.c
@@ -18,6 +18,8 @@
 #include <stdbool.h>
 #include <string.h>
 #include <stdio.h>
+#include <signal.h>
+#include <setjmp.h>
 #include <errno.h>
 
 #include "pm_c_util.h"
@@ -26,6 +28,26 @@
 #include "mallocvar.h"
 #include "pam.h"
 
+
+/* About SIGPIPE:
+
+   Unix has a strange function where by default, if you write into a pipe when
+   the reading side of the pipe has been closed, it generates a signal (of
+   class SIGPIPE), but if you tell the OS to ignore signals of class SIGPIPE,
+   then instead of generating that signal, the system call to write to the
+   pipe just fails.
+
+   Pamexec writes to a pipe when it feeds an image to the user's program's
+   Standard Input.  Should the user's program close its end of the pipe (such
+   as by exiting) before reading the whole image, that would, if we did
+   nothing to deal with it, cause Pamexec to receive a SIGPIPE signal, which
+   would make the OS terminate Pamexec.
+
+   We don't want that, so we tell the OS to ignore SIGPIPE signals, so that
+   instead our attempt to write to the pipe just fails and we fail with a
+   meaningful error message.
+*/
+
 struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
@@ -108,9 +130,25 @@ pipeOneImage(FILE * const infileP,
 
     tuplerow = pnm_allocpamrow(&inpam);
 
-    for (row = 0; row < inpam.height; ++row) {
-        pnm_readpamrow(&inpam, tuplerow);
-        pnm_writepamrow(&outpam, tuplerow);
+    {
+        jmp_buf jmpbuf;
+        int rc;
+        rc = setjmp(jmpbuf);
+        if (rc == 0) {
+            pm_setjmpbuf(&jmpbuf);
+
+            for (row = 0; row < inpam.height; ++row) {
+                pnm_readpamrow(&inpam, tuplerow);
+                pnm_writepamrow(&outpam, tuplerow);
+            }
+        } else {
+            pm_setjmpbuf(NULL);
+            pm_error("Failed to read image and pipe it to program's "
+                     "Standard Input.  If previous messages indicate "
+                     "a broken pipe error, that means the program closed "
+                     "its Standard Error (possibly by exiting) before "
+                     "it had read the entire image>");
+        }
     }
 
     pnm_freepamrow(tuplerow);
@@ -171,6 +209,11 @@ main(int argc, const char *argv[]) {
 
     parseCommandLine(argc, argv, &cmdline);
 
+    /* Make write to closed pipe fail rather than generate a signal.
+       See comments at top of program.
+    */
+    signal(SIGPIPE, SIG_IGN);
+
     ifP = pm_openr(cmdline.inputFileName);
 
     for (eof = false, imageSeq = 0; !eof; ++imageSeq) {