@@ -238,10 +238,113 @@ bool launch_agent(void)
238238 fio_redirect (infd [0 ], outfd [1 ], errfd [0 ]); /* write to stdout */
239239 }
240240
241- /* Make sure that remote agent has the same version
242- * TODO: we must also check PG version and fork edition
243- */
244- agent_version = fio_get_agent_version ();
241+
242+ /* Make sure that remote agent has the same version, fork and other features to be binary compatible */
243+ {
244+ char payload_buf [1024 ];
245+ fio_get_agent_version (& agent_version , payload_buf , sizeof payload_buf );
246+ check_remote_agent_compatibility (agent_version , payload_buf , sizeof payload_buf );
247+ }
248+
249+ return true;
250+ }
251+
252+ #ifdef PGPRO_EDITION
253+ /* PGPRO 10-13 checks to be "(certified)", with exceptional case PGPRO_11 conforming to "(standard certified)" */
254+ static bool check_certified ()
255+ {
256+ return strstr (PGPRO_VERSION_STR , "(certified)" ) ||
257+ strstr (PGPRO_VERSION_STR , "(standard certified)" );
258+ }
259+ #endif
260+
261+ static char * extract_pg_edition_str ()
262+ {
263+ static char * vanilla = "vanilla" ;
264+ #ifdef PGPRO_EDITION
265+ static char * _1C = "1C" ;
266+ static char * std = "standard" ;
267+ static char * ent = "enterprise" ;
268+ static char * std_cert = "standard-certified" ;
269+ static char * ent_cert = "enterprise-certified" ;
270+
271+ if (strcmp (PGPRO_EDITION , _1C ) == 0 )
272+ return vanilla ;
273+
274+ if (PG_VERSION_NUM < 100000 )
275+ return PGPRO_EDITION ;
276+
277+ /* these "certified" checks are applicable to PGPRO from 10 up to 12 versions.
278+ * 13+ certified versions are compatible to non-certified ones */
279+ if (PG_VERSION_NUM < 130000 && check_certified ())
280+ {
281+ if (strcmp (PGPRO_EDITION , std ) == 0 )
282+ return std_cert ;
283+ else if (strcmp (PGPRO_EDITION , ent ) == 0 )
284+ return ent_cert ;
285+ else
286+ Assert ("Bad #define PGPRO_EDITION value" == 0 );
287+ }
288+
289+ return PGPRO_EDITION ;
290+ #else
291+ return vanilla ;
292+ #endif
293+ }
294+
295+ #define COMPATIBILITY_VAL_STR (macro ) { #macro, macro, 0 }
296+ #define COMPATIBILITY_VAL_INT (macro ) { #macro, NULL, macro }
297+
298+ #define COMPATIBILITY_VAL_SEPARATOR "="
299+ #define COMPATIBILITY_LINE_SEPARATOR "\n"
300+
301+ /*
302+ * Compose compatibility string to be sent by pg_probackup agent
303+ * through ssh and to be verified by pg_probackup peer.
304+ * Compatibility string contains postgres essential vars as strings
305+ * in format "var_name" + COMPATIBILITY_VAL_SEPARATOR + "var_value" + COMPATIBILITY_LINE_SEPARATOR
306+ */
307+ size_t prepare_compatibility_str (char * compatibility_buf , size_t compatibility_buf_size )
308+ {
309+ typedef struct compatibility_param_tag {
310+ const char * name ;
311+ const char * strval ;
312+ int intval ;
313+ } compatibility_param ;
314+
315+ compatibility_param compatibility_params [] = {
316+ COMPATIBILITY_VAL_STR (PG_MAJORVERSION ),
317+ { "edition" , extract_pg_edition_str (), 0 },
318+ COMPATIBILITY_VAL_INT (SIZEOF_VOID_P ),
319+ };
320+
321+ size_t result_size = 0 ;
322+ * compatibility_buf = '\0' ;
323+
324+ for (int i = 0 ; i < (sizeof compatibility_params / sizeof (compatibility_param )); i ++ )
325+ {
326+ if (compatibility_params [i ].strval != NULL )
327+ result_size += snprintf (compatibility_buf + result_size , compatibility_buf_size - result_size ,
328+ "%s" COMPATIBILITY_VAL_SEPARATOR "%s" COMPATIBILITY_LINE_SEPARATOR ,
329+ compatibility_params [i ].name ,
330+ compatibility_params [i ].strval );
331+ else
332+ result_size += snprintf (compatibility_buf + result_size , compatibility_buf_size - result_size ,
333+ "%s" COMPATIBILITY_VAL_SEPARATOR "%d" COMPATIBILITY_LINE_SEPARATOR ,
334+ compatibility_params [i ].name ,
335+ compatibility_params [i ].intval );
336+ Assert (result_size < compatibility_buf_size );
337+ }
338+ return result_size + 1 ;
339+ }
340+
341+ /*
342+ * Check incoming remote agent's compatibility params for equality to local ones.
343+ */
344+ void check_remote_agent_compatibility (int agent_version , char * compatibility_str , size_t compatibility_str_max_size )
345+ {
346+ elog (LOG , "Agent version=%d\n" , agent_version );
347+
245348 if (agent_version != AGENT_PROTOCOL_VERSION )
246349 {
247350 char agent_version_str [1024 ];
@@ -255,5 +358,21 @@ bool launch_agent(void)
255358 agent_version_str , AGENT_PROTOCOL_VERSION_STR );
256359 }
257360
258- return true;
361+ /* checking compatibility params */
362+ if (strnlen (compatibility_str , compatibility_str_max_size ) == compatibility_str_max_size )
363+ {
364+ elog (ERROR , "Corrupted remote compatibility protocol: compatibility string has no terminating \\0" );
365+ }
366+
367+ elog (LOG , "Agent compatibility params:\n%s" , compatibility_str );
368+
369+ {
370+ char buf [1024 ];
371+
372+ prepare_compatibility_str (buf , sizeof buf );
373+ if (strcmp (compatibility_str , buf ))
374+ {
375+ elog (ERROR , "Incompatible remote agent params, expected:\n%s, actual:\n:%s" , buf , compatibility_str );
376+ }
377+ }
259378}
0 commit comments