Files
openGauss-server/src/bin/psql/input.cpp
2022-09-03 16:22:35 +08:00

319 lines
9.8 KiB
C++

/*
* psql - the PostgreSQL interactive terminal
*
* Copyright (c) 2000-2012, PostgreSQL Global Development Group
*
* src/bin/psql/input.c
*/
#include "settings.h"
#include "postgres_fe.h"
#include <fcntl.h>
#include <limits.h>
#include "input.h"
#include "tab-complete.h"
#include "common.h"
/* Runtime options for turning off readline and history */
/* (of course there is no runtime command for doing that :) */
#ifdef USE_READLINE
bool useReadline = false;
#ifdef ENABLE_UT
#define static
#endif
/*
* Preserve newlines in saved queries by mapping '\n' to NL_IN_HISTORY
*
* It is assumed NL_IN_HISTORY will never be entered by the user
* nor appear inside a multi-byte string. 0x00 is not properly
* handled by the readline routines so it can not be used
* for this purpose.
*/
#define NL_IN_HISTORY 0x01
#endif
/*
* gets_interactive()
*
* Gets a line of interactive input, using readline if desired.
* The result is a malloc'd string.
*
* Caller *must* have set up sigint_interrupt_jmp before calling.
*/
char* gets_interactive(const char* prompt)
{
#ifdef USE_READLINE
if (useReadline) {
char* result = NULL;
/* Enable SIGINT to longjmp to sigint_interrupt_jmp */
sigint_interrupt_enabled = true;
/* On some platforms, readline is declared as readline(char *) */
result = readline((char*)prompt);
/* Disable SIGINT again */
sigint_interrupt_enabled = false;
return result;
}
#endif
fputs(prompt, stdout);
(void)fflush(stdout);
return gets_fromFile(stdin);
}
/*
* Append the line to the history buffer, making sure there is a trailing '\n'
*/
void pg_append_history(const char* s, PQExpBuffer history_buf)
{
#ifdef USE_READLINE
if (useReadline && s != NULL) {
appendPQExpBufferStr(history_buf, s);
if (!s[0] || s[strlen(s) - 1] != '\n')
appendPQExpBufferChar(history_buf, '\n');
}
#endif
}
/*
* Emit accumulated history entry to readline's history mechanism,
* then reset the buffer to empty.
*
* Note: we write nothing if history_buf is empty, so extra calls to this
* function don't hurt. There must have been at least one line added by
* pg_append_history before we'll do anything.
*/
void pg_send_history(PQExpBuffer history_buf)
{
#ifdef USE_READLINE
static char* prev_hist = NULL;
char* s = history_buf->data;
int i;
/* Trim any trailing \n's (OK to scribble on history_buf) */
for (i = strlen(s) - 1; i >= 0 && s[i] == '\n'; i--)
;
s[(uint32)(i + 1)] = '\0';
if (useReadline && s[0]) {
if (((((unsigned int)pset.histcontrol) & hctl_ignorespace) && s[0] == ' ') ||
((((unsigned int)pset.histcontrol) & hctl_ignoredups) && (prev_hist != NULL) &&
strcmp(s, prev_hist) == 0)) {
/* Ignore this line as far as history is concerned */
} else {
/* Save each previous line for ignoredups processing */
if (prev_hist != NULL)
free(prev_hist);
prev_hist = pg_strdup(s);
/* And send it to readline */
if (!SensitiveStrCheck(s) && canAddHist) {
(void)add_history(s);
/* Count lines added to history for use later */
} else if (!canAddHist) {
canAddHist = true;
}
}
}
resetPQExpBuffer(history_buf);
#endif
}
/*
* gets_fromFile
*
* Gets a line of noninteractive input from a file (which could be stdin).
* The result is a malloc'd string, or NULL on EOF or input error.
*
* Caller *must* have set up sigint_interrupt_jmp before calling.
*
* Note: we re-use a static PQExpBuffer for each call. This is to avoid
* leaking memory if interrupted by SIGINT.
*/
char* gets_fromFile(FILE* source)
{
static PQExpBuffer buffer = NULL;
errno_t rc = 0;
if (buffer == NULL) /* first time through? */
buffer = createPQExpBuffer();
else
(void)resetPQExpBuffer(buffer);
for (;;) {
char* result = NULL;
/* Enable SIGINT to longjmp to sigint_interrupt_jmp */
sigint_interrupt_enabled = true;
/* Database Security: Data importing/dumping support AES128. */
/* Get some data */
if (pset.decryptInfo.encryptInclude == false) {
result = fgets(pset.decryptInfo.currLine, sizeof(pset.decryptInfo.currLine), source);
} else {
result = getLineFromAesEncryptFile(source, &pset.decryptInfo);
}
/* Disable SIGINT again */
sigint_interrupt_enabled = false;
/* EOF or error? */
if (result == NULL) {
if (ferror(source)) {
psql_error("could not read from input file: %s\n", strerror(errno));
return NULL;
}
break;
}
/*
* Data importing/dumping support AES128 through VPP SSL.
* when decrypt text is longer than MAX_DECRYPT_BUFF_LEN
* We use decryptBuff here.
*/
if (pset.decryptInfo.encryptInclude == true) {
appendPQExpBufferStr(buffer, (char*)pset.decryptInfo.decryptBuff);
free(pset.decryptInfo.decryptBuff);
pset.decryptInfo.decryptBuff = NULL;
} else {
appendPQExpBufferStr(buffer, pset.decryptInfo.currLine);
}
/* Clear password related memory to avoid leaks when core. */
if (pset.cur_cmd_interactive) {
rc = memset_s(
pset.decryptInfo.currLine, sizeof(pset.decryptInfo.currLine), 0, sizeof(pset.decryptInfo.currLine));
securec_check_c(rc, "\0", "\0");
}
if (PQExpBufferBroken(buffer)) {
psql_error("out of memory\n");
return NULL;
}
/* EOL? */
if (buffer->len > 0 && buffer->data[buffer->len - 1] == '\n') {
char* line = NULL;
buffer->data[buffer->len - 1] = '\0';
line = pg_strdup(buffer->data);
/* Clear password related memory to avoid leaks when core. */
if (pset.cur_cmd_interactive) {
rc = memset_s(buffer->data, strlen(buffer->data), 0, strlen(buffer->data));
securec_check_c(rc, "\0", "\0");
}
return line;
}
}
if (buffer->len > 0) { /* EOF after reading some bufferload(s) */
char* line = NULL;
line = pg_strdup(buffer->data);
/* Clear password related memory to avoid leaks when core. */
if (pset.cur_cmd_interactive) {
rc = memset_s(buffer->data, strlen(buffer->data), 0, strlen(buffer->data));
securec_check_c(rc, "\0", "\0");
}
return line;
}
/* EOF, so return null */
return NULL;
}
bool SensitiveStrCheck(const char* target)
{
int j = 0;
int target_len = (int)strlen(target);
char* target_copy = (char*)pg_malloc(target_len + 1);
errno_t rc;
rc = strncpy_s(target_copy, target_len + 1, target, target_len);
securec_check_c(rc, "\0", "\0");
target_copy[target_len] = '\0';
for (j = 0; j < target_len; j++) {
target_copy[j] = toupper(target_copy[j]);
}
if (strstr(target_copy, "PASSWORD") != NULL || strstr(target_copy, "IDENTIFIED") != NULL ||
strstr(target_copy, "GS_ENCRYPT_AES128") != NULL || strstr(target_copy, "GS_DECRYPT_AES128") != NULL ||
strstr(target_copy, "GS_ENCRYPT") != NULL || strstr(target_copy, "GS_DECRYPT") != NULL ||
strstr(target_copy, "AES_ENCRYPT") != NULL || strstr(target_copy, "AES_DECRYPT") != NULL ||
strstr(target_copy, "PG_CREATE_PHYSICAL_REPLICATION_SLOT_EXTERN") != NULL ||
strstr(target_copy, "SECRET_ACCESS_KEY") != NULL ||
strstr(target_copy, "SECRETKEY") != NULL || strstr(target_copy, "CREATE_CREDENTIAL") != NULL ||
strstr(target_copy, "ACCESS_KEY") != NULL || strstr(target_copy, "SECRET_ACCESS_KEY") != NULL) {
free(target_copy);
return TRUE;
} else {
free(target_copy);
return FALSE;
}
}
void setHistSize(const char* targetName, const char* targetValue, bool setToDefault)
{
#ifndef ENABLE_LLT
char* end = NULL;
long int result;
#define MAXHISTSIZE 500
#define DEFHISTSIZE 32
if (targetName == NULL) {
return;
}
if (strcmp(targetName, "HISTSIZE") == 0) {
if (!setToDefault) {
if (targetValue == NULL || strlen(targetValue) == 0) {
fprintf(stderr, "warning:\"HISTSIZE\" is not changed,because its value can not be null\n");
return;
} else {
errno = 0;
result = strtol(targetValue, &end, 0);
if ((errno == ERANGE && (result == LONG_MAX || result == LONG_MIN)) || (errno != 0 && result == 0)) {
fprintf(stderr, "warning:\"HISTSIZE\" is not changed,because its value overflows\n");
return;
}
if (*end || result < 0) {
fprintf(stderr, "warning:\"HISTSIZE\" is not changed,because its value must be positive integer\n");
return;
}
}
if (result > MAXHISTSIZE) {
fprintf(stderr, "warning:\"HISTSIZE\" is set to 500,because its value can not be greater than 500\n");
result = MAXHISTSIZE;
}
} else {
result = DEFHISTSIZE;
}
stifle_history((int)result);
}
#endif
}
/*
* Put any startup stuff related to input in here. It's good to maintain
* abstraction this way.
*
* The only "flag" right now is 1 for use readline & history.
*/
void initializeInput(int flags)
{
#ifdef USE_READLINE
flags &= useReadline;
if (flags & 1) {
/* these two things must be done in this order: */
initialize_readline();
rl_variable_bind ("enable-meta-key", "off");
rl_initialize();
}
#endif
}