GUC patch for Win32 - Mailing list pgsql-patches

From Bruce Momjian
Subject GUC patch for Win32
Date
Msg-id 200305012013.h41KD8312533@candle.pha.pa.us
Whole thread Raw
Responses Re: GUC patch for Win32  (Tom Lane <tgl@sss.pgh.pa.us>)
Re: GUC patch for Win32  (Peter Eisentraut <peter_e@gmx.net>)
List pgsql-patches
Here is a patch that saves the postmaster GUC state into a file to be
read in by exec'ed backends.  This is a start toward a fork/exec option
for Unix (for testing) and a CreateProcess option for Win32.

The patch basically writes all modified GUC variables to a binary file
that can be read in by newly created exec'ed backends.

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
Index: src/backend/postmaster/postmaster.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/backend/postmaster/postmaster.c,v
retrieving revision 1.315
diff -c -c -r1.315 postmaster.c
*** src/backend/postmaster/postmaster.c    26 Apr 2003 02:57:14 -0000    1.315
--- src/backend/postmaster/postmaster.c    1 May 2003 19:53:54 -0000
***************
*** 256,266 ****
  static void CleanupProc(int pid, int exitstatus);
  static void LogChildExit(int lev, const char *procname,
               int pid, int exitstatus);
! static int    DoBackend(Port *port);
  void        ExitPostmaster(int status);
  static void usage(const char *);
  static int    ServerLoop(void);
  static int    BackendStartup(Port *port);
  static int    ProcessStartupPacket(Port *port, bool SSLdone);
  static void processCancelRequest(Port *port, void *pkt);
  static int    initMasks(fd_set *rmask, fd_set *wmask);
--- 256,267 ----
  static void CleanupProc(int pid, int exitstatus);
  static void LogChildExit(int lev, const char *procname,
               int pid, int exitstatus);
! static int    BackendFinalize(Port *port);
  void        ExitPostmaster(int status);
  static void usage(const char *);
  static int    ServerLoop(void);
  static int    BackendStartup(Port *port);
+ static void BackendFork(Port *port, Backend *bn);
  static int    ProcessStartupPacket(Port *port, bool SSLdone);
  static void processCancelRequest(Port *port, void *pkt);
  static int    initMasks(fd_set *rmask, fd_set *wmask);
***************
*** 570,575 ****
--- 571,579 ----
      SetDataDir(potential_DataDir);

      ProcessConfigFile(PGC_POSTMASTER);
+ #ifdef EXEC_BACKEND
+     write_nondefault_variables(PGC_POSTMASTER);
+ #endif

      /*
       * Check for invalid combinations of GUC settings.
***************
*** 1231,1237 ****
       * Now fetch parameters out of startup packet and save them into the
       * Port structure.  All data structures attached to the Port struct
       * must be allocated in TopMemoryContext so that they won't disappear
!      * when we pass them to PostgresMain (see DoBackend).  We need not worry
       * about leaking this storage on failure, since we aren't in the postmaster
       * process anymore.
       */
--- 1235,1241 ----
       * Now fetch parameters out of startup packet and save them into the
       * Port structure.  All data structures attached to the Port struct
       * must be allocated in TopMemoryContext so that they won't disappear
!      * when we pass them to PostgresMain (see BackendFinalize).  We need not worry
       * about leaking this storage on failure, since we aren't in the postmaster
       * process anymore.
       */
***************
*** 1568,1573 ****
--- 1572,1580 ----
          elog(LOG, "Received SIGHUP, reloading configuration files");
          SignalChildren(SIGHUP);
          ProcessConfigFile(PGC_SIGHUP);
+ #ifdef EXEC_BACKEND
+         write_nondefault_variables(PGC_SIGHUP);
+ #endif
          load_hba();
          load_ident();
      }
***************
*** 2053,2080 ****
      pid = fork();

      if (pid == 0)                /* child */
