681 lines
19 KiB
C++
681 lines
19 KiB
C++
/*
|
|
* util.c
|
|
*
|
|
* utility functions
|
|
*
|
|
* Copyright (c) 2010-2012, PostgreSQL Global Development Group
|
|
* contrib/pg_upgrade/util.c
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
#include "knl/knl_variable.h"
|
|
|
|
#include "pg_upgrade.h"
|
|
#include "miscadmin.h"
|
|
|
|
#include <signal.h>
|
|
|
|
#define LOG_MAX_SIZE (16 * 1024 * 1024)
|
|
#define LOG_MAX_TIMELEN 80
|
|
#define curLogFileMark "-current.log"
|
|
|
|
LogOpts log_opts;
|
|
|
|
static int get_log_filename(
|
|
char* current_logfile_name, char* prefix_name, const char* log_path, char* current_localtime);
|
|
/*
|
|
* report_status()
|
|
*
|
|
* Displays the result of an operation (ok, failed, error message,...)
|
|
*/
|
|
void report_status(eLogType type, const char* fmt, ...)
|
|
{
|
|
va_list args;
|
|
char message[MAX_STRING];
|
|
|
|
va_start(args, fmt);
|
|
/* we can not care the result*/
|
|
(void)vsnprintf(message, sizeof(message), fmt, args);
|
|
va_end(args);
|
|
|
|
pg_log(type, "%s\n", message);
|
|
}
|
|
|
|
/*
|
|
* prep_status
|
|
*
|
|
* Displays a message that describes an operation we are about to begin.
|
|
* We pad the message out to MESSAGE_WIDTH characters so that all of the "ok" and
|
|
* "failed" indicators line up nicely.
|
|
*
|
|
* A typical sequence would look like this:
|
|
* prep_status("about to flarb the next %d files", fileCount );
|
|
*
|
|
* if(( message = flarbFiles(fileCount)) == NULL)
|
|
* report_status(PG_REPORT, "ok" );
|
|
* else
|
|
* pg_log(PG_FATAL, "failed - %s\n", message );
|
|
*/
|
|
void prep_status(const char* fmt, ...)
|
|
{
|
|
va_list args;
|
|
char message[MAX_STRING];
|
|
|
|
va_start(args, fmt);
|
|
(void)vsnprintf(message, sizeof(message), fmt, args);
|
|
va_end(args);
|
|
|
|
if (strlen(message) > 0 && message[strlen(message) - 1] == '\n')
|
|
pg_log(PG_REPORT, "%s", message);
|
|
else
|
|
pg_log(PG_REPORT, "%-" MESSAGE_WIDTH "s", message);
|
|
}
|
|
|
|
static void set_log_filename(char* log_new_name, char* log_old_name)
|
|
{
|
|
int len_log_old_name = 0;
|
|
int len_suffix_name = 0;
|
|
int len_log_new_name = 0;
|
|
errno_t rc = 0;
|
|
|
|
len_log_old_name = strlen(log_old_name);
|
|
len_suffix_name = strlen(curLogFileMark);
|
|
|
|
len_log_new_name = len_log_old_name - len_suffix_name;
|
|
rc = strncpy_s(log_new_name, MAXPGPATH, log_old_name, len_log_new_name);
|
|
securec_check_c(rc, "\0", "\0");
|
|
rc = strncat_s(log_new_name, MAXPGPATH - len_log_new_name, ".log", strlen(".log"));
|
|
securec_check_c(rc, "\0", "\0");
|
|
}
|
|
|
|
void pg_init_logfiles(char* log_path, char* instance_name)
|
|
{
|
|
pg_time_t current_time;
|
|
struct tm* systm;
|
|
char current_localtime[LOG_MAX_TIMELEN] = {0};
|
|
int32 i;
|
|
struct passwd* pwd;
|
|
char path[MAXPGPATH] = {0};
|
|
|
|
char prefixname[MAXPGPATH] = {0};
|
|
int nRet = 0;
|
|
|
|
current_time = time(NULL);
|
|
systm = localtime(¤t_time);
|
|
if (NULL != systm) {
|
|
strftime(current_localtime, LOG_MAX_TIMELEN, "-%Y-%m-%d_%H%M%S", systm);
|
|
}
|
|
|
|
nRet = snprintf_s(path, MAXPGPATH, MAXPGPATH - 1, "%s/gs_upgrade", log_path);
|
|
securec_check_ss_c(nRet, "\0", "\0");
|
|
pwd = getpwnam(new_cluster.user);
|
|
if (NULL == pwd) {
|
|
mkdir(path, 0777);
|
|
} else {
|
|
mkdir(path, 0700);
|
|
chown(path, pwd->pw_uid, pwd->pw_gid);
|
|
}
|
|
|
|
nRet = snprintf_s(prefixname, MAXPGPATH, MAXPGPATH - 1, "gs_upgrade_server%s", instance_name);
|
|
securec_check_ss_c(nRet, "\0", "\0");
|
|
|
|
get_log_filename(SERVER_LOG_FILE, prefixname, path, current_localtime);
|
|
|
|
#ifndef WIN32
|
|
nRet = snprintf_s(SERVER_START_LOG_FILE, MAXPGPATH, MAXPGPATH - 1, "%s", SERVER_LOG_FILE);
|
|
securec_check_ss_c(nRet, "\0", "\0");
|
|
#else
|
|
get_log_filename(SERVER_START_LOG_FILE, "gs_upgrade_server_start", path, current_localtime);
|
|
#endif
|
|
|
|
nRet = snprintf_s(prefixname, MAXPGPATH, MAXPGPATH - 1, "gs_upgrade_restore%s", instance_name);
|
|
securec_check_ss_c(nRet, "\0", "\0");
|
|
get_log_filename(RESTORE_LOG_FILE, prefixname, path, current_localtime);
|
|
|
|
nRet = snprintf_s(prefixname, MAXPGPATH, MAXPGPATH - 1, "gs_upgrade_utility%s", instance_name);
|
|
securec_check_ss_c(nRet, "\0", "\0");
|
|
get_log_filename(UTILITY_LOG_FILE, prefixname, path, current_localtime);
|
|
|
|
nRet = snprintf_s(prefixname, MAXPGPATH, MAXPGPATH - 1, "gs_upgrade_internal%s", instance_name);
|
|
securec_check_ss_c(nRet, "\0", "\0");
|
|
get_log_filename(INTERNAL_LOG_FILE, prefixname, path, current_localtime);
|
|
|
|
if (NULL != systm) {
|
|
strftime(current_localtime, LOG_MAX_TIMELEN, "%Y-%m-%d %H:%M:%S", systm);
|
|
}
|
|
|
|
for (i = 0; i < PG_LOG_TYPE_BUT; i++) {
|
|
FILE* fp = NULL;
|
|
|
|
#ifndef WIN32
|
|
if (i == PG_LOG_TYPE_SERVER_START) {
|
|
continue;
|
|
}
|
|
#endif
|
|
if ((fp = fopen_priv(output_files[i], "a")) == NULL)
|
|
pg_log(PG_FATAL, "cannot write to log file %s\n", output_files[i]);
|
|
|
|
/* Start with newline because we might be appending to a file. */
|
|
fprintf(fp,
|
|
"\n"
|
|
"-----------------------------------------------------------------\n"
|
|
" gs_upgrade run on %s\n"
|
|
"-----------------------------------------------------------------\n\n",
|
|
current_localtime);
|
|
fclose(fp);
|
|
if (pwd == NULL) {
|
|
chmod(output_files[i], 0777);
|
|
} else {
|
|
(void)chmod(output_files[i], 0600);
|
|
(void)chown(output_files[i], pwd->pw_uid, pwd->pw_gid);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int get_log_filename(char* current_logfile_name, char* prefix_name, const char* log_path, char* log_create_time)
|
|
{
|
|
DIR* dir = NULL;
|
|
struct dirent* de;
|
|
char log_temp_name[MAXPGPATH] = {0};
|
|
char log_new_name[MAXPGPATH] = {0};
|
|
// check validity of current log file name
|
|
char* name_ptr = NULL;
|
|
int nRet = 0;
|
|
|
|
struct stat statbuf;
|
|
|
|
if (NULL == (dir = opendir(log_path))) {
|
|
pg_log(PG_FATAL, "cannot open to log folder %s\n", log_path);
|
|
|
|
return -1;
|
|
}
|
|
|
|
while (NULL != (de = readdir(dir))) {
|
|
// exist current log file
|
|
if (NULL != strstr(de->d_name, prefix_name)) {
|
|
name_ptr = strstr(de->d_name, curLogFileMark);
|
|
if (NULL != name_ptr) {
|
|
name_ptr += strlen(curLogFileMark);
|
|
if ('\0' == (*name_ptr)) {
|
|
nRet = memset_s(current_logfile_name, MAXPGPATH, 0, MAXPGPATH);
|
|
securec_check_c(nRet, "\0", "\0");
|
|
nRet = snprintf_s(current_logfile_name, MAXPGPATH, MAXPGPATH - 1, "%s/%s", log_path, de->d_name);
|
|
securec_check_ss_c(nRet, "\0", "\0");
|
|
(void)lstat(current_logfile_name, &statbuf);
|
|
|
|
if (statbuf.st_size > LOG_MAX_SIZE) {
|
|
set_log_filename(log_temp_name, de->d_name);
|
|
nRet = snprintf_s(log_new_name, MAXPGPATH, MAXPGPATH - 1, "%s/%s", log_path, log_temp_name);
|
|
securec_check_ss_c(nRet, "\0", "\0");
|
|
(void)rename(current_logfile_name, log_new_name);
|
|
break;
|
|
}
|
|
|
|
closedir(dir);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// current log file not exists or renamed to another file
|
|
nRet = memset_s(current_logfile_name, MAXPGPATH, 0, MAXPGPATH);
|
|
securec_check_c(nRet, "\0", "\0");
|
|
nRet = snprintf_s(current_logfile_name,
|
|
MAXPGPATH,
|
|
MAXPGPATH - 1,
|
|
"%s/%s%s%s",
|
|
log_path,
|
|
prefix_name,
|
|
log_create_time,
|
|
curLogFileMark);
|
|
securec_check_ss_c(nRet, "\0", "\0");
|
|
|
|
closedir(dir);
|
|
return 0;
|
|
}
|
|
|
|
void pg_log(eLogType type, char* fmt, ...)
|
|
{
|
|
va_list args;
|
|
char message[MAX_STRING] = {0};
|
|
|
|
va_start(args, fmt);
|
|
(void)vsnprintf(message, sizeof(message), fmt, args);
|
|
va_end(args);
|
|
|
|
pg_time_t current_time;
|
|
struct tm* systm;
|
|
char current_localtime[LOG_MAX_TIMELEN] = {0};
|
|
|
|
current_time = time(NULL);
|
|
systm = localtime(¤t_time);
|
|
if (NULL != systm) {
|
|
strftime(current_localtime, LOG_MAX_TIMELEN, "%Y-%m-%d %H:%M:%S", systm);
|
|
}
|
|
|
|
/* PG_VERBOSE is only output in verbose mode */
|
|
/* fopen() on log_opts.internal might have failed, so check it */
|
|
if ((type == PG_VERBOSE || log_opts.verbose) && log_opts.internal != NULL) {
|
|
/*
|
|
* There's nothing much we can do about it if fwrite fails, but some
|
|
* platforms declare fwrite with warn_unused_result. Do a little
|
|
* dance with casting to void to shut up the compiler in such cases.
|
|
*/
|
|
size_t rc;
|
|
|
|
rc = fwrite(message, strlen(message), 1, log_opts.internal);
|
|
/* if we are using OVERWRITE_MESSAGE, add newline to log file */
|
|
if (strchr(message, '\r') != NULL)
|
|
rc = fwrite("\n", 1, 1, log_opts.internal);
|
|
(void)rc;
|
|
fflush(log_opts.internal);
|
|
}
|
|
|
|
switch (type) {
|
|
case PG_VERBOSE:
|
|
if (log_opts.verbose)
|
|
printf("%s %s", current_localtime, _(message));
|
|
break;
|
|
|
|
case PG_REPORT:
|
|
case PG_WARNING:
|
|
printf("%s %s", current_localtime, _(message));
|
|
break;
|
|
|
|
case PG_FATAL:
|
|
printf("\n%s %s", current_localtime, _(message));
|
|
printf("%s Failure, exiting\n", current_localtime);
|
|
exit(1);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
fflush(stdout);
|
|
}
|
|
|
|
void check_ok(void)
|
|
{
|
|
/* all seems well */
|
|
report_status(PG_REPORT, "ok");
|
|
fflush(stdout);
|
|
}
|
|
|
|
/*
|
|
* quote_identifier()
|
|
* Properly double-quote a SQL identifier.
|
|
*
|
|
* The result should be pg_free'd, but most callers don't bother because
|
|
* memory leakage is not a big deal in this program.
|
|
*/
|
|
char* quote_identifier(const char* s)
|
|
{
|
|
char* result = (char*)pg_malloc(strlen(s) * 2 + 3);
|
|
char* r = result;
|
|
|
|
*r++ = '"';
|
|
while (*s) {
|
|
if (*s == '"')
|
|
*r++ = *s;
|
|
*r++ = *s;
|
|
s++;
|
|
}
|
|
*r++ = '"';
|
|
*r++ = '\0';
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* get_user_info()
|
|
* (copied from initdb.c) find the current user
|
|
*/
|
|
int get_user_info(char** user_name)
|
|
{
|
|
int user_id;
|
|
|
|
#ifndef WIN32
|
|
struct passwd* pw = getpwuid(geteuid());
|
|
|
|
user_id = geteuid();
|
|
#else /* the windows code */
|
|
struct passwd_win32 {
|
|
int pw_uid;
|
|
char pw_name[128];
|
|
} pass_win32;
|
|
struct passwd_win32* pw = &pass_win32;
|
|
DWORD pwname_size = sizeof(pass_win32.pw_name) - 1;
|
|
|
|
GetUserName(pw->pw_name, &pwname_size);
|
|
|
|
user_id = 1;
|
|
#endif
|
|
|
|
*user_name = pg_strdup(pw->pw_name);
|
|
|
|
return user_id;
|
|
}
|
|
|
|
void* pg_malloc(size_t size)
|
|
{
|
|
void* p = NULL;
|
|
|
|
/* Avoid unportable behavior of malloc(0) */
|
|
if (size == 0)
|
|
size = 1;
|
|
p = malloc(size);
|
|
if (p == NULL)
|
|
pg_log(PG_FATAL, "%s: out of memory\n", os_info.progname);
|
|
return p;
|
|
}
|
|
|
|
void* pg_realloc(void* ptr, size_t size)
|
|
{
|
|
void* p = NULL;
|
|
|
|
/* Avoid unportable behavior of realloc(NULL, 0) */
|
|
if (ptr == NULL && size == 0)
|
|
size = 1;
|
|
p = realloc(ptr, size);
|
|
if (p == NULL)
|
|
pg_log(PG_FATAL, "%s: out of memory\n", os_info.progname);
|
|
return p;
|
|
}
|
|
|
|
void pg_free(void* ptr)
|
|
{
|
|
if (ptr != NULL)
|
|
free(ptr);
|
|
}
|
|
|
|
char* pg_strdup(const char* s)
|
|
{
|
|
char* result = strdup(s);
|
|
|
|
if (result == NULL)
|
|
pg_log(PG_FATAL, "%s: out of memory\n", os_info.progname);
|
|
|
|
return result;
|
|
}
|
|
|
|
#define MAX_P_READ_BUF 256
|
|
int g_max_commands_parallel = 0;
|
|
int g_cur_commands_parallel = 0;
|
|
|
|
typedef struct tag_pcommand {
|
|
FILE* pfp;
|
|
char readbuf[512];
|
|
int cur_buf_loc;
|
|
char* nodename;
|
|
char* instance_name;
|
|
char* commad_type;
|
|
int retvalue;
|
|
} PARALLEL_COMMAND_S;
|
|
|
|
PARALLEL_COMMAND_S* g_parallel_command_cxt = NULL;
|
|
|
|
void execute_popen_commands_parallel(char* cmd, char* nodename, char* instance_name)
|
|
{
|
|
PARALLEL_COMMAND_S* curr_cxt = NULL;
|
|
errno_t nRet = 0;
|
|
|
|
if (g_cur_commands_parallel >= g_max_commands_parallel) {
|
|
g_max_commands_parallel += 128;
|
|
if (NULL == g_parallel_command_cxt)
|
|
g_parallel_command_cxt =
|
|
(PARALLEL_COMMAND_S*)pg_malloc(g_max_commands_parallel * sizeof(PARALLEL_COMMAND_S));
|
|
else
|
|
g_parallel_command_cxt = (PARALLEL_COMMAND_S*)pg_realloc(
|
|
g_parallel_command_cxt, g_max_commands_parallel * sizeof(PARALLEL_COMMAND_S));
|
|
}
|
|
|
|
curr_cxt = &g_parallel_command_cxt[g_cur_commands_parallel];
|
|
g_cur_commands_parallel++;
|
|
|
|
curr_cxt->cur_buf_loc = 0;
|
|
nRet = memset_s(curr_cxt->readbuf, sizeof(curr_cxt->readbuf), '\0', sizeof(curr_cxt->readbuf));
|
|
securec_check_c(nRet, "\0", "\0");
|
|
curr_cxt->nodename = nodename ? pg_strdup(nodename) : NULL;
|
|
curr_cxt->instance_name = instance_name ? pg_strdup(instance_name) : NULL;
|
|
|
|
curr_cxt->pfp = popen(cmd, "r");
|
|
|
|
if (NULL != curr_cxt->pfp) {
|
|
int flags;
|
|
int fd = fileno(curr_cxt->pfp);
|
|
flags = fcntl(fd, F_GETFL, 0);
|
|
flags |= O_NONBLOCK;
|
|
fcntl(fd, F_SETFL, flags);
|
|
}
|
|
}
|
|
|
|
void SleepInMilliSec(uint32_t sleepMs)
|
|
{
|
|
struct timespec ts;
|
|
ts.tv_sec = (sleepMs - (sleepMs % 1000)) / 1000;
|
|
ts.tv_nsec = (sleepMs % 1000) * 1000;
|
|
|
|
(void)nanosleep(&ts, NULL);
|
|
}
|
|
|
|
void read_popen_output_parallel()
|
|
{
|
|
int idx;
|
|
bool read_pending = false;
|
|
char* fcmd = NULL;
|
|
PARALLEL_COMMAND_S* curr_cxt = NULL;
|
|
bool error_in_execution = false;
|
|
|
|
fcmd = (char*)pg_malloc(MAX_P_READ_BUF + 1);
|
|
memset_s(fcmd, MAX_P_READ_BUF + 1, '\0', MAX_P_READ_BUF + 1);
|
|
|
|
read_pending = true;
|
|
while (true == read_pending) {
|
|
read_pending = false;
|
|
for (idx = 0; idx < g_cur_commands_parallel; idx++) {
|
|
curr_cxt = g_parallel_command_cxt + idx;
|
|
|
|
if (NULL == curr_cxt->pfp) {
|
|
continue;
|
|
}
|
|
errno = 0;
|
|
if (fgets(fcmd, MAX_P_READ_BUF - 1, curr_cxt->pfp) != NULL) {
|
|
int len = strlen(fcmd);
|
|
int hasnewline = false;
|
|
|
|
read_pending = true;
|
|
if (len > 1 && fcmd[len - 1] == '\n') {
|
|
hasnewline = true;
|
|
} else if ((curr_cxt->cur_buf_loc + len + 1) < (int)sizeof(curr_cxt->readbuf)) {
|
|
(void)strncpy_s(curr_cxt->readbuf + curr_cxt->cur_buf_loc,
|
|
sizeof(curr_cxt->readbuf) - curr_cxt->cur_buf_loc,
|
|
fcmd,
|
|
len + 1);
|
|
|
|
curr_cxt->cur_buf_loc += len;
|
|
continue;
|
|
}
|
|
|
|
if (curr_cxt->nodename && curr_cxt->instance_name) {
|
|
pg_log(PG_REPORT,
|
|
"[%s] [%s] %s%s%s",
|
|
curr_cxt->nodename,
|
|
curr_cxt->instance_name,
|
|
curr_cxt->readbuf,
|
|
fcmd,
|
|
hasnewline ? "" : "\n");
|
|
} else if (curr_cxt->nodename) {
|
|
pg_log(
|
|
PG_REPORT, "[%s] %s%s%s", curr_cxt->nodename, curr_cxt->readbuf, fcmd, hasnewline ? "" : "\n");
|
|
} else if (curr_cxt->instance_name) {
|
|
pg_log(PG_REPORT,
|
|
"[%s] %s%s%s",
|
|
curr_cxt->instance_name,
|
|
curr_cxt->readbuf,
|
|
fcmd,
|
|
hasnewline ? "" : "\n");
|
|
} else {
|
|
pg_log(PG_REPORT, "%s%s%s", curr_cxt->readbuf, fcmd, hasnewline ? "" : "\n");
|
|
}
|
|
|
|
curr_cxt->readbuf[0] = '\0';
|
|
curr_cxt->cur_buf_loc = 0;
|
|
} else if (errno == EAGAIN) {
|
|
read_pending = true;
|
|
continue;
|
|
} else {
|
|
curr_cxt->retvalue = pclose(curr_cxt->pfp);
|
|
curr_cxt->pfp = NULL;
|
|
if (curr_cxt->retvalue != 0) {
|
|
error_in_execution = true;
|
|
pg_log(PG_WARNING,
|
|
"gs_upgrade failed for %s %s.\n",
|
|
curr_cxt->instance_name ? curr_cxt->instance_name : curr_cxt->nodename,
|
|
curr_cxt->instance_name ? "instance name" : "node name");
|
|
}
|
|
|
|
if (curr_cxt->nodename) {
|
|
free(curr_cxt->nodename);
|
|
curr_cxt->nodename = NULL;
|
|
}
|
|
|
|
if (curr_cxt->instance_name) {
|
|
free(curr_cxt->instance_name);
|
|
curr_cxt->instance_name = NULL;
|
|
}
|
|
if (curr_cxt->retvalue != 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (true == error_in_execution) {
|
|
break;
|
|
}
|
|
SleepInMilliSec(100);
|
|
}
|
|
|
|
if (error_in_execution) {
|
|
for (idx = 0; idx < g_cur_commands_parallel; idx++) {
|
|
curr_cxt = g_parallel_command_cxt + idx;
|
|
|
|
if (NULL == curr_cxt->pfp) {
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Use the OM tool to make an exception after the process and the removal of the remaining files
|
|
*/
|
|
if (curr_cxt->nodename)
|
|
free(curr_cxt->nodename);
|
|
curr_cxt->nodename = NULL;
|
|
if (curr_cxt->instance_name)
|
|
free(curr_cxt->instance_name);
|
|
curr_cxt->instance_name = NULL;
|
|
}
|
|
|
|
pg_log(PG_FATAL, "gs_upgrade failed, please check last warning message for more details.\n");
|
|
}
|
|
|
|
g_cur_commands_parallel = 0;
|
|
|
|
free(fcmd);
|
|
}
|
|
|
|
int execute_popen_command(char* cmd, char* nodename, char* instance_name)
|
|
{
|
|
FILE* pfp = NULL;
|
|
char* fcmd = NULL;
|
|
|
|
fcmd = (char*)pg_malloc(MAX_P_READ_BUF);
|
|
fcmd[MAX_P_READ_BUF - 1] = 0;
|
|
|
|
pfp = popen(cmd, "r");
|
|
|
|
while (fgets(fcmd, MAX_P_READ_BUF - 1, pfp) != NULL) {
|
|
if (nodename && instance_name)
|
|
pg_log(PG_REPORT, "[%s] [%s] %s", nodename, instance_name, fcmd);
|
|
else if (nodename)
|
|
pg_log(PG_REPORT, "[%s] %s", nodename, fcmd);
|
|
else
|
|
pg_log(PG_REPORT, "[%s] %s", instance_name, fcmd);
|
|
}
|
|
|
|
return pclose(pfp);
|
|
}
|
|
|
|
/*
|
|
* getErrorText()
|
|
*
|
|
* Returns the text of the error message for the given error number
|
|
*
|
|
* This feature is factored into a separate function because it is
|
|
* system-dependent.
|
|
*/
|
|
const char* getErrorText(int errNum)
|
|
{
|
|
#ifdef WIN32
|
|
_dosmaperr(GetLastError());
|
|
#endif
|
|
return pg_strdup(strerror(errNum));
|
|
}
|
|
|
|
/*
|
|
* str2uint()
|
|
*
|
|
* convert string to oid
|
|
*/
|
|
unsigned int str2uint(const char* str)
|
|
{
|
|
return strtoul(str, NULL, 10);
|
|
}
|
|
|
|
/*
|
|
* str2uint64()
|
|
*
|
|
* convert string to 64-bit unsigned int
|
|
*/
|
|
uint64 str2uint64(const char* str)
|
|
{
|
|
#ifdef _MSC_VER /* MSVC only */
|
|
return _strtoui64(str, NULL, 10);
|
|
#elif defined(HAVE_STRTOULL) && SIZEOF_LONG < 8
|
|
return strtoull(str, NULL, 10);
|
|
#else
|
|
return strtoul(str, NULL, 10);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* pg_putenv()
|
|
*
|
|
* This is like putenv(), but takes two arguments.
|
|
* It also does unsetenv() if val is NULL.
|
|
*/
|
|
void pg_putenv(const char* var, const char* val)
|
|
{
|
|
if (val) {
|
|
#ifndef WIN32
|
|
char* envstr = (char*)pg_malloc(strlen(var) + strlen(val) + 2);
|
|
int nRet = 0;
|
|
|
|
nRet = snprintf_s(envstr, strlen(var) + strlen(val) + 2, strlen(var) + strlen(val) + 1, "%s=%s", var, val);
|
|
securec_check_ss_c(nRet, "\0", "\0");
|
|
putenv(envstr);
|
|
|
|
/*
|
|
* Do not free envstr because it becomes part of the environment on
|
|
* some operating systems. See port/unsetenv.c::unsetenv.
|
|
*/
|
|
#else
|
|
SetEnvironmentVariableA(var, val);
|
|
#endif
|
|
} else {
|
|
#ifndef WIN32
|
|
unsetenv(var);
|
|
#else
|
|
SetEnvironmentVariableA(var, "");
|
|
#endif
|
|
}
|
|
}
|