Package: netpbm
Version: 2:10.0-15.3
Severity: wishlist
Tags: patch upstream
User: reproducible-bui...@lists.alioth.debian.org
Usertags: toolchain
X-Debbugs-Cc: reproducible-bui...@lists.alioth.debian.org

Dear Maintainer,

While working on the "reproducible builds" effort [1], we have noticed
that some packages (like latex2html) use one of the netpbm utilities in
their process, leading to unreproducible output (due to the use of the
random generator, seeded from a time() call).

To solve this kind of issues, it would be nice to make netpbm support
the SOURCE_DATE_EPOCH environment variable [2].

See the attached patch for a proposed solution.

Regards,
Alexis Bienvenüe.

[1] https://wiki.debian.org/ReproducibleBuilds
[2] https://reproducible-builds.org/specs/source-date-epoch/


diff -u netpbm-free-10.0/debian/changelog netpbm-free-10.0/debian/changelog
--- netpbm-free-10.0/debian/changelog
+++ netpbm-free-10.0/debian/changelog
@@ -1,3 +1,9 @@
+netpbm-free (2:10.0-15.3.0~reproducible1) UNRELEASED; urgency=medium
+
+  * SOURCE_DATE_EPOCH support.
+
+ -- Alexis Bienvenüe <p...@passoire.fr>  Sun, 12 Jun 2016 11:43:19 +0200
+
 netpbm-free (2:10.0-15.3) unstable; urgency=medium
 
   * Non-maintainer upload.
only in patch2:
unchanged:
--- netpbm-free-10.0.orig/include/pm.h
+++ netpbm-free-10.0/include/pm.h
@@ -20,6 +20,7 @@
 #include <stdio.h>
 #include <errno.h>
 #include <sys/stat.h>
+#include <time.h>
 
 #ifdef VMS
 #include <perror.h>
@@ -245,5 +246,10 @@
 char *
 pm_arg0toprogname(const char arg0[]);
 
+/* SOURCE_DATE_EPOCH support */
+
+time_t
+pm_source_time(int use_pid);
+
 #endif
 
only in patch2:
unchanged:
--- netpbm-free-10.0.orig/pbm/libpm.c
+++ netpbm-free-10.0/pbm/libpm.c
@@ -23,6 +23,9 @@
 #include <stdarg.h>
 #include <string.h>
 #include <errno.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <unistd.h>
 
 /* The following are set by pm_init(), then used by subsequent calls to other
    pm_xxx() functions.
@@ -938,3 +941,42 @@
 	return realloc(a, b*c);
 }
 
+time_t pm_source_time(int use_pid)
+{
+  time_t now;
+  char *source_date_epoch;
+  unsigned long long epoch;
+  char *endptr;
+
+  source_date_epoch = getenv("SOURCE_DATE_EPOCH");
+  if (source_date_epoch) {
+    errno = 0;
+    epoch = strtoull(source_date_epoch, &endptr, 10);
+    if ((errno == ERANGE && (epoch == ULLONG_MAX || epoch == 0))
+        || (errno != 0 && epoch == 0)) {
+      fprintf(stderr, "Environment variable $SOURCE_DATE_EPOCH: strtoull: %s\n", strerror(errno));
+      exit(EXIT_FAILURE);
+    }
+    if (endptr == source_date_epoch) {
+      fprintf(stderr, "Environment variable $SOURCE_DATE_EPOCH: No digits were found: %s\n", endptr);
+      exit(EXIT_FAILURE);
+    }
+    if (*endptr != '\0') {
+      fprintf(stderr, "Environment variable $SOURCE_DATE_EPOCH: Trailing garbage: %s\n", endptr);
+      exit(EXIT_FAILURE);
+    }
+    if (epoch > ULONG_MAX) {
+      fprintf(stderr, "Environment variable $SOURCE_DATE_EPOCH: value must be smaller than or equal to: %lu but was found to be: %llu \n", ULONG_MAX, epoch);
+      exit(EXIT_FAILURE);
+    }
+    now = epoch;
+    return(now);
+  } else {
+    now = time(NULL);
+    if(use_pid) {
+      return(now ^ getpid());
+    } else {
+      return(now);
+    }
+  }
+}
only in patch2:
unchanged:
--- netpbm-free-10.0.orig/pbm/pbmreduce.c
+++ netpbm-free-10.0/pbm/pbmreduce.c
@@ -98,7 +98,7 @@
         if ( thiserr == 0 || nexterr == 0 )
           pm_error( "out of memory" );
 
-        srand( (int) ( time( 0 ) ^ getpid( ) ) );
+        srand( (int) ( pm_source_time(1) ) );
         for ( col = 0; col < newcols + 2; ++col )
           thiserr[col] = ( rand( ) % SCALE - HALFSCALE ) / 4;
 	    /* (random errors in [-SCALE/8 .. SCALE/8]) */