!     {
!         int            status;
!
! #ifdef LINUX_PROFILE
!         setitimer(ITIMER_PROF, &prof_itimer, NULL);
! #endif
!
! #ifdef __BEOS__
!         /* Specific beos backend startup actions */
!         beos_backend_startup();
! #endif
!         free(bn);
!
!         status = DoBackend(port);
!         if (status != 0)
!         {
!             elog(LOG, "connection startup failed");
!             proc_exit(status);
!         }
!         else
!             proc_exit(0);
!     }

      /* in parent, error */
      if (pid < 0)
--- 2060,2066 ----
      pid = fork();

      if (pid == 0)                /* child */
!         BackendFork(port, bn);    /* never returns */

      /* in parent, error */
      if (pid < 0)
***************
*** 2108,2113 ****
--- 2094,2124 ----
  }


+ static void
+ BackendFork(Port *port, Backend *bn)
+ {
+     int            status;
+
+ #ifdef LINUX_PROFILE
+     setitimer(ITIMER_PROF, &prof_itimer, NULL);
+ #endif
+
+ #ifdef __BEOS__
+     /* Specific beos backend startup actions */
+     beos_backend_startup();
+ #endif
+     free(bn);
+
+     status = BackendFinalize(port);
+     if (status != 0)
+     {
+         elog(LOG, "connection startup failed");
+         proc_exit(status);
+     }
+     else
+         proc_exit(0);
+ }
+
  /*
   * Try to report backend fork() failure to client before we close the
   * connection.    Since we do not care to risk blocking the postmaster on
***************
*** 2173,2179 ****
  }

  /*
!  * DoBackend -- perform authentication, and if successful, set up the
   *        backend's argument list and invoke backend main().
   *
   * This used to perform an execv() but we no longer exec the backend;
--- 2184,2190 ----
  }

  /*
!  * BackendFinalize -- perform authentication, and if successful, set up the
   *        backend's argument list and invoke backend main().
   *
   * This used to perform an execv() but we no longer exec the backend;
***************
*** 2184,2190 ****
   *        If PostgresMain() fails, return status.
   */
  static int
! DoBackend(Port *port)
  {
      char       *remote_host;
      char      **av;
--- 2195,2201 ----
   *        If PostgresMain() fails, return status.
   */
  static int
! BackendFinalize(Port *port)
  {
      char       *remote_host;
      char      **av;
***************
*** 2221,2226 ****
--- 2232,2241 ----
      /* Reset MyProcPid to new backend's pid */
      MyProcPid = getpid();

+ #ifdef EXEC_BACKEND
+     read_nondefault_variables();
+ #endif
+
      /*
       * Initialize libpq and enable reporting of elog errors to the client.
       * Must do this now because authentication uses libpq to send
***************
*** 2248,2254 ****
          unsigned short remote_port;
          char       *host_addr;
  #ifdef HAVE_IPV6
!         char       ip_hostinfo[INET6_ADDRSTRLEN];
  #else
          char       ip_hostinfo[INET_ADDRSTRLEN];
  #endif
--- 2263,2269 ----
          unsigned short remote_port;
          char       *host_addr;
  #ifdef HAVE_IPV6
!         char       ip_hostinfo[INET6_ADDRSTRLEN];
  #else
          char       ip_hostinfo[INET_ADDRSTRLEN];
  #endif
***************
*** 2294,2300 ****
      }
      else
      {
!         /* not AF_INET */
          remote_host = "[local]";

          if (Log_connections)
--- 2309,2315 ----
      }
      else
      {
!            /* not AF_INET */
          remote_host = "[local]";

          if (Log_connections)
***************
*** 2318,2324 ****
       * indefinitely.  PreAuthDelay doesn't count against the time limit.
       */
      if (!enable_sig_alarm(AuthenticationTimeout * 1000, false))
!         elog(FATAL, "DoBackend: Unable to set timer for auth timeout");

      /*
       * Receive the startup packet (which might turn out to be a cancel
--- 2333,2339 ----
       * indefinitely.  PreAuthDelay doesn't count against the time limit.
       */
      if (!enable_sig_alarm(AuthenticationTimeout * 1000, false))
!         elog(FATAL, "BackendFinalize: Unable to set timer for auth timeout");

      /*
       * Receive the startup packet (which might turn out to be a cancel
***************
*** 2347,2353 ****
       * SIGTERM/SIGQUIT again until backend startup is complete.
       */
      if (!disable_sig_alarm(false))
!         elog(FATAL, "DoBackend: Unable to disable timer for auth timeout");
      PG_SETMASK(&BlockSig);

      if (Log_connections)
--- 2362,2368 ----
       * SIGTERM/SIGQUIT again until backend startup is complete.
       */
      if (!disable_sig_alarm(false))
!         elog(FATAL, "BackendFinalize: Unable to disable timer for auth timeout");
      PG_SETMASK(&BlockSig);

      if (Log_connections)
Index: src/backend/utils/misc/guc.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/backend/utils/misc/guc.c,v
retrieving revision 1.119
diff -c -c -r1.119 guc.c
*** src/backend/utils/misc/guc.c    25 Apr 2003 19:45:09 -0000    1.119
--- src/backend/utils/misc/guc.c    1 May 2003 19:53:57 -0000
***************
*** 60,65 ****
--- 60,68 ----
  #define PG_KRB_SRVTAB ""
  #endif

+ #ifdef EXEC_BACKEND
+ #define CONFIG_EXEC_PARAMS "global/config_exec_params"
+ #endif

  /* XXX these should appear in other modules' header files */
  extern bool Log_connections;
***************
*** 2801,2806 ****
--- 2804,3007 ----
  }


+ #ifdef EXEC_BACKEND
+ /*
+  *    This routine dumps out all non-default GUC options into a binary
+  *    file that is read by all exec'ed backends.  The format is:
+  *
+  *        variable name, string, null terminated
+  *        variable value, string, null terminated
+  *        variable source, integer
+  */
+ void
+ write_nondefault_variables(GucContext context)
+ {
+     int i;
+     char *new_filename, *filename;
+     int elevel;
+     FILE *fp;
+
+     Assert(context == PGC_POSTMASTER || context == PGC_SIGHUP);
+     Assert(DataDir);
+     elevel = (context == PGC_SIGHUP) ? DEBUG3 : ERROR;
+
+     /*
+      * Open file
+      */
+     new_filename = malloc(strlen(DataDir) + strlen(CONFIG_EXEC_PARAMS) +
+                             strlen(".new") + 2);
+     filename = malloc(strlen(DataDir) + strlen(CONFIG_EXEC_PARAMS) + 2);
+     if (new_filename == NULL || filename == NULL)
+     {
+         elog(elevel, "out of memory");
+         return;
+     }
+     sprintf(new_filename, "%s/" CONFIG_EXEC_PARAMS ".new", DataDir);
+     sprintf(filename, "%s/" CONFIG_EXEC_PARAMS, DataDir);
+
+     fp = AllocateFile(new_filename, "w");
+     if (!fp)
+     {
+          free(new_filename);
+         free(filename);
+         elog(elevel, "could not write exec config params file `"
+                     CONFIG_EXEC_PARAMS "': %s", strerror(errno));
+         return;
+     }
+
+     for (i = 0; i < num_guc_variables; i++)
+     {
+         struct config_generic *gconf = guc_variables[i];
+
+         if (gconf->source != PGC_S_DEFAULT)
+         {
+             fprintf(fp, "%s", gconf->name);
+             fputc(0, fp);
+
+             switch (gconf->vartype)
+             {
+                 case PGC_BOOL:
+                     {
+                         struct config_bool *conf = (struct config_bool *) gconf;
+
+                         if (*conf->variable == 0)
+                             fprintf(fp, "false");
+                         else
+                             fprintf(fp, "true");
+                     }
+                     break;
+
+                 case PGC_INT:
+                     {
+                         struct config_int *conf = (struct config_int *) gconf;
+
+                         fprintf(fp, "%d", *conf->variable);
+                     }
+                     break;
+
+                 case PGC_REAL:
+                     {
+                         struct config_real *conf = (struct config_real *) gconf;
+
+                         /* Could lose precision here? */
+                         fprintf(fp, "%f", *conf->variable);
+                     }
+                     break;
+
+                 case PGC_STRING:
+                     {
+                         struct config_string *conf = (struct config_string *) gconf;
+
+                         fprintf(fp, "%s", *conf->variable);
+                     }
+                     break;
+             }
+
+             fputc(0, fp);
+
+             fwrite(&gconf->source, sizeof(gconf->source), 1, fp);
+         }
+     }
+
+     FreeFile(fp);
+     /* Put new file in place, this could delay on Win32 */
+     rename(new_filename, filename);
+     free(new_filename);
+     free(filename);
+     return;
+ }
+
+
+ /*
+  *    Read string, including null byte from file
+  *
+  *    Return NULL on EOF and nothing read
+  */
+ static char *
+ read_string_with_null(FILE *fp)
+ {
+     int i = 0, ch, maxlen = 256;
+     char *str = NULL;
+
+     do
+     {
+         if ((ch = fgetc(fp)) == EOF)
+         {
+             if (i == 0)
+                 return NULL;
+             else
+                 elog(FATAL, "Invalid format of exec config params file");
+         }
+         if (i == 0)
+             str = malloc(maxlen);
+         else if (i == maxlen)
+             str = realloc(str, maxlen *= 2);
+         str[i++] = ch;
+     } while (ch != 0);
+
+     return str;
+ }
+
+
+ /*
+  *    This routine loads a previous postmaster dump of its non-default
+  *    settings.
+  */
+ void
+ read_nondefault_variables(void)
+ {
+     char *filename;
+     FILE *fp;
+     char *varname, *varvalue;
+     int varsource;
+
+     Assert(DataDir);
+
+     /*
+      * Open file
+      */
+     filename = malloc(strlen(DataDir) + strlen(CONFIG_EXEC_PARAMS) + 2);
+     if (filename == NULL)
+     {
+         elog(ERROR, "out of memory");
+         return;
+     }
+     sprintf(filename, "%s/" CONFIG_EXEC_PARAMS, DataDir);
+
+     fp = AllocateFile(filename, "r");
+     if (!fp)
+     {
+         free(filename);
+         /* File not found is fine */
+         if (errno != ENOENT)
+             elog(FATAL, "could not read exec config params file `"
+                     CONFIG_EXEC_PARAMS "': %s", strerror(errno));
+         return;
+     }
+
+     while (1)
+     {
+         if ((varname = read_string_with_null(fp)) == NULL)
+             break;
+
+         if ((varvalue = read_string_with_null(fp)) == NULL)
+             elog(FATAL, "Invalid format of exec config params file");
+          if (fread(&varsource, sizeof(varsource), 1, fp) == 0)
+             elog(FATAL, "Invalid format of exec config params file");
+
+         (void) set_config_option(varname, varvalue, PGC_POSTMASTER,
+                 varsource, false, true);
+         free(varname);
+         free(varvalue);
+     }
+
+     FreeFile(fp);
+     free(filename);
+     return;
+ }
+ #endif
+
+
  /*
   * A little "long argument" simulation, although not quite GNU
   * compliant. Takes a string of the form "some-option=some value" and
***************
*** 3203,3205 ****
--- 3404,3407 ----


  #include "guc-file.c"
+
Index: src/include/utils/guc.h
===================================================================
RCS file: /cvsroot/pgsql-server/src/include/utils/guc.h,v
retrieving revision 1.27
diff -c -c -r1.27 guc.h
*** src/include/utils/guc.h    25 Apr 2003 19:45:09 -0000    1.27
--- src/include/utils/guc.h    1 May 2003 19:54:01 -0000
***************
*** 135,138 ****
--- 135,143 ----
  extern ArrayType *GUCArrayAdd(ArrayType *array, const char *name, const char *value);
  extern ArrayType *GUCArrayDelete(ArrayType *array, const char *name);

+ #ifdef EXEC_BACKEND
+ void write_nondefault_variables(GucContext context);
+ void read_nondefault_variables(void);
+ #endif
+
  #endif   /* GUC_H */

pgsql-patches by date:

Previous
From: Josh Berkus
Date:
Subject: Re: [HACKERS] "Adding missing from clause"
Next
From: Tom Lane
Date:
Subject: Re: GUC patch for Win32