only in patch2:
unchanged:
--- netpbm-free-10.0.orig/pgm/pgmcrater.c
+++ netpbm-free-10.0/pgm/pgmcrater.c
@@ -107,7 +107,7 @@
 {
     int i;
 
-    i = time((long *) 0) * 0xF37C;
+    i = pm_source_time(0) * 0xF37C;
     srand(i);
     for (i = 0; i < 7; i++) {
 	V rand();
only in patch2:
unchanged:
--- netpbm-free-10.0.orig/pgm/pgmnoise.c
+++ netpbm-free-10.0/pgm/pgmnoise.c
@@ -57,7 +57,7 @@
 	pgm_writepgminit(stdout, cols, rows, PGM_MAXMAXVAL, 0);
 
 	/* get time of day to feed the random number generator */
-	timenow = time(NULL);
+	timenow = pm_source_time(0);
 	srand(timenow);
 
 	/* create the (gray) noise */
only in patch2:
unchanged:
--- netpbm-free-10.0.orig/pgm/pgmtopbm.c
+++ netpbm-free-10.0/pgm/pgmtopbm.c
@@ -132,7 +132,7 @@
 	    overflow_add(cols, 2);
 	    thiserr = (long*) pm_allocrow( cols + 2, sizeof(long) );
 	    nexterr = (long*) pm_allocrow( cols + 2, sizeof(long) );
-	    srand( (int) ( time( 0 ) ^ getpid( ) ) );
+	    srand( (int) ( pm_source_time(1) ) );
 	    for ( col = 0; col < cols + 2; ++col )
 		thiserr[col] = ( rand( ) % FS_SCALE - HALF_FS_SCALE ) / 4;
 	    /* (random errors in [-FS_SCALE/8 .. FS_SCALE/8]) */
only in patch2:
unchanged:
--- netpbm-free-10.0.orig/pnm/pnmremap.c
+++ netpbm-free-10.0/pnm/pnmremap.c
@@ -243,7 +243,7 @@
             pm_error("Out of memory allocating Floyd-Steinberg structures");
     }
 
-    srand((int)(time(0) ^ getpid()));
+    srand((int)(pm_source_time(1)));
 
     {
         int col;
only in patch2:
unchanged:
--- netpbm-free-10.0.orig/ppm/libppmfloyd.c
+++ netpbm-free-10.0/ppm/libppmfloyd.c
@@ -48,7 +48,7 @@
         fi->flags = flags;
 
         if( flags & FS_RANDOMINIT ) {
-            srandom((int)(time(0) ^ getpid()));
+            srandom((int)(pm_source_time(1)));
             for( i = 0; i < cols +2; i++ ) {
                 /* random errors in [-1..+1] */
                 fi->thisrederr[i]   = random() % 32 - 16;
only in patch2:
unchanged:
--- netpbm-free-10.0.orig/ppm/ppmforge.c
+++ netpbm-free-10.0/ppm/ppmforge.c
@@ -322,7 +322,7 @@
 
 static void initseed()
 {
-    int i = time((long *) 0) ^ 0xF37C;
+    int i = pm_source_time(0) ^ 0xF37C;
     V srand(i);
     for (i = 0; i < 7; i++)
         V rand();
only in patch2:
unchanged:
--- netpbm-free-10.0.orig/ppm/ppmpat.c
+++ netpbm-free-10.0/ppm/ppmpat.c
@@ -142,7 +142,7 @@
     if ( argn != argc )
 	pm_usage( usage);
 
-    srand( (int) ( time( 0 ) ^ getpid( ) ) );
+    srand( (int) ( pm_source_time(1) ) );
     pixels = ppm_allocarray( cols, rows );
 
     switch ( pattern )
only in patch2:
unchanged:
--- netpbm-free-10.0.orig/ppm/ppmquant.c
+++ netpbm-free-10.0/ppm/ppmquant.c
@@ -522,7 +522,7 @@
     fserrP->thisberr = (long*) pm_allocrow( cols + 2, sizeof(long) );
     fserrP->nextberr = (long*) pm_allocrow( cols + 2, sizeof(long) );
 
-    srand( (int) ( time( 0 ) ^ getpid( ) ) );
+    srand( (int) ( pm_source_time(1) ) );
 
     for (col = 0; col < cols + 2; ++col) {
         fserrP->thisrerr[col] = rand( ) % ( FS_SCALE * 2 ) - FS_SCALE;
only in patch2:
unchanged:
--- netpbm-free-10.0.orig/ppm/ppmshift.c
+++ netpbm-free-10.0/ppm/ppmshift.c
@@ -76,7 +76,7 @@
 	ppm_writeppminit(stdout, cols, rows, maxval, 0);
 
 	/* get time of day to feed the random number generator */
-	timenow = time(NULL);
+	timenow = pm_source_time(0);
 	srand(timenow);
 
 	/** now do the shifting **/
only in patch2:
unchanged:
--- netpbm-free-10.0.orig/ppm/ppmspread.c
+++ netpbm-free-10.0/ppm/ppmspread.c
@@ -72,7 +72,7 @@
 
 	/* set seed for random number generator */
 	/* get time of day to feed the random number generator */
-	timenow = time(NULL);
+	timenow = pm_source_time(0);
 	srand(timenow);
 
 	/* start displacing pixels */
only in patch2:
unchanged:
--- netpbm-free-10.0.orig/urt/rle_addhist.c
+++ netpbm-free-10.0/urt/rle_addhist.c
@@ -29,6 +29,7 @@
 
 #include "rle.h"
 #include <stdio.h>
+#include <stdlib.h>
 
 #ifdef	USE_TIME_H
 #include <time.h>
@@ -63,7 +64,7 @@
 	/*     plus one for "="					*/
 	static CONST_DECL char	*histoire="HISTORY",
 				*padding="\t";
-	char	*timedate,*old= NULL;
+	char	*timedate,*old= NULL,*source_date_epoch;
 	static char	*newc;
 
 	if(getenv("NO_ADD_RLE_HISTORY"))return;
@@ -75,8 +76,14 @@
 		overflow_add(length+1, strlen(argv[i]));
 		length+= strlen(argv[i]) +1;					/* length of each arg plus space. */
 	}
-	(void)time (&temp);
-	timedate=ctime(&temp);
+        source_date_epoch=getenv("SOURCE_DATE_EPOCH");
+	if(source_date_epoch) {
+          temp=strtoull(source_date_epoch,NULL,10);
+          timedate=asctime(gmtime(&temp));
+        } else {
+          (void)time (&temp);
+          timedate=ctime(&temp);
+        }
 	length+= strlen(timedate);						/* length of date and time in ASCII. */
 
 	overflow_add(strlen(padding), 4);

Reply via email to