Exemplo n.º 1
0
MODRET authfile_getgroups(cmd_rec *cmd) {
  struct passwd *pwd = NULL;
  struct group *grp = NULL;
  array_header *gids = NULL, *groups = NULL;
  char *name = cmd->argv[0];

  if (name == NULL) {
    return PR_DECLINED(cmd);
  }

  if (af_setpwent(cmd->tmp_pool) < 0) {
    return PR_DECLINED(cmd);
  }

  if (af_setgrent(cmd->tmp_pool) < 0) {
    return PR_DECLINED(cmd);
  }

  /* Check for NULLs */
  if (cmd->argv[1]) {
    gids = (array_header *) cmd->argv[1];
  }

  if (cmd->argv[2]) {
    groups = (array_header *) cmd->argv[2];
  }

  /* Retrieve the necessary info. */
  pwd = af_getpwnam(cmd->tmp_pool, name);
  if (pwd == NULL) {
    return PR_DECLINED(cmd);
  }

  /* Populate the first group ID and name. */
  if (gids) {
    *((gid_t *) push_array(gids)) = pwd->pw_gid;
  }

  if (groups &&
      (grp = af_getgrgid(cmd->tmp_pool, pwd->pw_gid)) != NULL) {
    *((char **) push_array(groups)) = pstrdup(session.pool, grp->gr_name);
  }

  (void) af_setgrent(cmd->tmp_pool);

  /* This is where things get slow, expensive, and ugly.  Loop through
   * everything, checking to make sure we haven't already added it.
   */
  while ((grp = af_getgrent(cmd->tmp_pool)) != NULL &&
      grp->gr_mem) {
    char **gr_mems = NULL;

    pr_signals_handle();

    /* Loop through each member name listed */
    for (gr_mems = grp->gr_mem; *gr_mems; gr_mems++) {

      /* If it matches the given username... */
      if (strcmp(*gr_mems, pwd->pw_name) == 0) {

        /* ...add the GID and name */
        if (gids) {
          *((gid_t *) push_array(gids)) = grp->gr_gid;
        }

        if (groups) {
          *((char **) push_array(groups)) = pstrdup(session.pool, grp->gr_name);
        }
      }
    }
  }

  if (gids && gids->nelts > 0) {
    return mod_create_data(cmd, (void *) &gids->nelts);

  } else if (groups && groups->nelts > 0) {
    return mod_create_data(cmd, (void *) &groups->nelts);
  }

  return PR_DECLINED(cmd);
}
Exemplo n.º 2
0
int main(int argc, char *argv[]) {
  unsigned char verbose = FALSE;
  char **respargv = NULL;
  const char *cmdopts = "hs:v";

  register int i = 0;
  char *socket_file = PR_RUN_DIR "/proftpd.sock";
  int sockfd = -1, optc = 0, status = 0, respargc = 0;
  unsigned int reqargc = 0;
  pool *ctl_pool = NULL;
  array_header *reqargv = NULL;

  /* Make sure we were called with at least one argument. */
  if (argc-1 < 1) {
    fprintf(stdout, "%s: missing required arguments\n", program);
    exit(1);
  }

  /* Set the POSIXLY_CORRECT environment variable, so that control handlers
   * can themselves have optional flags.
   */
  if (putenv("POSIXLY_CORRECT=1") < 0) {
    fprintf(stderr, "%s: unable to set POSIXLY_CORRECT: %s\n", program,
      strerror(errno));
    exit(1);
  }

  opterr = 0;
  while ((optc = getopt(argc, argv, cmdopts)) != -1) {
    switch (optc) {
      case 'h':
        usage();
        return 0;

      case 's':
        if (*optarg != '/') {
          fprintf(stderr, "%s: alternate socket path must be an absolute "
            "path\n", program);
          return 1;
        }

        socket_file = optarg;
        break;

      case 'v':
        verbose = TRUE;
        break;

      case '?':
        fprintf(stdout, "%s: unknown option: %c\n", program, (char) optopt);
        break;
    }
  }

  signal(SIGPIPE, sig_pipe);

  /* Allocate some memory for proftpd objects. */
  ctl_pool = make_sub_pool(NULL);

  reqargv = make_array(ctl_pool, 0, sizeof(char *));

  /* Process the command-line args into an array_header. */
  for (i = optind; i < argc; i++) {
    if (verbose)
      fprintf(stdout, "%s: adding \"%s\" to reqargv\n", program, argv[i]);
    *((char **) push_array(reqargv)) = pstrdup(ctl_pool, argv[i]);
    reqargc++;
  }

  /* Don't forget to NULL-terminate the array. */
  *((char **) push_array(reqargv)) = NULL;

  /* Open a connection to the socket maintained by mod_ctrls. */
  if (verbose)
    fprintf(stdout, "%s: contacting server using '%s'\n", program,
      socket_file);

  sockfd = pr_ctrls_connect(socket_file);
  if (sockfd < 0) {
    fprintf(stderr, "%s: error contacting server using '%s': %s\n", program,
      socket_file, strerror(errno));
    exit(1);
  }

  if (verbose)
    fprintf(stdout, "%s: sending control request\n", program);

  if (pr_ctrls_send_msg(sockfd, 0, reqargc, (char **) reqargv->elts) < 0) {
    fprintf(stderr, "%s: error sending request: %s\n", program,
      strerror(errno));
    exit(1);
  }

  /* Read and display the responses. */

  if (verbose)
    fprintf(stdout, "%s: receiving control response\n", program);

  /* Manually set errno to this value, so that if an error (like a segfault)
   * occurs, the error string displayed to the user is more indicative of
   * the cause of the lack of responses.
   */
  errno = EPERM;

  if ((respargc = pr_ctrls_recv_response(ctl_pool, sockfd, &status,
      &respargv)) < 0) {
    fprintf(stdout, "%s: error receiving response: %s\n", program,
      strerror(errno));
    exit(1);
  }

  if (respargv != NULL) {
    for (i = 0; i < respargc; i++)
      fprintf(stdout, "%s: %s\n", program, respargv[i]);

  } else
    fprintf(stdout, "%s: no response from server\n", program);

  destroy_pool(ctl_pool);
  ctl_pool = NULL;

  return 0;
}
Exemplo n.º 3
0
/*
 * Turn a Datum into jsonb, adding it to the result JsonbInState.
 *
 * tcategory and outfuncoid are from a previous call to json_categorize_type,
 * except that if is_null is true then they can be invalid.
 *
 * If key_scalar is true, the value is stored as a key, so insist
 * it's of an acceptable type, and force it to be a jbvString.
 */
static void
datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
			   JsonbTypeCategory tcategory, Oid outfuncoid,
			   bool key_scalar)
{
	char	   *outputstr;
	bool		numeric_error;
	JsonbValue	jb;
	bool		scalar_jsonb = false;

	check_stack_depth();

	/* Convert val to a JsonbValue in jb (in most cases) */
	if (is_null)
	{
		Assert(!key_scalar);
		jb.type = jbvNull;
	}
	else if (key_scalar &&
			 (tcategory == JSONBTYPE_ARRAY ||
			  tcategory == JSONBTYPE_COMPOSITE ||
			  tcategory == JSONBTYPE_JSON ||
			  tcategory == JSONBTYPE_JSONB ||
			  tcategory == JSONBTYPE_JSONCAST))
	{
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
		 errmsg("key value must be scalar, not array, composite, or json")));
	}
	else
	{
		if (tcategory == JSONBTYPE_JSONCAST)
			val = OidFunctionCall1(outfuncoid, val);

		switch (tcategory)
		{
			case JSONBTYPE_ARRAY:
				array_to_jsonb_internal(val, result);
				break;
			case JSONBTYPE_COMPOSITE:
				composite_to_jsonb(val, result);
				break;
			case JSONBTYPE_BOOL:
				if (key_scalar)
				{
					outputstr = DatumGetBool(val) ? "true" : "false";
					jb.type = jbvString;
					jb.val.string.len = strlen(outputstr);
					jb.val.string.val = outputstr;
				}
				else
				{
					jb.type = jbvBool;
					jb.val.boolean = DatumGetBool(val);
				}
				break;
			case JSONBTYPE_NUMERIC:
				outputstr = OidOutputFunctionCall(outfuncoid, val);
				if (key_scalar)
				{
					/* always quote keys */
					jb.type = jbvString;
					jb.val.string.len = strlen(outputstr);
					jb.val.string.val = outputstr;
				}
				else
				{
					/*
					 * Make it numeric if it's a valid JSON number, otherwise
					 * a string. Invalid numeric output will always have an
					 * 'N' or 'n' in it (I think).
					 */
					numeric_error = (strchr(outputstr, 'N') != NULL ||
									 strchr(outputstr, 'n') != NULL);
					if (!numeric_error)
					{
						jb.type = jbvNumeric;
						jb.val.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(outputstr), 0, -1));

						pfree(outputstr);
					}
					else
					{
						jb.type = jbvString;
						jb.val.string.len = strlen(outputstr);
						jb.val.string.val = outputstr;
					}
				}
				break;
			case JSONBTYPE_DATE:
				{
					DateADT		date;
					struct pg_tm tm;
					char		buf[MAXDATELEN + 1];

					date = DatumGetDateADT(val);
					/* Same as date_out(), but forcing DateStyle */
					if (DATE_NOT_FINITE(date))
						EncodeSpecialDate(date, buf);
					else
					{
						j2date(date + POSTGRES_EPOCH_JDATE,
							   &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
						EncodeDateOnly(&tm, USE_XSD_DATES, buf);
					}
					jb.type = jbvString;
					jb.val.string.len = strlen(buf);
					jb.val.string.val = pstrdup(buf);
				}
				break;
			case JSONBTYPE_TIMESTAMP:
				{
					Timestamp	timestamp;
					struct pg_tm tm;
					fsec_t		fsec;
					char		buf[MAXDATELEN + 1];

					timestamp = DatumGetTimestamp(val);
					/* Same as timestamp_out(), but forcing DateStyle */
					if (TIMESTAMP_NOT_FINITE(timestamp))
						EncodeSpecialTimestamp(timestamp, buf);
					else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
						EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
					else
						ereport(ERROR,
								(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
								 errmsg("timestamp out of range")));
					jb.type = jbvString;
					jb.val.string.len = strlen(buf);
					jb.val.string.val = pstrdup(buf);
				}
				break;
			case JSONBTYPE_TIMESTAMPTZ:
				{
					TimestampTz timestamp;
					struct pg_tm tm;
					int			tz;
					fsec_t		fsec;
					const char *tzn = NULL;
					char		buf[MAXDATELEN + 1];

					timestamp = DatumGetTimestampTz(val);
					/* Same as timestamptz_out(), but forcing DateStyle */
					if (TIMESTAMP_NOT_FINITE(timestamp))
						EncodeSpecialTimestamp(timestamp, buf);
					else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0)
						EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
					else
						ereport(ERROR,
								(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
								 errmsg("timestamp out of range")));
					jb.type = jbvString;
					jb.val.string.len = strlen(buf);
					jb.val.string.val = pstrdup(buf);
				}
				break;
			case JSONBTYPE_JSONCAST:
			case JSONBTYPE_JSON:
				{
					/* parse the json right into the existing result object */
					JsonLexContext *lex;
					JsonSemAction sem;
					text	   *json = DatumGetTextP(val);

					lex = makeJsonLexContext(json, true);

					memset(&sem, 0, sizeof(sem));

					sem.semstate = (void *) result;

					sem.object_start = jsonb_in_object_start;
					sem.array_start = jsonb_in_array_start;
					sem.object_end = jsonb_in_object_end;
					sem.array_end = jsonb_in_array_end;
					sem.scalar = jsonb_in_scalar;
					sem.object_field_start = jsonb_in_object_field_start;

					pg_parse_json(lex, &sem);

				}
				break;
			case JSONBTYPE_JSONB:
				{
					Jsonb	   *jsonb = DatumGetJsonb(val);
					JsonbIterator *it;

					it = JsonbIteratorInit(&jsonb->root);

					if (JB_ROOT_IS_SCALAR(jsonb))
					{
						(void) JsonbIteratorNext(&it, &jb, true);
						Assert(jb.type == jbvArray);
						(void) JsonbIteratorNext(&it, &jb, true);
						scalar_jsonb = true;
					}
					else
					{
						JsonbIteratorToken type;

						while ((type = JsonbIteratorNext(&it, &jb, false))
							   != WJB_DONE)
						{
							if (type == WJB_END_ARRAY || type == WJB_END_OBJECT ||
								type == WJB_BEGIN_ARRAY || type == WJB_BEGIN_OBJECT)
								result->res = pushJsonbValue(&result->parseState,
															 type, NULL);
							else
								result->res = pushJsonbValue(&result->parseState,
															 type, &jb);
						}
					}
				}
				break;
			default:
				outputstr = OidOutputFunctionCall(outfuncoid, val);
				jb.type = jbvString;
				jb.val.string.len = checkStringLen(strlen(outputstr));
				jb.val.string.val = outputstr;
				break;
		}
	}

	/* Now insert jb into result, unless we did it recursively */
	if (!is_null && !scalar_jsonb &&
		tcategory >= JSONBTYPE_JSON && tcategory <= JSONBTYPE_JSONCAST)
	{
		/* work has been done recursively */
		return;
	}
	else if (result->parseState == NULL)
	{
		/* single root scalar */
		JsonbValue	va;

		va.type = jbvArray;
		va.val.array.rawScalar = true;
		va.val.array.nElems = 1;

		result->res = pushJsonbValue(&result->parseState, WJB_BEGIN_ARRAY, &va);
		result->res = pushJsonbValue(&result->parseState, WJB_ELEM, &jb);
		result->res = pushJsonbValue(&result->parseState, WJB_END_ARRAY, NULL);
	}
	else
	{
		JsonbValue *o = &result->parseState->contVal;

		switch (o->type)
		{
			case jbvArray:
				result->res = pushJsonbValue(&result->parseState, WJB_ELEM, &jb);
				break;
			case jbvObject:
				result->res = pushJsonbValue(&result->parseState,
											 key_scalar ? WJB_KEY : WJB_VALUE,
											 &jb);
				break;
			default:
				elog(ERROR, "unexpected parent of nested structure");
		}
	}
}
/*
 * regoperout		- converts operator OID to "opr_name"
 */
Datum
regoperout(PG_FUNCTION_ARGS)
{
	Oid			oprid = PG_GETARG_OID(0);
	char	   *result;
	HeapTuple	opertup;

	if (oprid == InvalidOid)
	{
		result = pstrdup("0");
		PG_RETURN_CSTRING(result);
	}

	opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(oprid));

	if (HeapTupleIsValid(opertup))
	{
		Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup);
		char	   *oprname = NameStr(operform->oprname);

		/*
		 * In bootstrap mode, skip the fancy namespace stuff and just return
		 * the oper name.  (This path is only needed for debugging output
		 * anyway.)
		 */
		if (IsBootstrapProcessingMode())
			result = pstrdup(oprname);
		else
		{
			FuncCandidateList clist;

			/*
			 * Would this oper be found (uniquely!) by regoperin? If not,
			 * qualify it.
			 */
			clist = OpernameGetCandidates(list_make1(makeString(oprname)),
										  '\0');
			if (clist != NULL && clist->next == NULL &&
				clist->oid == oprid)
				result = pstrdup(oprname);
			else
			{
				const char *nspname;

				nspname = get_namespace_name(operform->oprnamespace);
				nspname = quote_identifier(nspname);
				result = (char *) palloc(strlen(nspname) + strlen(oprname) + 2);
				sprintf(result, "%s.%s", nspname, oprname);
			}
		}

		ReleaseSysCache(opertup);
	}
	else
	{
		/*
		 * If OID doesn't match any pg_operator entry, return it numerically
		 */
		result = (char *) palloc(NAMEDATALEN);
		snprintf(result, NAMEDATALEN, "%u", oprid);
	}

	PG_RETURN_CSTRING(result);
}
Exemplo n.º 5
0
/*
 * Connect to remote server using specified server and user mapping properties.
 */
static PGconn *
connect_pg_server(ForeignServer *server, UserMapping *user)
{
	PGconn	   *volatile conn = NULL;

	/*
	 * Use PG_TRY block to ensure closing connection on error.
	 */
	PG_TRY();
	{
		const char **keywords;
		const char **values;
		int			n;

		/*
		 * Construct connection params from generic options of ForeignServer
		 * and UserMapping.  (Some of them might not be libpq options, in
		 * which case we'll just waste a few array slots.)  Add 3 extra slots
		 * for fallback_application_name, client_encoding, end marker.
		 */
		n = list_length(server->options) + list_length(user->options) + 3;
		keywords = (const char **) palloc(n * sizeof(char *));
		values = (const char **) palloc(n * sizeof(char *));

		n = 0;
		n += ExtractConnectionOptions(server->options,
									  keywords + n, values + n);
		n += ExtractConnectionOptions(user->options,
									  keywords + n, values + n);

		/* Use "postgres_fdw" as fallback_application_name. */
		keywords[n] = "fallback_application_name";
		values[n] = "postgres_fdw";
		n++;

		/* Set client_encoding so that libpq can convert encoding properly. */
		keywords[n] = "client_encoding";
		values[n] = GetDatabaseEncodingName();
		n++;

		keywords[n] = values[n] = NULL;

		/* verify connection parameters and make connection */
		check_conn_params(keywords, values);

		conn = PQconnectdbParams(keywords, values, false);
		if (!conn || PQstatus(conn) != CONNECTION_OK)
		{
			char	   *connmessage;
			int			msglen;

			/* libpq typically appends a newline, strip that */
			connmessage = pstrdup(PQerrorMessage(conn));
			msglen = strlen(connmessage);
			if (msglen > 0 && connmessage[msglen - 1] == '\n')
				connmessage[msglen - 1] = '\0';
			ereport(ERROR,
			   (errcode(ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION),
				errmsg("could not connect to server \"%s\"",
					   server->servername),
				errdetail_internal("%s", connmessage)));
		}

		/*
		 * Check that non-superuser has used password to establish connection;
		 * otherwise, he's piggybacking on the postgres server's user
		 * identity. See also dblink_security_check() in contrib/dblink.
		 */
		if (!superuser() && !PQconnectionUsedPassword(conn))
			ereport(ERROR,
				  (errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
				   errmsg("password is required"),
				   errdetail("Non-superuser cannot connect if the server does not request a password."),
				   errhint("Target server's authentication method must be changed.")));

		/* Prepare new session for use */
		configure_remote_session(conn);

		pfree(keywords);
		pfree(values);
	}
	PG_CATCH();
	{
		/* Release PGconn data structure if we managed to create one */
		if (conn)
			PQfinish(conn);
		PG_RE_THROW();
	}
	PG_END_TRY();

	return conn;
}
Exemplo n.º 6
0
/*
 * sepgsql_avc_compute
 *
 * A fallback path, when cache mishit. It asks SELinux its access control
 * decision for the supplied pair of security context and object class.
 */
static avc_cache *
sepgsql_avc_compute(const char *scontext, const char *tcontext, uint16 tclass)
{
	char	   *ucontext = NULL;
	char	   *ncontext = NULL;
	MemoryContext oldctx;
	avc_cache  *cache;
	uint32		hash;
	int			index;
	struct av_decision avd;

	hash = sepgsql_avc_hash(scontext, tcontext, tclass);
	index = hash % AVC_NUM_SLOTS;

	/*
	 * Validation check of the supplied security context. Because it always
	 * invoke system-call, frequent check should be avoided. Unless security
	 * policy is reloaded, validation status shall be kept, so we also cache
	 * whether the supplied security context was valid, or not.
	 */
	if (security_check_context_raw((security_context_t) tcontext) != 0)
		ucontext = sepgsql_avc_unlabeled();

	/*
	 * Ask SELinux its access control decision
	 */
	if (!ucontext)
		sepgsql_compute_avd(scontext, tcontext, tclass, &avd);
	else
		sepgsql_compute_avd(scontext, ucontext, tclass, &avd);

	/*
	 * It also caches a security label to be switched when a client labeled as
	 * 'scontext' executes a procedure labeled as 'tcontext', not only access
	 * control decision on the procedure. The security label to be switched
	 * shall be computed uniquely on a pair of 'scontext' and 'tcontext',
	 * thus, it is reasonable to cache the new label on avc, and enables to
	 * reduce unnecessary system calls. It shall be referenced at
	 * sepgsql_needs_fmgr_hook to check whether the supplied function is a
	 * trusted procedure, or not.
	 */
	if (tclass == SEPG_CLASS_DB_PROCEDURE)
	{
		if (!ucontext)
			ncontext = sepgsql_compute_create(scontext, tcontext,
											  SEPG_CLASS_PROCESS, NULL);
		else
			ncontext = sepgsql_compute_create(scontext, ucontext,
											  SEPG_CLASS_PROCESS, NULL);
		if (strcmp(scontext, ncontext) == 0)
		{
			pfree(ncontext);
			ncontext = NULL;
		}
	}

	/*
	 * Set up an avc_cache object
	 */
	oldctx = MemoryContextSwitchTo(avc_mem_cxt);

	cache = palloc0(sizeof(avc_cache));

	cache->hash = hash;
	cache->scontext = pstrdup(scontext);
	cache->tcontext = pstrdup(tcontext);
	cache->tclass = tclass;

	cache->allowed = avd.allowed;
	cache->auditallow = avd.auditallow;
	cache->auditdeny = avd.auditdeny;
	cache->hot_cache = true;
	if (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE)
		cache->permissive = true;
	if (!ucontext)
		cache->tcontext_is_valid = true;
	if (ncontext)
		cache->ncontext = pstrdup(ncontext);

	avc_num_caches++;

	if (avc_num_caches > avc_threshold)
		sepgsql_avc_reclaim();

	avc_slots[index] = lcons(cache, avc_slots[index]);

	MemoryContextSwitchTo(oldctx);

	return cache;
}
Exemplo n.º 7
0
Arquivo: var.c Projeto: rogerhu/dd-wrt
int pr_var_set(pool *p, const char *name, const char *desc, int type,
    void *val, void *data, size_t datasz) {
  struct var *v;

  if (var_tab == NULL) {
    errno = EPERM;
    return -1;
  }

  if (p == NULL ||
      name == NULL ||
      val == NULL) {
    errno = EINVAL;
    return -1;
  }

  /* The length of the key must be greater than 3 characters (for "%{}"). */
  if (strlen(name) < 4) {
    errno = EINVAL;
    return -1;
  }

  /* Specifying data, but no length for that data, is an error. */
  if (data != NULL &&
      datasz == 0) {
    errno = EINVAL;
    return -1;
  }

  /* Specifying no data, but providing a non-zero length for that data, is an
   * error.
   */
  if (data == NULL &&
      datasz > 0) {
    errno = EINVAL;
    return -1;
  }

  /* Variable names MUST start with '%{', and end in '}'. */
  if (strncmp(name, "%{", 2) != 0 ||
      name[strlen(name)-1] != '}') {
    errno = EINVAL;
    return -1;
  }

  /* Remove any previously registered value for this name.  For names whose
   * values change rapidly (e.g. session.xfer.total_bytes), a callback
   * function should be used, rather than always setting the same name as an
   * update; using a callback avoids the memory consumption that setting does
   * (set always allocates a new struct var *).
   */
  (void) pr_var_delete(name);

  /* Note: if var_pool was used for allocating the struct var *, rather
   * than the given pool, then deleting an entry would not necessarily
   * lead to such memory consumption (assuming it would even be a problem).
   * However, if this was the case, then a churn counter would be needed,
   * and var_pool would need to be churned occasionally to limit memory
   * growth.
   */

  switch (type) {
    case PR_VAR_TYPE_STR:
      v = pcalloc(p, sizeof(struct var));

      if (desc)
        v->v_desc = (const char *) pstrdup(p, desc);
      v->v_type = PR_VAR_TYPE_STR; 
      v->v_val = pstrdup(p, (char *) val);
      v->v_datasz = strlen((char *) val);
      break;

    case PR_VAR_TYPE_FUNC:
      v = pcalloc(p, sizeof(struct var));

      if (desc)
        v->v_desc = (const char *) pstrdup(p, desc);
      v->v_type = PR_VAR_TYPE_FUNC; 
      v->v_val = val;

      if (data) {
        v->v_data = data;
        v->v_datasz = datasz;
      }

      break;

    default:
      errno = EINVAL;
      return -1;
  }

  return pr_table_add(var_tab, name, v, sizeof(struct var));
}
Exemplo n.º 8
0
/*
 * Create a table space
 *
 * Only superusers can create a tablespace. This seems a reasonable restriction
 * since we're determining the system layout and, anyway, we probably have
 * root if we're doing this kind of activity
 */
Oid
CreateTableSpace(CreateTableSpaceStmt *stmt)
{
#ifdef HAVE_SYMLINK
	Relation	rel;
	Datum		values[Natts_pg_tablespace];
	bool		nulls[Natts_pg_tablespace];
	HeapTuple	tuple;
	Oid			tablespaceoid;
	char	   *location;
	Oid			ownerId;

	/* Must be super user */
	if (!superuser())
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("permission denied to create tablespace \"%s\"",
						stmt->tablespacename),
				 errhint("Must be superuser to create a tablespace.")));

	/* However, the eventual owner of the tablespace need not be */
	if (stmt->owner)
		ownerId = get_role_oid(stmt->owner, false);
	else
		ownerId = GetUserId();

	/* Unix-ify the offered path, and strip any trailing slashes */
	location = pstrdup(stmt->location);
	canonicalize_path(location);

	/* disallow quotes, else CREATE DATABASE would be at risk */
	if (strchr(location, '\''))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_NAME),
				 errmsg("tablespace location cannot contain single quotes")));

	/*
	 * Allowing relative paths seems risky
	 *
	 * this also helps us ensure that location is not empty or whitespace
	 */
	if (!is_absolute_path(location))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
				 errmsg("tablespace location must be an absolute path")));

	/*
	 * Check that location isn't too long. Remember that we're going to append
	 * 'PG_XXX/<dboid>/<relid>.<nnn>'.	FYI, we never actually reference the
	 * whole path, but mkdir() uses the first two parts.
	 */
	if (strlen(location) + 1 + strlen(TABLESPACE_VERSION_DIRECTORY) + 1 +
		OIDCHARS + 1 + OIDCHARS + 1 + OIDCHARS > MAXPGPATH)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
				 errmsg("tablespace location \"%s\" is too long",
						location)));

	/*
	 * Disallow creation of tablespaces named "pg_xxx"; we reserve this
	 * namespace for system purposes.
	 */
	if (!allowSystemTableMods && IsReservedName(stmt->tablespacename))
		ereport(ERROR,
				(errcode(ERRCODE_RESERVED_NAME),
				 errmsg("unacceptable tablespace name \"%s\"",
						stmt->tablespacename),
		errdetail("The prefix \"pg_\" is reserved for system tablespaces.")));

	/*
	 * Check that there is no other tablespace by this name.  (The unique
	 * index would catch this anyway, but might as well give a friendlier
	 * message.)
	 */
	if (OidIsValid(get_tablespace_oid(stmt->tablespacename, true)))
		ereport(ERROR,
				(errcode(ERRCODE_DUPLICATE_OBJECT),
				 errmsg("tablespace \"%s\" already exists",
						stmt->tablespacename)));

	/*
	 * Insert tuple into pg_tablespace.  The purpose of doing this first is to
	 * lock the proposed tablename against other would-be creators. The
	 * insertion will roll back if we find problems below.
	 */
	rel = heap_open(TableSpaceRelationId, RowExclusiveLock);

	MemSet(nulls, false, sizeof(nulls));

	values[Anum_pg_tablespace_spcname - 1] =
		DirectFunctionCall1(namein, CStringGetDatum(stmt->tablespacename));
	values[Anum_pg_tablespace_spcowner - 1] =
		ObjectIdGetDatum(ownerId);
	nulls[Anum_pg_tablespace_spcacl - 1] = true;
	nulls[Anum_pg_tablespace_spcoptions - 1] = true;

	tuple = heap_form_tuple(rel->rd_att, values, nulls);

	tablespaceoid = simple_heap_insert(rel, tuple);

	CatalogUpdateIndexes(rel, tuple);

	heap_freetuple(tuple);

	/* Record dependency on owner */
	recordDependencyOnOwner(TableSpaceRelationId, tablespaceoid, ownerId);

	/* Post creation hook for new tablespace */
	InvokeObjectPostCreateHook(TableSpaceRelationId, tablespaceoid, 0);

	create_tablespace_directories(location, tablespaceoid);

	/* Record the filesystem change in XLOG */
	{
		xl_tblspc_create_rec xlrec;
		XLogRecData rdata[2];

		xlrec.ts_id = tablespaceoid;
		rdata[0].data = (char *) &xlrec;
		rdata[0].len = offsetof(xl_tblspc_create_rec, ts_path);
		rdata[0].buffer = InvalidBuffer;
		rdata[0].next = &(rdata[1]);

		rdata[1].data = (char *) location;
		rdata[1].len = strlen(location) + 1;
		rdata[1].buffer = InvalidBuffer;
		rdata[1].next = NULL;

		(void) XLogInsert(RM_TBLSPC_ID, XLOG_TBLSPC_CREATE, rdata);
	}

	/*
	 * Force synchronous commit, to minimize the window between creating the
	 * symlink on-disk and marking the transaction committed.  It's not great
	 * that there is any window at all, but definitely we don't want to make
	 * it larger than necessary.
	 */
	ForceSyncCommit();

	pfree(location);

	/* We keep the lock on pg_tablespace until commit */
	heap_close(rel, NoLock);
#else							/* !HAVE_SYMLINK */
	ereport(ERROR,
			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
			 errmsg("tablespaces are not supported on this platform")));
#endif   /* HAVE_SYMLINK */

	return tablespaceoid;
}
Exemplo n.º 9
0
/*
 * destroy_tablespace_directories
 *
 * Attempt to remove filesystem infrastructure for the tablespace.
 *
 * 'redo' indicates we are redoing a drop from XLOG; in that case we should
 * not throw an ERROR for problems, just LOG them.	The worst consequence of
 * not removing files here would be failure to release some disk space, which
 * does not justify throwing an error that would require manual intervention
 * to get the database running again.
 *
 * Returns TRUE if successful, FALSE if some subdirectory is not empty
 */
static bool
destroy_tablespace_directories(Oid tablespaceoid, bool redo)
{
	char	   *linkloc;
	char	   *linkloc_with_version_dir;
	DIR		   *dirdesc;
	struct dirent *de;
	char	   *subfile;
	struct stat st;

	linkloc_with_version_dir = psprintf("pg_tblspc/%u/%s", tablespaceoid,
			TABLESPACE_VERSION_DIRECTORY);

	/*
	 * Check if the tablespace still contains any files.  We try to rmdir each
	 * per-database directory we find in it.  rmdir failure implies there are
	 * still files in that subdirectory, so give up.  (We do not have to worry
	 * about undoing any already completed rmdirs, since the next attempt to
	 * use the tablespace from that database will simply recreate the
	 * subdirectory via TablespaceCreateDbspace.)
	 *
	 * Since we hold TablespaceCreateLock, no one else should be creating any
	 * fresh subdirectories in parallel. It is possible that new files are
	 * being created within subdirectories, though, so the rmdir call could
	 * fail.  Worst consequence is a less friendly error message.
	 *
	 * If redo is true then ENOENT is a likely outcome here, and we allow it
	 * to pass without comment.  In normal operation we still allow it, but
	 * with a warning.	This is because even though ProcessUtility disallows
	 * DROP TABLESPACE in a transaction block, it's possible that a previous
	 * DROP failed and rolled back after removing the tablespace directories
	 * and/or symlink.	We want to allow a new DROP attempt to succeed at
	 * removing the catalog entries (and symlink if still present), so we
	 * should not give a hard error here.
	 */
	dirdesc = AllocateDir(linkloc_with_version_dir);
	if (dirdesc == NULL)
	{
		if (errno == ENOENT)
		{
			if (!redo)
				ereport(WARNING,
						(errcode_for_file_access(),
						 errmsg("could not open directory \"%s\": %m",
								linkloc_with_version_dir)));
			/* The symlink might still exist, so go try to remove it */
			goto remove_symlink;
		}
		else if (redo)
		{
			/* in redo, just log other types of error */
			ereport(LOG,
					(errcode_for_file_access(),
					 errmsg("could not open directory \"%s\": %m",
							linkloc_with_version_dir)));
			pfree(linkloc_with_version_dir);
			return false;
		}
		/* else let ReadDir report the error */
	}

	while ((de = ReadDir(dirdesc, linkloc_with_version_dir)) != NULL)
	{
		if (strcmp(de->d_name, ".") == 0 ||
			strcmp(de->d_name, "..") == 0)
			continue;

		subfile = psprintf("%s/%s", linkloc_with_version_dir, de->d_name);

		/* This check is just to deliver a friendlier error message */
		if (!redo && !directory_is_empty(subfile))
		{
			FreeDir(dirdesc);
			pfree(subfile);
			pfree(linkloc_with_version_dir);
			return false;
		}

		/* remove empty directory */
		if (rmdir(subfile) < 0)
			ereport(redo ? LOG : ERROR,
					(errcode_for_file_access(),
					 errmsg("could not remove directory \"%s\": %m",
							subfile)));

		pfree(subfile);
	}

	FreeDir(dirdesc);

	/* remove version directory */
	if (rmdir(linkloc_with_version_dir) < 0)
	{
		ereport(redo ? LOG : ERROR,
				(errcode_for_file_access(),
				 errmsg("could not remove directory \"%s\": %m",
						linkloc_with_version_dir)));
		pfree(linkloc_with_version_dir);
		return false;
	}

	/*
	 * Try to remove the symlink.  We must however deal with the possibility
	 * that it's a directory instead of a symlink --- this could happen during
	 * WAL replay (see TablespaceCreateDbspace), and it is also the case on
	 * Windows where junction points lstat() as directories.
	 *
	 * Note: in the redo case, we'll return true if this final step fails;
	 * there's no point in retrying it.  Also, ENOENT should provoke no more
	 * than a warning.
	 */
remove_symlink:
	linkloc = pstrdup(linkloc_with_version_dir);
	get_parent_directory(linkloc);
	if (lstat(linkloc, &st) == 0 && S_ISDIR(st.st_mode))
	{
		if (rmdir(linkloc) < 0)
			ereport(redo ? LOG : ERROR,
					(errcode_for_file_access(),
					 errmsg("could not remove directory \"%s\": %m",
							linkloc)));
	}
	else
	{
		if (unlink(linkloc) < 0)
			ereport(redo ? LOG : (errno == ENOENT ? WARNING : ERROR),
					(errcode_for_file_access(),
					 errmsg("could not remove symbolic link \"%s\": %m",
							linkloc)));
	}

	pfree(linkloc_with_version_dir);
	pfree(linkloc);

	return true;
}
Exemplo n.º 10
0
/* check_hook: validate new temp_tablespaces */
bool
check_temp_tablespaces(char **newval, void **extra, GucSource source)
{
	char	   *rawname;
	List	   *namelist;

	/* Need a modifiable copy of string */
	rawname = pstrdup(*newval);

	/* Parse string into list of identifiers */
	if (!SplitIdentifierString(rawname, ',', &namelist))
	{
		/* syntax error in name list */
		GUC_check_errdetail("List syntax is invalid.");
		pfree(rawname);
		list_free(namelist);
		return false;
	}

	/*
	 * If we aren't inside a transaction, we cannot do database access so
	 * cannot verify the individual names.	Must accept the list on faith.
	 * Fortunately, there's then also no need to pass the data to fd.c.
	 */
	if (IsTransactionState())
	{
		temp_tablespaces_extra *myextra;
		Oid		   *tblSpcs;
		int			numSpcs;
		ListCell   *l;

		/* temporary workspace until we are done verifying the list */
		tblSpcs = (Oid *) palloc(list_length(namelist) * sizeof(Oid));
		numSpcs = 0;
		foreach(l, namelist)
		{
			char	   *curname = (char *) lfirst(l);
			Oid			curoid;
			AclResult	aclresult;

			/* Allow an empty string (signifying database default) */
			if (curname[0] == '\0')
			{
				tblSpcs[numSpcs++] = InvalidOid;
				continue;
			}

			/*
			 * In an interactive SET command, we ereport for bad info.  When
			 * source == PGC_S_TEST, don't throw a hard error for a
			 * nonexistent tablespace, only a NOTICE.  See comments in guc.h.
			 */
			curoid = get_tablespace_oid(curname, source <= PGC_S_TEST);
			if (curoid == InvalidOid)
			{
				if (source == PGC_S_TEST)
					ereport(NOTICE,
							(errcode(ERRCODE_UNDEFINED_OBJECT),
							 errmsg("tablespace \"%s\" does not exist",
									curname)));
				continue;
			}

			/*
			 * Allow explicit specification of database's default tablespace
			 * in temp_tablespaces without triggering permissions checks.
			 */
			if (curoid == MyDatabaseTableSpace)
			{
				tblSpcs[numSpcs++] = InvalidOid;
				continue;
			}

			/* Check permissions, similarly complaining only if interactive */
			aclresult = pg_tablespace_aclcheck(curoid, GetUserId(),
											   ACL_CREATE);
			if (aclresult != ACLCHECK_OK)
			{
				if (source >= PGC_S_INTERACTIVE)
					aclcheck_error(aclresult, ACL_KIND_TABLESPACE, curname);
				continue;
			}

			tblSpcs[numSpcs++] = curoid;
		}

		/* Now prepare an "extra" struct for assign_temp_tablespaces */
		myextra = malloc(offsetof(temp_tablespaces_extra, tblSpcs) +
						 numSpcs * sizeof(Oid));
		if (!myextra)
			return false;
		myextra->numSpcs = numSpcs;
		memcpy(myextra->tblSpcs, tblSpcs, numSpcs * sizeof(Oid));
		*extra = (void *) myextra;

		pfree(tblSpcs);
	}
Exemplo n.º 11
0
/*
 * Each database using a table space is isolated into its own name space
 * by a subdirectory named for the database OID.  On first creation of an
 * object in the tablespace, create the subdirectory.  If the subdirectory
 * already exists, fall through quietly.
 *
 * isRedo indicates that we are creating an object during WAL replay.
 * In this case we will cope with the possibility of the tablespace
 * directory not being there either --- this could happen if we are
 * replaying an operation on a table in a subsequently-dropped tablespace.
 * We handle this by making a directory in the place where the tablespace
 * symlink would normally be.  This isn't an exact replay of course, but
 * it's the best we can do given the available information.
 *
 * If tablespaces are not supported, we still need it in case we have to
 * re-create a database subdirectory (of $PGDATA/base) during WAL replay.
 */
void
TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo)
{
	struct stat st;
	char	   *dir;

	/*
	 * The global tablespace doesn't have per-database subdirectories, so
	 * nothing to do for it.
	 */
	if (spcNode == GLOBALTABLESPACE_OID)
		return;

	Assert(OidIsValid(spcNode));
	Assert(OidIsValid(dbNode));

	dir = GetDatabasePath(dbNode, spcNode);

	if (stat(dir, &st) < 0)
	{
		/* Directory does not exist? */
		if (errno == ENOENT)
		{
			/*
			 * Acquire TablespaceCreateLock to ensure that no DROP TABLESPACE
			 * or TablespaceCreateDbspace is running concurrently.
			 */
			LWLockAcquire(TablespaceCreateLock, LW_EXCLUSIVE);

			/*
			 * Recheck to see if someone created the directory while we were
			 * waiting for lock.
			 */
			if (stat(dir, &st) == 0 && S_ISDIR(st.st_mode))
			{
				/* Directory was created */
			}
			else
			{
				/* Directory creation failed? */
				if (mkdir(dir, S_IRWXU) < 0)
				{
					char	   *parentdir;

					/* Failure other than not exists or not in WAL replay? */
					if (errno != ENOENT || !isRedo)
						ereport(ERROR,
								(errcode_for_file_access(),
							  errmsg("could not create directory \"%s\": %m",
									 dir)));

					/*
					 * Parent directories are missing during WAL replay, so
					 * continue by creating simple parent directories rather
					 * than a symlink.
					 */

					/* create two parents up if not exist */
					parentdir = pstrdup(dir);
					get_parent_directory(parentdir);
					get_parent_directory(parentdir);
					/* Can't create parent and it doesn't already exist? */
					if (mkdir(parentdir, S_IRWXU) < 0 && errno != EEXIST)
						ereport(ERROR,
								(errcode_for_file_access(),
							  errmsg("could not create directory \"%s\": %m",
									 parentdir)));
					pfree(parentdir);

					/* create one parent up if not exist */
					parentdir = pstrdup(dir);
					get_parent_directory(parentdir);
					/* Can't create parent and it doesn't already exist? */
					if (mkdir(parentdir, S_IRWXU) < 0 && errno != EEXIST)
						ereport(ERROR,
								(errcode_for_file_access(),
							  errmsg("could not create directory \"%s\": %m",
									 parentdir)));
					pfree(parentdir);

					/* Create database directory */
					if (mkdir(dir, S_IRWXU) < 0)
						ereport(ERROR,
								(errcode_for_file_access(),
							  errmsg("could not create directory \"%s\": %m",
									 dir)));
				}
			}

			LWLockRelease(TablespaceCreateLock);
		}
		else
		{
			ereport(ERROR,
					(errcode_for_file_access(),
					 errmsg("could not stat directory \"%s\": %m", dir)));
		}
	}
	else
	{
		/* Is it not a directory? */
		if (!S_ISDIR(st.st_mode))
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("\"%s\" exists but is not a directory",
							dir)));
	}

	pfree(dir);
}
Exemplo n.º 12
0
Arquivo: cash.c Projeto: GisKook/Gis
/* cash_out()
 * Function to convert cash to a dollars and cents representation, using
 * the lc_monetary locale's formatting.
 */
Datum
cash_out(PG_FUNCTION_ARGS)
{
	Cash		value = PG_GETARG_CASH(0);
	char	   *result;
	char		buf[128];
	char	   *bufptr;
	bool		minus = false;
	int			digit_pos;
	int			points,
				mon_group;
	char		dsymbol;
	const char *ssymbol,
			   *csymbol,
			   *nsymbol;
	char		convention;
	struct lconv *lconvert = PGLC_localeconv();

	/* see comments about frac_digits in cash_in() */
	points = lconvert->frac_digits;
	if (points < 0 || points > 10)
		points = 2;				/* best guess in this case, I think */

	/*
	 * As with frac_digits, must apply a range check to mon_grouping to avoid
	 * being fooled by variant CHAR_MAX values.
	 */
	mon_group = *lconvert->mon_grouping;
	if (mon_group <= 0 || mon_group > 6)
		mon_group = 3;

	convention = lconvert->n_sign_posn;

	/* we restrict dsymbol to be a single byte, but not the other symbols */
	if (*lconvert->mon_decimal_point != '\0' &&
		lconvert->mon_decimal_point[1] == '\0')
		dsymbol = *lconvert->mon_decimal_point;
	else
		dsymbol = '.';
	if (*lconvert->mon_thousands_sep != '\0')
		ssymbol = lconvert->mon_thousands_sep;
	else						/* ssymbol should not equal dsymbol */
		ssymbol = (dsymbol != ',') ? "," : ".";
	csymbol = (*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$";
	nsymbol = (*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-";

	/* we work with positive amounts and add the minus sign at the end */
	if (value < 0)
	{
		minus = true;
		value = -value;
	}

	/* we build the result string right-to-left in buf[] */
	bufptr = buf + sizeof(buf) - 1;
	*bufptr = '\0';

	/*
	 * Generate digits till there are no non-zero digits left and we emitted
	 * at least one to the left of the decimal point.  digit_pos is the
	 * current digit position, with zero as the digit just left of the decimal
	 * point, increasing to the right.
	 */
	digit_pos = points;
	do
	{
		if (points && digit_pos == 0)
		{
			/* insert decimal point */
			*(--bufptr) = dsymbol;
		}
		else if (digit_pos < points && (digit_pos % mon_group) == 0)
		{
			/* insert thousands sep */
			bufptr -= strlen(ssymbol);
			memcpy(bufptr, ssymbol, strlen(ssymbol));
		}

		*(--bufptr) = ((uint64) value % 10) + '0';
		value = ((uint64) value) / 10;
		digit_pos--;
	} while (value || digit_pos >= 0);

	/* prepend csymbol */
	bufptr -= strlen(csymbol);
	memcpy(bufptr, csymbol, strlen(csymbol));

	/* see if we need to signify negative amount */
	if (minus)
	{
		result = palloc(strlen(bufptr) + strlen(nsymbol) + 3);

		/* Position code of 0 means use parens */
		if (convention == 0)
			sprintf(result, "(%s)", bufptr);
		else if (convention == 2)
			sprintf(result, "%s%s", bufptr, nsymbol);
		else
			sprintf(result, "%s%s", nsymbol, bufptr);
	}
	else
	{
		/* just emit what we have */
		result = pstrdup(bufptr);
	}

	PG_RETURN_CSTRING(result);
}
Exemplo n.º 13
0
/************************************************************************
 * NEWINPUT - A new input file is to be opened, saving the old.
 *
 * ARGUMENTS
 *	char *newname - the name of the file
 *
 * RETURNS  - none
 *
 * SIDE EFFECTS  
 *	- causes input stream to be switched
 *	- Linenumber is reset to 1
 *	- storage is allocated for the newname
 *	- Filename is set to the new name
 *
 * DESCRIPTION  
 *	The file is opened, and if successful, the current input stream is saved
 *	and the stream is switched to the new file. If the newname is NULL,
 *	then stdin is taken as the new input.
 *
 * AUTHOR - Ralph Ryan, Sept. 9, 1982
 *
 * MODIFICATIONS - none
 *
 ************************************************************************/
int newinput (char *newname, int m_open)
{
    filelist_t *pF;
    TEXT_TYPE	p;
    WCHAR	*pwch;

    if( newname == NULL ) {
	Fp = stdin;
    }
    else if((Fp = fopen(newname, "rb")) == NULL)
    {
	if(m_open == MUST_OPEN) {
	    Msg_Temp = GET_MSG (1013);
	    SET_MSG (Msg_Text, Msg_Temp, newname);
	    fatal(1013);
	}
	return(FALSE);
    }

    /* now push it onto the file stack */
    ++Findex;
    if(Findex >= LIMIT_NESTED_INCLUDES) {
	Msg_Temp = GET_MSG (1014);
	SET_MSG (Msg_Text, Msg_Temp);
	fatal(1014);
    }
    pF = &Fstack[Findex];
    if(Findex == 0) {
	p = &InputBuffer[(IO_BLOCK * 0) + PUSHBACK_BYTES];
	pwch = &wchInputBuffer[(IO_BLOCK * 0) + PUSHBACK_BYTES];
	pF->fl_bufsiz = SIX_K;
    }
    else {
	filelist_t	*pPrevF;

	pPrevF = pF - 1;
	if(Findex == 1) {			/* first level include */
	    p = &InputBuffer[(IO_BLOCK * 1) + PUSHBACK_BYTES];
	    pwch = &wchInputBuffer[(IO_BLOCK * 1) + PUSHBACK_BYTES];
	    pF->fl_bufsiz = FOUR_K;
	}
	else {		/* (Findex > 1) */
	    /* nested includes . . . */
	    p = &InputBuffer[(IO_BLOCK * 2) + PUSHBACK_BYTES];
	    pwch = &wchInputBuffer[(IO_BLOCK * 2) + PUSHBACK_BYTES];
	    pF->fl_bufsiz = TWO_K;
	}
	if((pPrevF->fl_numread > TWO_K) || (Findex > 2)) {
	    /*
		**  the parent file has read something into the upper section
		**  or this is a nested include at least 3 deep.
		**  the child will overwrite some parent info. we must take this
		**  into account for the parent to reread when the time comes.
		**  we also must stick in the eos char into the parents buffer.
		**  (this latter is the useless thing in deeply nested
		**  includes since we overwrite the thing we just put in. we'll
		**  handle this later when we fpop the child.)
		*/
	    TEXT_TYPE	pCurrC;
	    long		seek_posn;

	    seek_posn = pPrevF->fl_totalread;
	    if( Macro_depth != 0 ) {
		/*
		**  in a macro, the 'current char' we want is kept as the
		**  first thing in the macro structure.
		*/
		pCurrC = (TEXT_TYPE)Macro_expansion[1].exp_string;
	    }
	    else {
		pCurrC = (TEXT_TYPE)Current_char;
	    }
	    if(pCurrC >= p) {
		/*
		**  p is the start of the child section.
		**  current char is past it. ie, we've already read some
		**  from the upper section.
		**  current char - p = # of characters used in upper section.
		**  numread = 0 implies there are no chars left from the parent.
		**  since, this is really the 'end' of the parent's buffer,
		**  we'll have to update the info so that the next read from the
		**  parent (after the child is finished) will be the terminator
		**  and we want the io_eob handler to refill the buffer.
		**  we reset the parent's cur char ptr to the beginning of its
		**  buffer, and put the terminator there.
		*/
		seek_posn += (pCurrC - pPrevF->fl_buffer);
		pPrevF->fl_totalread += (pCurrC - pPrevF->fl_buffer);
		pPrevF->fl_numread = 0;
		if( Macro_depth != 0 ) {
		    Macro_expansion[1].exp_string = pPrevF->fl_buffer;
		}
		else {
		    Current_char = pPrevF->fl_buffer;
		}
		*(pPrevF->fl_buffer) = EOS_CHAR;
		*(pPrevF->fl_pwchBuffer) = EOS_CHAR;
	    }
	    else {
		/*
		**  the upper section has not been read from yet,
		**  but it has been read into.
		**  'p' is pointing to the start of the child's buffer.
		**  we add the terminator to the new end of the parent's buffer.
		*/
		seek_posn += TWO_K;
		pPrevF->fl_numread = TWO_K;
		*(pPrevF->fl_buffer + TWO_K) = EOS_CHAR;
		*(pPrevF->fl_pwchBuffer + TWO_K) = EOS_CHAR;
	    }

	    if (pPrevF->fl_fFileType == DFT_FILE_IS_8_BIT) {
		fseek(pPrevF->fl_file, seek_posn, SEEK_SET);
	    
	    } else {

		fseek(pPrevF->fl_file, seek_posn * sizeof (WCHAR), SEEK_SET);
	    }
	}
    }
    pF->fl_currc = Current_char;/*  previous file's current char */
    pF->fl_lineno = Linenumber;	/*  previous file's line number  */
    pF->fl_file = Fp;			/*  the new file descriptor  */
    pF->fl_buffer = p;
    pF->fl_pwchBuffer = pwch;
    pF->fl_numread = 0;
    pF->fl_totalread = 0;

    //- Added to support 16-bit files.
    //- 8-2-91 David Marsyla.
    pF->fl_fFileType = DetermineFileType (Fp);

    //- The file type is unknown, warn them and then take a stab at an
    //- 8-bit file.  8-2-91 David Marsyla.
    if (pF->fl_fFileType == DFT_FILE_IS_UNKNOWN) {
	Msg_Temp = GET_MSG (4413);
	SET_MSG (Msg_Text, Msg_Temp, newname);
	warning (4413);
	pF->fl_fFileType = DFT_FILE_IS_8_BIT;
    }

    vfCurrFileType = pF->fl_fFileType;

    Current_char = (ptext_t)p;
    io_eob();					/*  fill the buffer  */
    /*
	* Note that include filenames will live the entire compiland. This
	* puts the burden on the user with MANY include files.  Any other
	* scheme takes space out of static data.
	* Notice also, that we save the previous filename in the new file's
	* fl_name.
	*/
    pF->fl_name = pstrdup(Filename);
    strncpy(Filebuff,newname,sizeof(Filebuff));
    Linenumber = 0;	/*  do_newline() will increment to the first line */
    if(Eflag) {
	emit_line();
	fwrite("\n", 1, 1, OUTPUTFILE);		/* this line is inserted */
    }
    do_newline();	/*  a new file may have preproc cmd as first line  */
    return(TRUE);
}
Exemplo n.º 14
0
/* usage: AuthGroupFile path [id <min-max>] [name <regex>] */
MODRET set_authgroupfile(cmd_rec *cmd) {
  config_rec *c = NULL;
  authfile_file_t *file = NULL;
  int flags = 0;
  char *path;

#ifdef PR_USE_REGEX
  if (cmd->argc-1 < 1 ||
      cmd->argc-1 > 5) {
#else
  if (cmd->argc-1 < 1 ||
      cmd->argc-1 > 2) {
#endif /* regex support */
    CONF_ERROR(cmd, "wrong number of parameters");
  }

  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);

  path = cmd->argv[1];
  if (*path != '/') {
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
      "unable to use relative path for ", (char *) cmd->argv[0], " '",
      path, "'.", NULL));
  }

  /* Make sure the configured file has the correct permissions.  Note that
   * AuthGroupFiles, unlike AuthUserFiles, do not contain any sensitive
   * information, and can thus be world-readable.
   */
  flags = PR_AUTH_FILE_FL_ALLOW_WORLD_READABLE;
  if (af_check_file(cmd->tmp_pool, cmd->argv[0], cmd->argv[1], flags) < 0) {
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
      "unable to use ", path, ": ", strerror(errno), NULL));
  }

  c = add_config_param(cmd->argv[0], 1, NULL);

  file = pcalloc(c->pool, sizeof(authfile_file_t));
  file->af_path = pstrdup(c->pool, path);
  c->argv[0] = (void *) file;

  /* Check for restrictions */
  if (cmd->argc-1 != 1) {
    register unsigned int i = 0;

    for (i = 2; i < cmd->argc; i++) {
      if (strncmp(cmd->argv[i], "id", 3) == 0) {
        gid_t min, max;
        char *sep = NULL, *tmp = NULL;

        /* The range restriction parameter is of the form "min-max", where max
         * must be >= min.
         */

        sep = strchr(cmd->argv[++i], '-');
        if (sep == NULL) {
          CONF_ERROR(cmd, "badly formatted ID restriction parameter");
        }

        *sep = '\0';

        min = strtol(cmd->argv[i], &tmp, 10);
        if (tmp && *tmp) {
          CONF_ERROR(cmd, "badly formatted minimum ID");
        }

        tmp = NULL;

        max = strtol(sep+1, &tmp, 10);
        if (tmp && *tmp) {
          CONF_ERROR(cmd, "badly formatted maximum ID");
        }

        if (min > max) {
          CONF_ERROR(cmd, "minimum cannot be larger than maximum");
        }

        file->af_min_id.gid = min;
        file->af_max_id.gid = max;
        file->af_restricted_ids = TRUE;

#ifdef PR_USE_REGEX
      } else if (strncmp(cmd->argv[i], "name", 5) == 0) {
        char *filter = cmd->argv[++i];
        pr_regex_t *pre = NULL;
        int res = 0;

        pre = pr_regexp_alloc(&auth_file_module);

        /* Check for a ! negation/inversion filter prefix. */
        if (*filter == '!') {
          filter++;
          file->af_name_regex_inverted = TRUE;
        }

        res = pr_regexp_compile(pre, filter, REG_EXTENDED|REG_NOSUB);
        if (res != 0) {
          char errstr[200] = {'\0'};

          pr_regexp_error(res, pre, errstr, sizeof(errstr));
          pr_regexp_free(NULL, pre);

          CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", filter, "' failed "
            "regex compilation: ", errstr, NULL));
        }

        file->af_name_filter = pstrdup(c->pool, cmd->argv[i]);
        file->af_name_regex = pre;
        file->af_restricted_names = TRUE;
#endif /* regex support */

      } else {
        CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown restriction '",
          cmd->argv[i], "'", NULL));
      }
    }
  }

  return PR_HANDLED(cmd);
}

/* usage: AuthUserFile path [home <regexp>] [id <min-max>] [name <regex>] */
MODRET set_authuserfile(cmd_rec *cmd) {
  config_rec *c = NULL;
  authfile_file_t *file = NULL;
  int flags = 0;
  char *path;

#ifdef PR_USE_REGEX
  if (cmd->argc-1 < 1 ||
      cmd->argc-1 > 7) {
#else
  if (cmd->argc-1 < 1 ||
      cmd->argc-1 > 2) {
#endif /* regex support */
    CONF_ERROR(cmd, "wrong number of parameters");
  }

  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);

  path = cmd->argv[1];
  if (*path != '/') {
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
      "unable to use relative path for ", (char *) cmd->argv[0], " '",
      path, "'.", NULL));
  }

  /* Make sure the configured file has the correct permissions.  Note that
   * AuthUserFiles, unlike AuthGroupFiles, DO contain any sensitive
   * information, and thus CANNOT be world-readable.
   */
  flags = 0;
  if (af_check_file(cmd->tmp_pool, cmd->argv[0], cmd->argv[1], flags) < 0) {
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
      "unable to use ", path, ": ", strerror(errno), NULL));
  }

  c = add_config_param(cmd->argv[0], 1, NULL);

  file = pcalloc(c->pool, sizeof(authfile_file_t));
  file->af_path = pstrdup(c->pool, path);
  c->argv[0] = (void *) file;

  /* Check for restrictions */
  if (cmd->argc-1 != 1) {
    register unsigned int i = 0;

    for (i = 2; i < cmd->argc; i++) {
      if (strncmp(cmd->argv[i], "id", 3) == 0) {
        uid_t min, max;
        char *sep = NULL, *tmp = NULL;

        /* The range restriction parameter is of the form "min-max", where max
         * must be >= min.
         */

        sep = strchr(cmd->argv[++i], '-');
        if (sep == NULL) {
          CONF_ERROR(cmd, "badly formatted ID restriction parameter");
        }

        *sep = '\0';

        min = strtol(cmd->argv[i], &tmp, 10);
        if (tmp && *tmp) {
          CONF_ERROR(cmd, "badly formatted minimum ID");
        }

        tmp = NULL;

        max = strtol(sep+1, &tmp, 10);

        if (tmp && *tmp) {
          CONF_ERROR(cmd, "badly formatted maximum ID");
        }

        if (min > max) {
          CONF_ERROR(cmd, "minimum cannot be larger than maximum");
        }

        file->af_min_id.uid = min;
        file->af_max_id.uid = max;
        file->af_restricted_ids = TRUE;

#ifdef PR_USE_REGEX
      } else if (strncmp(cmd->argv[i], "home", 5) == 0) {
        char *filter = cmd->argv[++i];
        pr_regex_t *pre = NULL;
        int res = 0;

        pre = pr_regexp_alloc(&auth_file_module);

        /* Check for a ! negation/inversion filter prefix. */
        if (*filter == '!') {
          filter++;
          file->af_home_regex_inverted = TRUE;
        }

        res = pr_regexp_compile(pre, filter, REG_EXTENDED|REG_NOSUB);
        if (res != 0) {
          char errstr[200] = {'\0'};

          pr_regexp_error(res, pre, errstr, sizeof(errstr));
          pr_regexp_free(NULL, pre);

          CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", filter, "' failed "
            "regex compilation: ", errstr, NULL));
        }

        file->af_home_filter = pstrdup(c->pool, cmd->argv[i]);
        file->af_home_regex = pre;
        file->af_restricted_homes = TRUE;

      } else if (strncmp(cmd->argv[i], "name", 5) == 0) {
        char *filter = cmd->argv[++i];
        pr_regex_t *pre = NULL;
        int res = 0;

        pre = pr_regexp_alloc(&auth_file_module);

        /* Check for a ! negation/inversion filter prefix. */
        if (*filter == '!') {
          filter++;
          file->af_name_regex_inverted = TRUE;
        }

        res = pr_regexp_compile(pre, filter, REG_EXTENDED|REG_NOSUB);
        if (res != 0) {
          char errstr[200] = {'\0'};

          pr_regexp_error(res, pre, errstr, sizeof(errstr));
          pr_regexp_free(NULL, pre);

          CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", filter, "' failed "
            "regex compilation: ", errstr, NULL));
        }

        file->af_name_filter = pstrdup(c->pool, cmd->argv[i]);
        file->af_name_regex = pre;
        file->af_restricted_names = TRUE;
#endif /* regex support */

      } else {
        CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown restriction '",
          cmd->argv[i], "'", NULL));
      }
    }
  }

  return PR_HANDLED(cmd);
}

/* Event listeners
 */

static void authfile_sess_reinit_ev(const void *event_data, void *user_data) {
  int res;

  /* A HOST command changed the main_server pointer, reinitialize ourselves. */

  pr_event_unregister(&auth_file_module, "core.session-reinit",
    authfile_sess_reinit_ev);

  af_user_file = NULL;
  af_group_file = NULL;

  res = authfile_sess_init();
  if (res < 0) {
    pr_session_disconnect(&auth_file_module,
      PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL);
  }
}
Exemplo n.º 15
0
int pr_event_register(module *m, const char *event,
    void (*cb)(const void *, void *), void *user_data) {
  register unsigned int i;
  struct event_handler *evh;
  struct event_list *evl;
  pool *evl_pool;
  unsigned long flags = 0;

  if (event == NULL ||
      cb == NULL) {
    errno = EINVAL;
    return -1;
  }

  if (event_pool == NULL) {
    event_pool = make_sub_pool(permanent_pool);
    pr_pool_tag(event_pool, "Event Pool");
  }

  pr_trace_msg(trace_channel, 3,
    "module '%s' (%p) registering handler for event '%s' (at %p)",
    m ? m->name : "(none)", m, event, cb);

  evh = pcalloc(event_pool, sizeof(struct event_handler));

  evh->module = m;
  evh->cb = cb;
  evh->user_data = user_data;

  /* Is this an untraced event? */
  for (i = 0; untraced_events[i] != NULL; i++) {
    if (strcmp(event, untraced_events[i]) == 0) {
      flags = PR_EVENT_FL_UNTRACED;
      break;
    }
  }

  evh->flags = flags;

  /* Scan the currently registered lists, looking for where to add this
   * registration.
   */

  for (evl = events; evl; evl = evl->next) {
    if (strncmp(evl->event, event, evl->event_len + 1) == 0) {
      struct event_handler *evhi, *evhl = NULL;

      evhi = evl->handlers;
      if (evhi) {
        /* Make sure this event handler is added to the START of the list,
         * in order to preserve module load order handling of events (i.e.
         * last module loaded, first module handled).  The exception to this
         * rule are core callbacks (i.e. where m == NULL); these will always
         * be invoked last.
         *
         * Before that, though, check for duplicate registration/subscription.
         */ 
        while (evhi) {
          pr_signals_handle();

          if (evhi->cb == evh->cb) {
            /* Duplicate callback */
            errno = EEXIST;
            return -1;
          }

          evhl = evhi;

          if (evhi->next == NULL) {
            break;
          }

          evhi = evhi->next;
        }

        if (evh->module != NULL) {
          if (evl->handlers->next != NULL) {
            evl->handlers->next->prev = evh;
          }

          evh->next = evl->handlers;
          evl->handlers = evh;

        } else {
          /* Core event listeners go at the end. */
          if (evhl != NULL) {
            evhl->next = evh;
            evh->prev = evhl;

          } else {
            evl->handlers = evh;
          }
        }

      } else {
        evl->handlers = evh;
      }

      /* All done */
      return 0;
    }
  }

  evl_pool = pr_pool_create_sz(event_pool, EVENT_POOL_SZ);
  pr_pool_tag(evl_pool, "Event listener list pool");

  evl = pcalloc(evl_pool, sizeof(struct event_list));
  evl->pool = evl_pool;
  evl->event = pstrdup(evl->pool, event);
  evl->event_len = strlen(evl->event);
  evl->handlers = evh; 
  evl->next = events;

  events = evl;

  /* Clear any cached data. */
  curr_event = NULL;
  curr_evl = NULL;
  curr_evh = NULL;

  return 0;
}
Exemplo n.º 16
0
static Tuplestorestate *
build_tuplestore_recursively(char *key_fld,
							 char *parent_key_fld,
							 char *relname,
							 char *orderby_fld,
							 char *branch_delim,
							 char *start_with,
							 char *branch,
							 int level,
							 int *serial,
							 int max_depth,
							 bool show_branch,
							 bool show_serial,
							 MemoryContext per_query_ctx,
							 AttInMetadata *attinmeta,
							 Tuplestorestate *tupstore)
{
	TupleDesc	tupdesc = attinmeta->tupdesc;
	int			ret;
	int			proc;
	int			serial_column;
	StringInfoData sql;
	char	  **values;
	char	   *current_key;
	char	   *current_key_parent;
	char		current_level[INT32_STRLEN];
	char		serial_str[INT32_STRLEN];
	char	   *current_branch;
	HeapTuple	tuple;

	if (max_depth > 0 && level > max_depth)
		return tupstore;

	initStringInfo(&sql);

	/* Build initial sql statement */
	if (!show_serial)
	{
		appendStringInfo(&sql, "SELECT %s, %s FROM %s WHERE %s = %s AND %s IS NOT NULL AND %s <> %s",
						 key_fld,
						 parent_key_fld,
						 relname,
						 parent_key_fld,
						 quote_literal_cstr(start_with),
						 key_fld, key_fld, parent_key_fld);
		serial_column = 0;
	}
	else
	{
		appendStringInfo(&sql, "SELECT %s, %s FROM %s WHERE %s = %s AND %s IS NOT NULL AND %s <> %s ORDER BY %s",
						 key_fld,
						 parent_key_fld,
						 relname,
						 parent_key_fld,
						 quote_literal_cstr(start_with),
						 key_fld, key_fld, parent_key_fld,
						 orderby_fld);
		serial_column = 1;
	}

	if (show_branch)
		values = (char **) palloc((CONNECTBY_NCOLS + serial_column) * sizeof(char *));
	else
		values = (char **) palloc((CONNECTBY_NCOLS_NOBRANCH + serial_column) * sizeof(char *));

	/* First time through, do a little setup */
	if (level == 0)
	{
		/* root value is the one we initially start with */
		values[0] = start_with;

		/* root value has no parent */
		values[1] = NULL;

		/* root level is 0 */
		sprintf(current_level, "%d", level);
		values[2] = current_level;

		/* root branch is just starting root value */
		if (show_branch)
			values[3] = start_with;

		/* root starts the serial with 1 */
		if (show_serial)
		{
			sprintf(serial_str, "%d", (*serial)++);
			if (show_branch)
				values[4] = serial_str;
			else
				values[3] = serial_str;
		}

		/* construct the tuple */
		tuple = BuildTupleFromCStrings(attinmeta, values);

		/* now store it */
		tuplestore_puttuple(tupstore, tuple);

		/* increment level */
		level++;
	}

	/* Retrieve the desired rows */
	ret = SPI_execute(sql.data, true, 0);
	proc = SPI_processed;

	/* Check for qualifying tuples */
	if ((ret == SPI_OK_SELECT) && (proc > 0))
	{
		HeapTuple	spi_tuple;
		SPITupleTable *tuptable = SPI_tuptable;
		TupleDesc	spi_tupdesc = tuptable->tupdesc;
		int			i;
		StringInfoData branchstr;
		StringInfoData chk_branchstr;
		StringInfoData chk_current_key;

		/* First time through, do a little more setup */
		if (level == 0)
		{
			/*
			 * Check that return tupdesc is compatible with the one we got
			 * from the query, but only at level 0 -- no need to check more
			 * than once
			 */

			if (!compatConnectbyTupleDescs(tupdesc, spi_tupdesc))
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("invalid return type"),
						 errdetail("Return and SQL tuple descriptions are " \
								   "incompatible.")));
		}

		initStringInfo(&branchstr);
		initStringInfo(&chk_branchstr);
		initStringInfo(&chk_current_key);

		for (i = 0; i < proc; i++)
		{
			/* initialize branch for this pass */
			appendStringInfo(&branchstr, "%s", branch);
			appendStringInfo(&chk_branchstr, "%s%s%s", branch_delim, branch, branch_delim);

			/* get the next sql result tuple */
			spi_tuple = tuptable->vals[i];

			/* get the current key and parent */
			current_key = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
			appendStringInfo(&chk_current_key, "%s%s%s", branch_delim, current_key, branch_delim);
			current_key_parent = pstrdup(SPI_getvalue(spi_tuple, spi_tupdesc, 2));

			/* get the current level */
			sprintf(current_level, "%d", level);

			/* check to see if this key is also an ancestor */
			if (strstr(chk_branchstr.data, chk_current_key.data))
				elog(ERROR, "infinite recursion detected");

			/* OK, extend the branch */
			appendStringInfo(&branchstr, "%s%s", branch_delim, current_key);
			current_branch = branchstr.data;

			/* build a tuple */
			values[0] = pstrdup(current_key);
			values[1] = current_key_parent;
			values[2] = current_level;
			if (show_branch)
				values[3] = current_branch;
			if (show_serial)
			{
				sprintf(serial_str, "%d", (*serial)++);
				if (show_branch)
					values[4] = serial_str;
				else
					values[3] = serial_str;
			}

			tuple = BuildTupleFromCStrings(attinmeta, values);

			xpfree(current_key);
			xpfree(current_key_parent);

			/* store the tuple for later use */
			tuplestore_puttuple(tupstore, tuple);

			heap_freetuple(tuple);

			/* recurse using current_key_parent as the new start_with */
			tupstore = build_tuplestore_recursively(key_fld,
													parent_key_fld,
													relname,
													orderby_fld,
													branch_delim,
													values[0],
													current_branch,
													level + 1,
													serial,
													max_depth,
													show_branch,
													show_serial,
													per_query_ctx,
													attinmeta,
													tupstore);

			/* reset branch for next pass */
			resetStringInfo(&branchstr);
			resetStringInfo(&chk_branchstr);
			resetStringInfo(&chk_current_key);
		}

		xpfree(branchstr.data);
		xpfree(chk_branchstr.data);
		xpfree(chk_current_key.data);
	}

	return tupstore;
}
Exemplo n.º 17
0
static int send_snmp_inform_or_trap(const GpErrorData * errorData, const char * subject, const char * severity)
{

	netsnmp_session session, *ss = NULL;
	netsnmp_pdu    *pdu, *response;
	int				status;
	char            csysuptime[20];
	static bool 	snmp_initialized = false;
	static char myhostname[255];	/* gethostname usually is limited to 65 chars out, but make this big to be safe */
	char	   *rawstring = NULL;
	List	   *elemlist = NIL;
	ListCell   *l = NULL;

	/*
	 * "inform" messages get a positive acknowledgement response from the SNMP manager.
	 * If it doesn't come, the message might be resent.
	 *
	 * "trap" messages are one-way, and we have no idea if the manager received it.
	 * But, it's faster and cheaper, and no need to retry.  So some people might prefer it.
	 */
	bool inform = strcmp(gp_snmp_use_inform_or_trap,"inform") == 0;


	if (gp_snmp_monitor_address == NULL || gp_snmp_monitor_address[0] == '\0')
	{
		static bool firsttime = 1;

		ereport(firsttime ? LOG : DEBUG1,(errmsg("SNMP inform/trap alerts are disabled")));
		firsttime = false;

		return -1;
	}


	/*
	 * SNMP managers are required to handle messages up to at least 484 bytes long, but I believe most existing
	 * managers support messages up to one packet (ethernet frame) in size, 1472 bytes.
	 *
	 * But, should we take that chance?  Or play it safe and limit the message to 484 bytes?
	 */

	elog(DEBUG2,"send_snmp_inform_or_trap");

	if (!snmp_initialized)
	{
		snmp_enable_stderrlog();

		if (gp_snmp_debug_log != NULL && gp_snmp_debug_log[0] != '\0')
		{
			snmp_enable_filelog(gp_snmp_debug_log, 1);

			//debug_register_tokens("ALL");
			snmp_set_do_debugging(1);
		}

		/*
		 * Initialize the SNMP library.  This also reads the MIB database.
		 */
		/* Add GPDB-MIB to the list to be loaded */
		putenv("MIBS=+GPDB-MIB:SNMP-FRAMEWORK-MIB:SNMPv2-CONF:SNMPv2-TC:SNMPv2-TC");

		init_snmp("sendalert");

		snmp_initialized = true;

		{
			char portnum[16];
			myhostname[0] = '\0';

			if (gethostname(myhostname, sizeof(myhostname)) == 0)
			{
				strcat(myhostname,":");
				pg_ltoa(PostPortNumber,portnum);
				strcat(myhostname,portnum);
			}
		}
	}

	/*
	 * Trap/Inform messages always start with the system up time. (SysUpTime.0)
	 *
	 * This presumably would be the uptime of GPDB, not the machine it is running on, I think.
	 *
	 * Use Postmaster's "MyStartTime" as a way to get that.
	 */

	sprintf(csysuptime, "%ld", (long)(time(NULL) - MyStartTime));


	/*
	// ERRCODE_DISK_FULL could be reported vi rbmsMIB rdbmsTraps rdbmsOutOfSpace trap.
	// But it appears we never generate that error?

	// ERRCODE_ADMIN_SHUTDOWN means SysAdmin aborted somebody's request.  Not interesting?

	// ERRCODE_CRASH_SHUTDOWN sounds interesting, but I don't see that we ever generate it.

	// ERRCODE_CANNOT_CONNECT_NOW means we are starting up, shutting down, in recovery, or Too many users are logged on.

	// abnormal database system shutdown
	*/



	/*
	 * The gpdbAlertSeverity is a crude attempt to classify some of these messages based on severity,
	 * where OK means everything is running normal, Down means everything is shut down, degraded would be
	 * for times when some segments are down, but the system is up, The others are maybe useful in the future
	 *
	 *  gpdbSevUnknown(0),
	 *	gpdbSevOk(1),
	 *	gpdbSevWarning(2),
	 *	gpdbSevError(3),
	 *	gpdbSevFatal(4),
	 *	gpdbSevPanic(5),
	 *	gpdbSevSystemDegraded(6),
	 *	gpdbSevSystemDown(7)
	 */


	char detail[MAX_ALERT_STRING+1];
	snprintf(detail, MAX_ALERT_STRING, "%s", errorData->error_detail);
	detail[127] = '\0';

	char sqlstmt[MAX_ALERT_STRING+1];
	char * sqlstmtp = errorData->debug_query_string;
	if (sqlstmtp == NULL || sqlstmtp[0] == '\0')
		sqlstmtp = errorData->internal_query;
	if (sqlstmtp == NULL)
		sqlstmtp = "";


	snprintf(sqlstmt, MAX_ALERT_STRING, "%s", sqlstmtp);
	sqlstmt[MAX_ALERT_STRING] = '\0';


	/* Need a modifiable copy of To list */
	rawstring = pstrdup(gp_snmp_monitor_address);

	/* Parse string into list of identifiers */
	if (!SplitMailString(rawstring, ',', &elemlist))
	{
		/* syntax error in list */
		ereport(LOG,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid list syntax for \"gp_snmp_monitor_address\"")));
		return -1;
	}


	/*
	 * This session is just a template, and doesn't need to be connected.
	 * It is used by snmp_add(), which copies this info, opens the new session, and assigns the transport.
	 */
	snmp_sess_init( &session );	/* Initialize session to default values */
	session.version = SNMP_VERSION_2c;
	session.timeout = SNMP_DEFAULT_TIMEOUT;
	session.retries = SNMP_DEFAULT_RETRIES;
	session.remote_port = 162; 							// I think this isn't used by net-snmp any more.

	/*if (strchr(session.peername,':')==NULL)
		strcat(session.peername,":162");*/
	session.community = (u_char *)gp_snmp_community;
	session.community_len = strlen((const char *)session.community);  // SNMP_DEFAULT_COMMUNITY_LEN means "public"
	session.callback_magic = NULL;

	foreach(l, elemlist)
	{
		char	   *cur_gp_snmp_monitor_address = (char *) lfirst(l);

		if (cur_gp_snmp_monitor_address == NULL || cur_gp_snmp_monitor_address[0] == '\0')
			continue;

		session.peername = cur_gp_snmp_monitor_address;
		/*
		 * If we try to "snmp_open( &session )", net-snmp will set up a connection to that
		 * endpoint on port 161, assuming we are the network monitor, and the other side is an agent.
		 *
		 * But we are pretending to be an agent, sending traps to the NM, so we don't need this.
		 */

		/*if (!snmp_open( &session ))   	// Don't open the session here!
		{
			const char     *str;
			int             xerr;
			xerr = snmp_errno;
			str = snmp_api_errstring(xerr);
			elog(LOG, "snmp_open: %s", str);
			return -1;
		}*/

		/*
		 * This call copies the info from "session" to "ss", assigns the transport, and opens the session.
		 * We must specify "snmptrap" so the transport will know we want port 162 by default.
		 */
		ss = snmp_add(&session,
					  netsnmp_transport_open_client("snmptrap", cur_gp_snmp_monitor_address),
					  NULL, NULL);
		if (ss == NULL) {
			/*
			 * diagnose netsnmp_transport_open_client and snmp_add errors with
			 * the input netsnmp_session pointer
			 */
			{
				char           *err;
				snmp_error(&session, NULL, NULL, &err);
				elog(LOG, "send_alert snmp_add of %s failed: %s", cur_gp_snmp_monitor_address, err);
				free(err);
			}
			return -1;
		}

		/*
		 * We need to create the pdu each time, as it gets freed when we send a trap.
		 */
		pdu = snmp_pdu_create(inform ? SNMP_MSG_INFORM : SNMP_MSG_TRAP2);
		if (!pdu)
		{
			const char     *str;
			int             xerr;
			xerr = snmp_errno;
			str = snmp_api_errstring(xerr);
			elog(LOG, "Failed to create notification PDU: %s", str);
			return -1;
		}

		/*
		 * Trap/Inform messages always start with the system up time. (SysUpTime.0)
		 * We use Postmaster's "MyStartTime" as a way to get that.
		 */

		snmp_add_var(pdu, objid_sysuptime,
							sizeof(objid_sysuptime) / sizeof(oid),
							't', (const char *)csysuptime);

	#if 0
		/*
		 * In the future, we might want to send RDBMS-MIB::rdbmsStateChange when the system becomes unavailable or
		 * partially unavailable.  This code, which is not currently used, shows how to build the pdu for
		 * that trap.
		 */
		/* {iso(1) identified-organization(3) dod(6) internet(1) mgmt(2) mib-2(1) rdbmsMIB(39) rdbmsTraps(2) rdbmsStateChange(1)} */
		snmp_add_var(pdu, objid_snmptrap,
							sizeof(objid_snmptrap) / sizeof(oid),
							'o', "1.3.6.1.2.1.39.2.1");  // rdbmsStateChange


		snmp_add_var(pdu, objid_rdbmsrelstate,
							sizeof(objid_rdbmsrelstate) / sizeof(oid),
							'i', "5");  // 4 = restricted, 5 = unavailable
	#endif

		/* {iso(1) identified-organization(3) dod(6) internet(1) private(4) enterprises(1) gpdbMIB(31327) gpdbTraps(5) gpdbTrapsList(0) gpdbAlert(1)} */

		/*
		 * We could specify this trap oid by name, rather than numeric oid, but then if the GPDB-MIB wasn't
		 * found, we'd get an error.  Using the numeric oid means we can still work without the MIB loaded.
		 */
		snmp_add_var(pdu, objid_snmptrap,
							sizeof(objid_snmptrap) / sizeof(oid),
							'o', "1.3.6.1.4.1.31327.5.0.1");  // gpdbAlert


		snmp_add_var(pdu, objid_gpdbAlertMsg,
							sizeof(objid_gpdbAlertMsg) / sizeof(oid),
							's', subject);  // SnmpAdminString = UTF-8 text
		snmp_add_var(pdu, objid_gpdbAlertSeverity,
							sizeof(objid_gpdbAlertSeverity) / sizeof(oid),
							'i', (char *)severity);

		snmp_add_var(pdu, objid_gpdbAlertSqlstate,
							sizeof(objid_gpdbAlertSqlstate) / sizeof(oid),
							's', errorData->sql_state);

		snmp_add_var(pdu, objid_gpdbAlertDetail,
							sizeof(objid_gpdbAlertDetail) / sizeof(oid),
							's', detail); // SnmpAdminString = UTF-8 text

		snmp_add_var(pdu, objid_gpdbAlertSqlStmt,
							sizeof(objid_gpdbAlertSqlStmt) / sizeof(oid),
							's', sqlstmt); // SnmpAdminString = UTF-8 text

		snmp_add_var(pdu, objid_gpdbAlertSystemName,
							sizeof(objid_gpdbAlertSystemName) / sizeof(oid),
							's', myhostname); // SnmpAdminString = UTF-8 text



		elog(DEBUG2,"ready to send to %s",cur_gp_snmp_monitor_address);
		if (inform)
			status = snmp_synch_response(ss, pdu, &response);
		else
			status = snmp_send(ss, pdu) == 0;

		elog(DEBUG2,"send, status %d",status);
		if (status != STAT_SUCCESS)
		{
			/* Something went wrong */
			if (ss)
			{
				char           *err;
				snmp_error(ss, NULL, NULL, &err);
				elog(LOG, "sendalert failed to send %s: %s", inform ? "inform" : "trap", err);
				free(err);
			}
			else
			{
				elog(LOG, "sendalert failed to send %s: %s", inform ? "inform" : "trap", "Something went wrong");
			}
			if (!inform)
				snmp_free_pdu(pdu);
		}
		else if (inform)
			snmp_free_pdu(response);

		snmp_close(ss);
		ss = NULL;

	}
Exemplo n.º 18
0
Datum
connectby_text(PG_FUNCTION_ARGS)
{
	char	   *relname = text_to_cstring(PG_GETARG_TEXT_PP(0));
	char	   *key_fld = text_to_cstring(PG_GETARG_TEXT_PP(1));
	char	   *parent_key_fld = text_to_cstring(PG_GETARG_TEXT_PP(2));
	char	   *start_with = text_to_cstring(PG_GETARG_TEXT_PP(3));
	int			max_depth = PG_GETARG_INT32(4);
	char	   *branch_delim = NULL;
	bool		show_branch = false;
	bool		show_serial = false;
	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
	TupleDesc	tupdesc;
	AttInMetadata *attinmeta;
	MemoryContext per_query_ctx;
	MemoryContext oldcontext;

	/* check to see if caller supports us returning a tuplestore */
	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("set-valued function called in context that cannot accept a set")));
	if (!(rsinfo->allowedModes & SFRM_Materialize) ||
		rsinfo->expectedDesc == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("materialize mode required, but it is not " \
						"allowed in this context")));

	if (fcinfo->nargs == 6)
	{
		branch_delim = text_to_cstring(PG_GETARG_TEXT_PP(5));
		show_branch = true;
	}
	else
		/* default is no show, tilde for the delimiter */
		branch_delim = pstrdup("~");

	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
	oldcontext = MemoryContextSwitchTo(per_query_ctx);

	/* get the requested return tuple description */
	tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);

	/* does it meet our needs */
	validateConnectbyTupleDesc(tupdesc, show_branch, show_serial);

	/* OK, use it then */
	attinmeta = TupleDescGetAttInMetadata(tupdesc);

	/* OK, go to work */
	rsinfo->returnMode = SFRM_Materialize;
	rsinfo->setResult = connectby(relname,
								  key_fld,
								  parent_key_fld,
								  NULL,
								  branch_delim,
								  start_with,
								  max_depth,
								  show_branch,
								  show_serial,
								  per_query_ctx,
							  rsinfo->allowedModes & SFRM_Materialize_Random,
								  attinmeta);
	rsinfo->setDesc = tupdesc;

	MemoryContextSwitchTo(oldcontext);

	/*
	 * SFRM_Materialize mode expects us to return a NULL Datum. The actual
	 * tuples are in our tuplestore and passed back through rsinfo->setResult.
	 * rsinfo->setDesc is set to the tuple description that we actually used
	 * to build our tuples with, so the caller can verify we did what it was
	 * expecting.
	 */
	return (Datum) 0;
}
Exemplo n.º 19
0
/*
 * PerformCursorOpen
 *		Execute SQL DECLARE CURSOR command.
 *
 * The query has already been through parse analysis, rewriting, and planning.
 * When it gets here, it looks like a SELECT PlannedStmt, except that the
 * utilityStmt field is set.
 */
void
PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
				  const char *queryString, bool isTopLevel)
{
	DeclareCursorStmt *cstmt = (DeclareCursorStmt *) stmt->utilityStmt;
	Portal		portal;
	MemoryContext oldContext;

	if (cstmt == NULL || !IsA(cstmt, DeclareCursorStmt))
		elog(ERROR, "PerformCursorOpen called for non-cursor query");

	/*
	 * Disallow empty-string cursor name (conflicts with protocol-level
	 * unnamed portal).
	 */
	if (!cstmt->portalname || cstmt->portalname[0] == '\0')
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_CURSOR_NAME),
				 errmsg("invalid cursor name: must not be empty")));

	/*
	 * If this is a non-holdable cursor, we require that this statement has
	 * been executed inside a transaction block (or else, it would have no
	 * user-visible effect).
	 */
	if (!(cstmt->options & CURSOR_OPT_HOLD))
		RequireTransactionChain(isTopLevel, "DECLARE CURSOR");

	/*
	 * Create a portal and copy the plan and queryString into its memory.
	 */
	portal = CreatePortal(cstmt->portalname, false, false);

	oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));

	stmt = copyObject(stmt);
	stmt->utilityStmt = NULL;	/* make it look like plain SELECT */

	queryString = pstrdup(queryString);

	PortalDefineQuery(portal,
					  NULL,
					  queryString,
					  "SELECT", /* cursor's query is always a SELECT */
					  list_make1(stmt),
					  NULL);

	/*----------
	 * Also copy the outer portal's parameter list into the inner portal's
	 * memory context.	We want to pass down the parameter values in case we
	 * had a command like
	 *		DECLARE c CURSOR FOR SELECT ... WHERE foo = $1
	 * This will have been parsed using the outer parameter set and the
	 * parameter value needs to be preserved for use when the cursor is
	 * executed.
	 *----------
	 */
	params = copyParamList(params);

	MemoryContextSwitchTo(oldContext);

	/*
	 * Set up options for portal.
	 *
	 * If the user didn't specify a SCROLL type, allow or disallow scrolling
	 * based on whether it would require any additional runtime overhead to do
	 * so.	Also, we disallow scrolling for FOR UPDATE cursors.
	 */
	portal->cursorOptions = cstmt->options;
	if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
	{
		if (stmt->rowMarks == NIL &&
			ExecSupportsBackwardScan(stmt->planTree))
			portal->cursorOptions |= CURSOR_OPT_SCROLL;
		else
			portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
	}

	/*
	 * Start execution, inserting parameters if any.
	 */
	PortalStart(portal, params, 0, GetActiveSnapshot());

	Assert(portal->strategy == PORTAL_ONE_SELECT);

	/*
	 * We're done; the query won't actually be run until PerformPortalFetch is
	 * called.
	 */
}
Exemplo n.º 20
0
/*
 * Extracts the error message from the full HTTP response
 * We test for several conditions in the http_ret_code and the HTTP response message.
 * The first condition that matches, defines the final message string and ends the function.
 * The layout of the HTTP response message is:
 
 <html>
 <head>
 <meta meta_data_attributes />
 <title> title_content_which_has_a_brief_description_of_the_error </title>
 </head>
 <body>
 <h2> heading_containing_the_error_code </h2>
 <p> 
 main_body_paragraph_with_a_detailed_description_of_the_error_on_the_rest_server_side
 <pre> the_error_in_original_format_not_HTML_ususally_the_title_of_the_java_exception</pre>
 </p>
 <h3>Caused by:</h3>
 <pre>
 the_full_java_exception_with_the_stack_output
 </pre>
 <hr /><i><small>Powered by Jetty://</small></i>
 <br/>                                               
 <br/>                                               								 
 </body>
 </html> 
 
 * Our first priority is to get the paragraph <p> inside <body>, and in case we don't find it, then we try to get
 * the <title>.
 */
char*
get_http_error_msg(long http_ret_code, char* msg, char* curl_error_buffer)
{
	char *start, *end, *ret;
	StringInfoData errMsg;
	initStringInfo(&errMsg);
	
	/* 
	 * 1. The server not listening on the port specified in the <create external...> statement" 
	 *    In this case there is no Response from the server, so we issue our own message
	 */

	if (http_ret_code == 0)
	{
		if (curl_error_buffer == NULL)
			return "There is no pxf servlet listening on the host and port specified in the external table url";
		else
		{
			return curl_error_buffer;
		}
	}
	
	/*
	 * 2. There is a response from the server since the http_ret_code is not 0, but there is no response message.
	 *    This is an abnormal situation that could be the result of a bug, libraries incompatibility or versioning issue
	 *    in the Rest server or our curl client. In this case we again issue our own message. 
	 */
	if (!msg || (msg && strlen(msg) == 0) )
	{
		appendStringInfo(&errMsg, "HTTP status code is %ld but HTTP response string is empty", http_ret_code);
		ret = pstrdup(errMsg.data);
		pfree(errMsg.data);
		return ret;
	}
	
	/*
	 * 3. The "normal" case - There is an HTTP response and the response has a <body> section inside where 
	 *    there is a paragraph contained by the <p> tag.
	 */
	start =  strstr(msg, "<body>");
	if (start != NULL)
	{
		start = strstr(start, "<p>");
		if (start != NULL)
		{
			char *tmp;
			bool skip = false;
			
			start += 3;
			end = strstr(start, "</p>");	/* assuming where is a <p>, there is a </p> */
			if (end != NULL)
			{
				/* Take one more line after the </p> */
				tmp = strchr(end, '\n');
				if (tmp != NULL)
					end = tmp;

				tmp = start;
				
				/*
				 * Right now we have the full paragraph inside the <body>. We need to extract from it
				 * the <pre> tags, the '\n' and the '\r'.
				 */
				while (tmp != end)
				{
					if (*tmp == '>') /* skipping the <pre> tags */
						skip = false;
					else if (*tmp == '<') /* skipping the <pre> tags */
					{
						skip = true;
						appendStringInfoChar(&errMsg, ' ');
					}
					else if (*tmp != '\n' && *tmp != '\r' && skip == false)
						appendStringInfoChar(&errMsg, *tmp);
					tmp++;
				}
				
				ret = pstrdup(errMsg.data);
				pfree(errMsg.data);
				return ret;
			}
		}
	}
	
 	/*
	 * 4. We did not find the <body>. So we try to print the <title>. 
	 */
	start = strstr(msg, "<title>");
	if (start != NULL)  
	{
		start += 7;
		/* no need to check if end is null, if <title> exists then also </title> exists */
		end = strstr(start, "</title>");
		if (end != NULL)
		{
			ret = pnstrdup(start, end - start);
			return ret;
		}
	}
	
	/*
	 * 5. This is an unexpected situation. We received an error message from the server but it does not have neither a <body>
	 *    nor a <title>. In this case we return the error message we received as-is.
	 */
	return msg;
}
Exemplo n.º 21
0
/*
 * lowerstr_with_len --- fold string to lower case
 *
 * Input string need not be null-terminated.
 *
 * Returned string is palloc'd
 */
char *
lowerstr_with_len(const char *str, int len)
{
	char	   *out;

	if (len == 0)
		return pstrdup("");

#ifdef USE_WIDE_UPPER_LOWER

	/*
	 * Use wide char code only when max encoding length > 1 and ctype != C.
	 * Some operating systems fail with multi-byte encodings and a C locale.
	 * Also, for a C locale there is no need to process as multibyte. From
	 * backend/utils/adt/oracle_compat.c Teodor
	 */
	if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c())
	{
		wchar_t    *wstr,
				   *wptr;
		int			wlen;

		/*
		 * alloc number of wchar_t for worst case, len contains number of
		 * bytes >= number of characters and alloc 1 wchar_t for 0, because
		 * wchar2char wants zero-terminated string
		 */
		wptr = wstr = (wchar_t *) palloc(sizeof(wchar_t) * (len + 1));

		wlen = char2wchar(wstr, len + 1, str, len);
		Assert(wlen <= len);

		while (*wptr)
		{
			*wptr = towlower((wint_t) *wptr);
			wptr++;
		}

		/*
		 * Alloc result string for worst case + '\0'
		 */
		len = pg_database_encoding_max_length() * wlen + 1;
		out = (char *) palloc(len);

		wlen = wchar2char(out, wstr, len);

		pfree(wstr);

		if (wlen < 0)
			ereport(ERROR,
					(errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE),
			errmsg("conversion from wchar_t to server encoding failed: %m")));
		Assert(wlen < len);
	}
	else
#endif   /* USE_WIDE_UPPER_LOWER */
	{
		const char *ptr = str;
		char	   *outptr;

		outptr = out = (char *) palloc(sizeof(char) * (len + 1));
		while ((ptr - str) < len && *ptr)
		{
			*outptr++ = tolower(TOUCHAR(ptr));
			ptr++;
		}
		*outptr = '\0';
	}

	return out;
}
Exemplo n.º 22
0
void
worker_spi_main(Datum main_arg)
{
	int			index = DatumGetInt32(main_arg);
	worktable  *table;
	StringInfoData buf;
	char		name[20];

	table = palloc(sizeof(worktable));
	sprintf(name, "schema%d", index);
	table->schema = pstrdup(name);
	table->name = pstrdup("counted");

	/* Establish signal handlers before unblocking signals. */
	pqsignal(SIGHUP, worker_spi_sighup);
	pqsignal(SIGTERM, worker_spi_sigterm);

	/* We're now ready to receive signals */
	BackgroundWorkerUnblockSignals();

	/* Connect to our database */
	BackgroundWorkerInitializeConnection("postgres", NULL);

	elog(LOG, "%s initialized with %s.%s",
		 MyBgworkerEntry->bgw_name, table->schema, table->name);
	initialize_worker_spi(table);

	/*
	 * Quote identifiers passed to us.  Note that this must be done after
	 * initialize_worker_spi, because that routine assumes the names are not
	 * quoted.
	 *
	 * Note some memory might be leaked here.
	 */
	table->schema = quote_identifier(table->schema);
	table->name = quote_identifier(table->name);

	initStringInfo(&buf);
	appendStringInfo(&buf,
					 "WITH deleted AS (DELETE "
					 "FROM %s.%s "
					 "WHERE type = 'delta' RETURNING value), "
					 "total AS (SELECT coalesce(sum(value), 0) as sum "
					 "FROM deleted) "
					 "UPDATE %s.%s "
					 "SET value = %s.value + total.sum "
					 "FROM total WHERE type = 'total' "
					 "RETURNING %s.value",
					 table->schema, table->name,
					 table->schema, table->name,
					 table->name,
					 table->name);

	/*
	 * Main loop: do this until the SIGTERM handler tells us to terminate
	 */
	while (!got_sigterm)
	{
		int			ret;
		int			rc;

		/*
		 * Background workers mustn't call usleep() or any direct equivalent:
		 * instead, they may wait on their process latch, which sleeps as
		 * necessary, but is awakened if postmaster dies.  That way the
		 * background process goes away immediately in an emergency.
		 */
		rc = WaitLatch(MyLatch,
					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
					   worker_spi_naptime * 1000L,
					   PG_WAIT_EXTENSION);
		ResetLatch(MyLatch);

		/* emergency bailout if postmaster has died */
		if (rc & WL_POSTMASTER_DEATH)
			proc_exit(1);

		/*
		 * In case of a SIGHUP, just reload the configuration.
		 */
		if (got_sighup)
		{
			got_sighup = false;
			ProcessConfigFile(PGC_SIGHUP);
		}

		/*
		 * Start a transaction on which we can run queries.  Note that each
		 * StartTransactionCommand() call should be preceded by a
		 * SetCurrentStatementStartTimestamp() call, which sets both the time
		 * for the statement we're about the run, and also the transaction
		 * start time.  Also, each other query sent to SPI should probably be
		 * preceded by SetCurrentStatementStartTimestamp(), so that statement
		 * start time is always up to date.
		 *
		 * The SPI_connect() call lets us run queries through the SPI manager,
		 * and the PushActiveSnapshot() call creates an "active" snapshot
		 * which is necessary for queries to have MVCC data to work on.
		 *
		 * The pgstat_report_activity() call makes our activity visible
		 * through the pgstat views.
		 */
		SetCurrentStatementStartTimestamp();
		StartTransactionCommand();
		SPI_connect();
		PushActiveSnapshot(GetTransactionSnapshot());
		pgstat_report_activity(STATE_RUNNING, buf.data);

		/* We can now execute queries via SPI */
		ret = SPI_execute(buf.data, false, 0);

		if (ret != SPI_OK_UPDATE_RETURNING)
			elog(FATAL, "cannot select from table %s.%s: error code %d",
				 table->schema, table->name, ret);

		if (SPI_processed > 0)
		{
			bool		isnull;
			int32		val;

			val = DatumGetInt32(SPI_getbinval(SPI_tuptable->vals[0],
											  SPI_tuptable->tupdesc,
											  1, &isnull));
			if (!isnull)
				elog(LOG, "%s: count in %s.%s is now %d",
					 MyBgworkerEntry->bgw_name,
					 table->schema, table->name, val);
		}

		/*
		 * And finish our transaction.
		 */
		SPI_finish();
		PopActiveSnapshot();
		CommitTransactionCommand();
		pgstat_report_stat(false);
		pgstat_report_activity(STATE_IDLE, NULL);
	}

	proc_exit(1);
}
Exemplo n.º 23
0
/*
 * Get any row security quals and check quals that should be applied to the
 * specified RTE.
 *
 * In addition, hasRowSecurity is set to true if row level security is enabled
 * (even if this RTE doesn't have any row security quals), and hasSubLinks is
 * set to true if any of the quals returned contain sublinks.
 */
void
get_row_security_policies(Query* root, CmdType commandType, RangeTblEntry* rte,
						  int rt_index, List **securityQuals,
						  List **withCheckOptions, bool *hasRowSecurity,
						  bool *hasSubLinks)
{
	Expr			   *rowsec_expr = NULL;
	Expr			   *rowsec_with_check_expr = NULL;
	Expr			   *hook_expr_restrictive = NULL;
	Expr			   *hook_with_check_expr_restrictive = NULL;
	Expr			   *hook_expr_permissive = NULL;
	Expr			   *hook_with_check_expr_permissive = NULL;

	List			   *rowsec_policies;
	List			   *hook_policies_restrictive = NIL;
	List			   *hook_policies_permissive = NIL;

	Relation 			rel;
	Oid					user_id;
	int					sec_context;
	int					rls_status;
	bool				defaultDeny = false;

	/* Defaults for the return values */
	*securityQuals = NIL;
	*withCheckOptions = NIL;
	*hasRowSecurity = false;
	*hasSubLinks = false;

	/* This is just to get the security context */
	GetUserIdAndSecContext(&user_id, &sec_context);

	/* Switch to checkAsUser if it's set */
	user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId();

	/*
	 * If this is not a normal relation, or we have been told
	 * to explicitly skip RLS (perhaps because this is an FK check)
	 * then just return immediately.
	 */
	if (rte->relid < FirstNormalObjectId
		|| rte->relkind != RELKIND_RELATION
		|| (sec_context & SECURITY_ROW_LEVEL_DISABLED))
		return;

	/* Determine the state of RLS for this, pass checkAsUser explicitly */
	rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false);

	/* If there is no RLS on this table at all, nothing to do */
	if (rls_status == RLS_NONE)
		return;

	/*
	 * RLS_NONE_ENV means we are not doing any RLS now, but that may change
	 * with changes to the environment, so we mark it as hasRowSecurity to
	 * force a re-plan when the environment changes.
	 */
	if (rls_status == RLS_NONE_ENV)
	{
		/*
		 * Indicate that this query may involve RLS and must therefore
		 * be replanned if the environment changes (GUCs, role), but we
		 * are not adding anything here.
		 */
		*hasRowSecurity = true;

		return;
	}

	/* Grab the built-in policies which should be applied to this relation. */
	rel = heap_open(rte->relid, NoLock);

	rowsec_policies = pull_row_security_policies(commandType, rel,
												 user_id);

	/*
	 * Check if this is only the default-deny policy.
	 *
	 * Normally, if the table has row security enabled but there are
	 * no policies, we use a default-deny policy and not allow anything.
	 * However, when an extension uses the hook to add their own
	 * policies, we don't want to include the default deny policy or
	 * there won't be any way for a user to use an extension exclusively
	 * for the policies to be used.
	 */
	if (((RowSecurityPolicy *) linitial(rowsec_policies))->policy_id
			== InvalidOid)
		defaultDeny = true;

	/* Now that we have our policies, build the expressions from them. */
	process_policies(root, rowsec_policies, rt_index, &rowsec_expr,
					 &rowsec_with_check_expr, hasSubLinks, OR_EXPR);

	/*
	 * Also, allow extensions to add their own policies.
	 *
	 * extensions can add either permissive or restrictive policies.
	 *
	 * Note that, as with the internal policies, if multiple policies are
	 * returned then they will be combined into a single expression with
	 * all of them OR'd (for permissive) or AND'd (for restrictive) together.
	 *
	 * If only a USING policy is returned by the extension then it will be
	 * used for WITH CHECK as well, similar to how internal policies are
	 * handled.
	 *
	 * The only caveat to this is that if there are NO internal policies
	 * defined, there ARE policies returned by the extension, and RLS is
	 * enabled on the table, then we will ignore the internally-generated
	 * default-deny policy and use only the policies returned by the
	 * extension.
	 */
	if (row_security_policy_hook_restrictive)
	{
		hook_policies_restrictive = (*row_security_policy_hook_restrictive)(commandType, rel);

		/* Build the expression from any policies returned. */
		if (hook_policies_restrictive != NIL)
			process_policies(root, hook_policies_restrictive, rt_index,
							 &hook_expr_restrictive,
							 &hook_with_check_expr_restrictive,
							 hasSubLinks,
							 AND_EXPR);
	}

	if (row_security_policy_hook_permissive)
	{
		hook_policies_permissive = (*row_security_policy_hook_permissive)(commandType, rel);

		/* Build the expression from any policies returned. */
		if (hook_policies_permissive != NIL)
			process_policies(root, hook_policies_permissive, rt_index,
							 &hook_expr_permissive,
							 &hook_with_check_expr_permissive, hasSubLinks,
							 OR_EXPR);
	}

	/*
	 * If the only built-in policy is the default-deny one, and hook
	 * policies exist, then use the hook policies only and do not apply
	 * the default-deny policy.  Otherwise, we will apply both sets below.
	 */
	if (defaultDeny &&
		(hook_policies_restrictive != NIL || hook_policies_permissive != NIL))
	{
		rowsec_expr = NULL;
		rowsec_with_check_expr = NULL;
	}

	/*
	 * For INSERT or UPDATE, we need to add the WITH CHECK quals to
	 * Query's withCheckOptions to verify that any new___ records pass the
	 * WITH CHECK policy (this will be a copy of the USING policy, if no
	 * explicit WITH CHECK policy exists).
	 */
	if (commandType == CMD_INSERT || commandType == CMD_UPDATE)
	{
		/*
		 * WITH CHECK OPTIONS wants a WCO node which wraps each Expr, so
		 * create them as necessary.
		 */

		/*
		 * Handle any restrictive policies first.
		 *
		 * They can simply be added.
		 */
		if (hook_with_check_expr_restrictive)
		{
			WithCheckOption	   *wco;

			wco = (WithCheckOption *) makeNode(WithCheckOption);
			wco->kind = commandType == CMD_INSERT ? WCO_RLS_INSERT_CHECK :
														  WCO_RLS_UPDATE_CHECK;
			wco->relname = pstrdup(RelationGetRelationName(rel));
			wco->qual = (Node *) hook_with_check_expr_restrictive;
			wco->cascaded = false;
			*withCheckOptions = lappend(*withCheckOptions, wco);
		}

		/*
		 * Handle built-in policies, if there are no permissive
		 * policies from the hook.
		 */
		if (rowsec_with_check_expr && !hook_with_check_expr_permissive)
		{
			WithCheckOption	   *wco;

			wco = (WithCheckOption *) makeNode(WithCheckOption);
			wco->kind = commandType == CMD_INSERT ? WCO_RLS_INSERT_CHECK :
														  WCO_RLS_UPDATE_CHECK;
			wco->relname = pstrdup(RelationGetRelationName(rel));
			wco->qual = (Node *) rowsec_with_check_expr;
			wco->cascaded = false;
			*withCheckOptions = lappend(*withCheckOptions, wco);
		}
		/* Handle the hook policies, if there are no built-in ones. */
		else if (!rowsec_with_check_expr && hook_with_check_expr_permissive)
		{
			WithCheckOption	   *wco;

			wco = (WithCheckOption *) makeNode(WithCheckOption);
			wco->kind = commandType == CMD_INSERT ? WCO_RLS_INSERT_CHECK :
														  WCO_RLS_UPDATE_CHECK;
			wco->relname = pstrdup(RelationGetRelationName(rel));
			wco->qual = (Node *) hook_with_check_expr_permissive;
			wco->cascaded = false;
			*withCheckOptions = lappend(*withCheckOptions, wco);
		}
		/* Handle the case where there are both. */
		else if (rowsec_with_check_expr && hook_with_check_expr_permissive)
		{
			WithCheckOption	   *wco;
			List			   *combined_quals = NIL;
			Expr			   *combined_qual_eval;

			combined_quals = lcons(copyObject(rowsec_with_check_expr),
								   combined_quals);

			combined_quals = lcons(copyObject(hook_with_check_expr_permissive),
								   combined_quals);

			combined_qual_eval = makeBoolExpr(OR_EXPR, combined_quals, -1);

			wco = (WithCheckOption *) makeNode(WithCheckOption);
			wco->kind = commandType == CMD_INSERT ? WCO_RLS_INSERT_CHECK :
														  WCO_RLS_UPDATE_CHECK;
			wco->relname = pstrdup(RelationGetRelationName(rel));
			wco->qual = (Node *) combined_qual_eval;
			wco->cascaded = false;
			*withCheckOptions = lappend(*withCheckOptions, wco);
		}

		/*
		 * ON CONFLICT DO UPDATE has an RTE that is subject to both INSERT and
		 * UPDATE RLS enforcement.  Those are enforced (as a special, distinct
		 * kind of WCO) on the target tuple.
		 *
		 * Make a second, recursive pass over the RTE for this, gathering
		 * UPDATE-applicable RLS checks/WCOs, and gathering and converting
		 * UPDATE-applicable security quals into WCO_RLS_CONFLICT_CHECK RLS
		 * checks/WCOs.  Finally, these distinct kinds of RLS checks/WCOs are
		 * concatenated with our own INSERT-applicable list.
		 */
		if (root->onConflict && root->onConflict->action == ONCONFLICT_UPDATE &&
			commandType == CMD_INSERT)
		{
			List	   *conflictSecurityQuals = NIL;
			List	   *conflictWCOs = NIL;
			ListCell   *item;
			bool		conflictHasRowSecurity = false;
			bool		conflictHasSublinks = false;

			/* Assume that RTE is target resultRelation */
			get_row_security_policies(root, CMD_UPDATE, rte, rt_index,
									  &conflictSecurityQuals, &conflictWCOs,
									  &conflictHasRowSecurity,
									  &conflictHasSublinks);

			if (conflictHasRowSecurity)
				*hasRowSecurity = true;
			if (conflictHasSublinks)
				*hasSubLinks = true;

			/*
			 * Append WITH CHECK OPTIONs/RLS checks, which should not conflict
			 * between this INSERT and the auxiliary UPDATE
			 */
			*withCheckOptions = list_concat(*withCheckOptions,
											conflictWCOs);

			foreach(item, conflictSecurityQuals)
			{
				Expr			   *conflict_rowsec_expr = (Expr *) lfirst(item);
				WithCheckOption	   *wco;

				wco = (WithCheckOption *) makeNode(WithCheckOption);

				wco->kind = WCO_RLS_CONFLICT_CHECK;
				wco->relname = pstrdup(RelationGetRelationName(rel));
				wco->qual = (Node *) copyObject(conflict_rowsec_expr);
				wco->cascaded = false;
				*withCheckOptions = lappend(*withCheckOptions, wco);
			}
		}
Exemplo n.º 24
0
config_rec *pr_parser_config_ctxt_open(const char *name) {
  config_rec *c = NULL, *parent = *parser_curr_config;
  pool *c_pool = NULL, *parent_pool = NULL;
  xaset_t **set = NULL;

  if (!name) {
    errno = EINVAL;
    return NULL;
  }

  if (parent) {
    parent_pool = parent->pool;
    set = &parent->subset;

  } else {
    parent_pool = (*parser_curr_server)->pool;
    set = &(*parser_curr_server)->conf;
  }

  /* Allocate a sub-pool for this config_rec.
   *
   * Note: special exception for <Global> configs: the parent pool is
   * 'global_config_pool' (a pool just for that context), not the pool of the
   * parent server.  This keeps <Global> config recs from being freed
   * prematurely, and helps to avoid memory leaks.
   */
  if (strncasecmp(name, "<Global>", 9) == 0) {
    if (!global_config_pool) {
      global_config_pool = make_sub_pool(permanent_pool);
      pr_pool_tag(global_config_pool, "<Global> Pool");
    }

    parent_pool = global_config_pool;
  }

  c_pool = make_sub_pool(parent_pool);
  pr_pool_tag(c_pool, "sub-config pool");

  c = (config_rec *) pcalloc(c_pool, sizeof(config_rec));

  if (!*set) {
    pool *set_pool = make_sub_pool(parent_pool);
    *set = xaset_create(set_pool, NULL);
    (*set)->pool = set_pool;
  }

  xaset_insert(*set, (xasetmember_t *) c);

  c->pool = c_pool;
  c->set = *set;
  c->parent = parent;
  c->name = pstrdup(c->pool, name);

  if (parent) {
    if (parent->config_type == CONF_DYNDIR)
      c->flags |= CF_DYNAMIC;
  }

  add_config_ctxt(c);
  return c;
}
Exemplo n.º 25
0
/* Figure out where a binary is installed
 * argvz: argv[0]
 * dir: Where to put the directory component
 * fil: Where to put the filename component
 * returns a pointer to dir or NULL for failure */
char *whereAmI(const char *argvz, char **dir, char **fil)
{
    char *workd, *path, *retname;
    char *pathelem[1024];
    int i, j, osl;
    struct stat sbuf;
    char *argvzd = pstrdup(argvz);
    if (!argvzd) { perror("strdup"); exit(1); }

#ifdef _WIN32
    for (i = 0; argvzd[i]; i++) {
        if (argvzd[i] == '\\') {
            argvzd[i] = '/';
        }
    }
#endif
    
    /* 1: full path, yippee! */
    if (argvzd[0] == '/') {
        dirAndFil(argvzd, dir, fil);
        return argvzd;
    }
    
#ifdef _WIN32
    /* 1.5: full path on Windows */
    if (argvzd[1] == ':' &&
        argvzd[2] == '/') {
        dirAndFil(argvzd, dir, fil);
        return argvzd;
    }
#endif
    
    /* 2: relative path */
    if (strchr(argvzd, '/')) {
        workd = (char *) malloc(1024 * sizeof(char));
        if (!workd) { perror("malloc"); exit(1); }
        
        if (getcwd(workd, 1024)) {
            retname = (char *) malloc((strlen(workd) + strlen(argvzd) + 2) * sizeof(char));
            if (!retname) { perror("malloc"); exit(1); }
            
            sprintf(retname, "%s/%s", workd, argvzd);
            free(workd);
            
            dirAndFil(retname, dir, fil);
            free(argvzd);
            return retname;
        }
    }
    
    /* 3: worst case: find in PATH */
    path = getenv("PATH");
    if (path == NULL) {
        return NULL;
    }
    path = pstrdup(path);
    
    /* tokenize by : */
    memset(pathelem, 0, 1024 * sizeof(char *));
    pathelem[0] = path;
    i = 1;
    osl = strlen(path);
    for (j = 0; j < osl; j++) {
        for (; path[j] != '\0' && path[j] != ':'; j++);
        
        if (path[j] == ':') {
            path[j] = '\0';
            
            j++;
            pathelem[i++] = path + j;
        }
    }
    
    /* go through every pathelem */
    for (i = 0; pathelem[i]; i++) {
        retname = (char *) malloc((strlen(pathelem[i]) + strlen(argvzd) + 2) * sizeof(char));
        if (!retname) { perror("malloc"); exit(1); }
        
        sprintf(retname, "%s/%s", pathelem[i], argvzd);
        
        if (stat(retname, &sbuf) == -1) {
            free(retname);
            continue;
        }

#ifdef S_IXUSR
        if (sbuf.st_mode & S_IXUSR)
#else
        if (1)
#endif
        {
            dirAndFil(retname, dir, fil);
            free(argvzd);
            return retname;
        }
        
        free(retname);
    }
    
    /* 4: can't find it */
    dir = NULL;
    fil = NULL;
    free(argvzd);
    return NULL;
}
Exemplo n.º 26
0
cmd_rec *pr_parser_parse_line(pool *p) {
  register unsigned int i;
  char buf[PR_TUNABLE_BUFFER_SIZE+1], *arg = "", *word = NULL;
  cmd_rec *cmd = NULL;
  pool *sub_pool = NULL;
  array_header *arr = NULL;

  if (p == NULL) {
    errno = EINVAL;
    return NULL;
  }

  memset(buf, '\0', sizeof(buf));
  
  while (pr_parser_read_line(buf, sizeof(buf)-1) != NULL) {
    char *bufp = buf;

    pr_signals_handle();

    /* Build a new pool for the command structure and array */
    sub_pool = make_sub_pool(p);
    pr_pool_tag(sub_pool, "parser cmd subpool");

    cmd = pcalloc(sub_pool, sizeof(cmd_rec));
    cmd->pool = sub_pool;
    cmd->stash_index = -1;
    cmd->stash_hash = 0;

    /* Add each word to the array */
    arr = make_array(cmd->pool, 4, sizeof(char **));
    while ((word = pr_str_get_word(&bufp, 0)) != NULL) {
      char *tmp;

      tmp = get_config_word(cmd->pool, word);

      *((char **) push_array(arr)) = tmp;
      cmd->argc++;
    }

    /* Terminate the array with a NULL. */
    *((char **) push_array(arr)) = NULL;

    /* The array header's job is done, we can forget about it and
     * it will get purged when the command's pool is destroyed.
     */

    cmd->argv = (char **) arr->elts;

    /* Perform a fixup on configuration directives so that:
     *
     *   -argv[0]--  -argv[1]-- ----argv[2]-----
     *   <Option     /etc/adir  /etc/anotherdir>
     *
     *  becomes:
     *
     *   -argv[0]--  -argv[1]-  ----argv[2]----
     *   <Option>    /etc/adir  /etc/anotherdir
     */

    if (cmd->argc &&
        *(cmd->argv[0]) == '<') {
      char *cp = cmd->argv[cmd->argc-1];

      if (*(cp + strlen(cp)-1) == '>' &&
          cmd->argc > 1) {

        if (strncmp(cp, ">", 2) == 0) {
          cmd->argv[cmd->argc-1] = NULL;
          cmd->argc--;

        } else {
          *(cp + strlen(cp)-1) = '\0';
        }

        cp = cmd->argv[0];
        if (*(cp + strlen(cp)-1) != '>') {
          cmd->argv[0] = pstrcat(cmd->pool, cp, ">", NULL);
        }
      }
    }

    if (cmd->argc < 2) {
      arg = pstrdup(cmd->pool, arg);
    }

    for (i = 1; i < cmd->argc; i++) {
      arg = pstrcat(cmd->pool, arg, *arg ? " " : "", cmd->argv[i], NULL);
    }

    cmd->arg = arg;
    return cmd;
  }

  return NULL;
}
Exemplo n.º 27
0
static void
compileTheSubstitute(DictThesaurus *d)
{
	int			i;

	for (i = 0; i < d->nsubst; i++)
	{
		TSLexeme   *rem = d->subst[i].res,
				   *outptr,
				   *inptr;
		int			n = 2;

		outptr = d->subst[i].res = (TSLexeme *) palloc(sizeof(TSLexeme) * n);
		outptr->lexeme = NULL;
		inptr = rem;

		while (inptr && inptr->lexeme)
		{
			TSLexeme   *lexized,
						tmplex[2];

			if (inptr->flags & DT_USEASIS)
			{					/* do not lexize */
				tmplex[0] = *inptr;
				tmplex[0].flags = 0;
				tmplex[1].lexeme = NULL;
				lexized = tmplex;
			}
			else
			{
				lexized = (TSLexeme *) DatumGetPointer(
													   FunctionCall4(
													   &(d->subdict->lexize),
									   PointerGetDatum(d->subdict->dictData),
											  PointerGetDatum(inptr->lexeme),
										Int32GetDatum(strlen(inptr->lexeme)),
														PointerGetDatum(NULL)
																	 )
					);
			}

			if (lexized && lexized->lexeme)
			{
				int			toset = (lexized->lexeme && outptr != d->subst[i].res) ? (outptr - d->subst[i].res) : -1;

				while (lexized->lexeme)
				{
					if (outptr - d->subst[i].res + 1 >= n)
					{
						int			diff = outptr - d->subst[i].res;

						n *= 2;
						d->subst[i].res = (TSLexeme *) repalloc(d->subst[i].res, sizeof(TSLexeme) * n);
						outptr = d->subst[i].res + diff;
					}

					*outptr = *lexized;
					outptr->lexeme = pstrdup(lexized->lexeme);

					outptr++;
					lexized++;
				}

				if (toset > 0)
					d->subst[i].res[toset].flags |= TSL_ADDPOS;
			}
			else if (lexized)
			{
				ereport(ERROR,
						(errcode(ERRCODE_CONFIG_FILE_ERROR),
						 errmsg("thesaurus substitute word \"%s\" is a stop word (rule %d)",
								inptr->lexeme, i + 1)));
			}
			else
			{
				ereport(ERROR,
						(errcode(ERRCODE_CONFIG_FILE_ERROR),
						 errmsg("thesaurus substitute word \"%s\" isn't recognized by subdictionary (rule %d)",
								inptr->lexeme, i + 1)));
			}

			if (inptr->lexeme)
				pfree(inptr->lexeme);
			inptr++;
		}

		if (outptr == d->subst[i].res)
			ereport(ERROR,
					(errcode(ERRCODE_CONFIG_FILE_ERROR),
					 errmsg("thesaurus substitute phrase is empty (rule %d)",
							i + 1)));

		d->subst[i].reslen = outptr - d->subst[i].res;

		pfree(rem);
	}
}
Exemplo n.º 28
0
static char *get_config_word(pool *p, char *word) {
  size_t wordlen;

  /* Should this word be replaced with a value from the environment?
   * If so, tmp will contain the expanded value, otherwise tmp will
   * contain a string duped from the given pool.
   */

  wordlen = strlen(word);

  if (wordlen > 7) {
    char *ptr = NULL;

    /* Does the given word use the environment syntax?
     *
     * In the simple (and most common) case, the entire word is the variable
     * syntax.  But we also need to check for cases where the environment
     * variable syntax is embedded within the word string.
     */

    if (strncmp(word, "%{env:", 6) == 0 &&
        word[wordlen-1] == '}') {
      char *env;

      word[wordlen-1] = '\0';

      env = pr_env_get(p, word + 6);

      return env ? pstrdup(p, env) : "";
    }

    /* This is in a while loop in order to handle a) multiple different
     * variables, and b) cases where the substituted value is itself a
     * variable.   (Hopefully no one is so clever as to want to actually
     * _use_ the latter approach.)
     */
    ptr = strstr(word, "%{env:");
    while (ptr != NULL) {
      char *env, *key, *ptr2, *var;
      unsigned int keylen;

      pr_signals_handle();

      ptr2 = strchr(ptr + 6, '}');
      if (ptr2 == NULL) {
        /* No terminating marker; continue on to the next potential
         * variable in the word.
         */
        ptr2 = ptr + 6;
        ptr = strstr(ptr2, "%{env:");
        continue;
      }

      keylen = (ptr2 - ptr - 6);
      var = pstrndup(p, ptr, (ptr2 - ptr) + 1);

      key = pstrndup(p, ptr + 6, keylen);

      env = pr_env_get(p, key);
      if (env == NULL) {
        /* No value in the environment; continue on to the next potential
         * variable in the word.
         */
        ptr = strstr(ptr2, "%{env:");
        continue;
      }

      word = sreplace(p, word, var, env, NULL);
      ptr = strstr(word, "%{env:");
    }
  }

  return pstrdup(p, word);
}
Exemplo n.º 29
0
/*
 * ExportSnapshot
 *		Export the snapshot to a file so that other backends can import it.
 *		Returns the token (the file name) that can be used to import this
 *		snapshot.
 */
char *
ExportSnapshot(Snapshot snapshot)
{
	TransactionId topXid;
	TransactionId *children;
	int			nchildren;
	int			addTopXid;
	StringInfoData buf;
	FILE	   *f;
	int			i;
	MemoryContext oldcxt;
	char		path[MAXPGPATH];
	char		pathtmp[MAXPGPATH];

	/*
	 * It's tempting to call RequireTransactionChain here, since it's not very
	 * useful to export a snapshot that will disappear immediately afterwards.
	 * However, we haven't got enough information to do that, since we don't
	 * know if we're at top level or not.  For example, we could be inside a
	 * plpgsql function that is going to fire off other transactions via
	 * dblink.  Rather than disallow perfectly legitimate usages, don't make a
	 * check.
	 *
	 * Also note that we don't make any restriction on the transaction's
	 * isolation level; however, importers must check the level if they are
	 * serializable.
	 */

	/*
	 * This will assign a transaction ID if we do not yet have one.
	 */
	topXid = GetTopTransactionId();

	/*
	 * We cannot export a snapshot from a subtransaction because there's no
	 * easy way for importers to verify that the same subtransaction is still
	 * running.
	 */
	if (IsSubTransaction())
		ereport(ERROR,
				(errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
				 errmsg("cannot export a snapshot from a subtransaction")));

	/*
	 * We do however allow previous committed subtransactions to exist.
	 * Importers of the snapshot must see them as still running, so get their
	 * XIDs to add them to the snapshot.
	 */
	nchildren = xactGetCommittedChildren(&children);

	/*
	 * Copy the snapshot into TopTransactionContext, add it to the
	 * exportedSnapshots list, and mark it pseudo-registered.  We do this to
	 * ensure that the snapshot's xmin is honored for the rest of the
	 * transaction.  (Right now, because SnapshotResetXmin is so stupid, this
	 * is overkill; but later we might make that routine smarter.)
	 */
	snapshot = CopySnapshot(snapshot);

	oldcxt = MemoryContextSwitchTo(TopTransactionContext);
	exportedSnapshots = lappend(exportedSnapshots, snapshot);
	MemoryContextSwitchTo(oldcxt);

	snapshot->regd_count++;
	RegisteredSnapshots++;

	/*
	 * Fill buf with a text serialization of the snapshot, plus identification
	 * data about this transaction.  The format expected by ImportSnapshot is
	 * pretty rigid: each line must be fieldname:value.
	 */
	initStringInfo(&buf);

	appendStringInfo(&buf, "xid:%u\n", topXid);
	appendStringInfo(&buf, "dbid:%u\n", MyDatabaseId);
	appendStringInfo(&buf, "iso:%d\n", XactIsoLevel);
	appendStringInfo(&buf, "ro:%d\n", XactReadOnly);

	appendStringInfo(&buf, "xmin:%u\n", snapshot->xmin);
	appendStringInfo(&buf, "xmax:%u\n", snapshot->xmax);

	/*
	 * We must include our own top transaction ID in the top-xid data, since
	 * by definition we will still be running when the importing transaction
	 * adopts the snapshot, but GetSnapshotData never includes our own XID in
	 * the snapshot.  (There must, therefore, be enough room to add it.)
	 *
	 * However, it could be that our topXid is after the xmax, in which case
	 * we shouldn't include it because xip[] members are expected to be before
	 * xmax.  (We need not make the same check for subxip[] members, see
	 * snapshot.h.)
	 */
	addTopXid = TransactionIdPrecedes(topXid, snapshot->xmax) ? 1 : 0;
	appendStringInfo(&buf, "xcnt:%d\n", snapshot->xcnt + addTopXid);
	for (i = 0; i < snapshot->xcnt; i++)
		appendStringInfo(&buf, "xip:%u\n", snapshot->xip[i]);
	if (addTopXid)
		appendStringInfo(&buf, "xip:%u\n", topXid);

	/*
	 * Similarly, we add our subcommitted child XIDs to the subxid data. Here,
	 * we have to cope with possible overflow.
	 */
	if (snapshot->suboverflowed ||
		snapshot->subxcnt + nchildren > GetMaxSnapshotSubxidCount())
		appendStringInfoString(&buf, "sof:1\n");
	else
	{
		appendStringInfoString(&buf, "sof:0\n");
		appendStringInfo(&buf, "sxcnt:%d\n", snapshot->subxcnt + nchildren);
		for (i = 0; i < snapshot->subxcnt; i++)
			appendStringInfo(&buf, "sxp:%u\n", snapshot->subxip[i]);
		for (i = 0; i < nchildren; i++)
			appendStringInfo(&buf, "sxp:%u\n", children[i]);
	}
	appendStringInfo(&buf, "rec:%u\n", snapshot->takenDuringRecovery);

	/*
	 * Now write the text representation into a file.  We first write to a
	 * ".tmp" filename, and rename to final filename if no error.  This
	 * ensures that no other backend can read an incomplete file
	 * (ImportSnapshot won't allow it because of its valid-characters check).
	 */
	XactExportFilePath(pathtmp, topXid, list_length(exportedSnapshots), ".tmp");
	if (!(f = AllocateFile(pathtmp, PG_BINARY_W)))
		ereport(ERROR,
				(errcode_for_file_access(),
				 errmsg("could not create file \"%s\": %m", pathtmp)));

	if (fwrite(buf.data, buf.len, 1, f) != 1)
		ereport(ERROR,
				(errcode_for_file_access(),
				 errmsg("could not write to file \"%s\": %m", pathtmp)));

	/* no fsync() since file need not survive a system crash */

	if (FreeFile(f))
		ereport(ERROR,
				(errcode_for_file_access(),
				 errmsg("could not write to file \"%s\": %m", pathtmp)));

	/*
	 * Now that we have written everything into a .tmp file, rename the file
	 * to remove the .tmp suffix.
	 */
	XactExportFilePath(path, topXid, list_length(exportedSnapshots), "");

	if (rename(pathtmp, path) < 0)
		ereport(ERROR,
				(errcode_for_file_access(),
				 errmsg("could not rename file \"%s\" to \"%s\": %m",
						pathtmp, path)));

	/*
	 * The basename of the file is what we return from pg_export_snapshot().
	 * It's already in path in a textual format and we know that the path
	 * starts with SNAPSHOT_EXPORT_DIR.  Skip over the prefix and the slash
	 * and pstrdup it so as not to return the address of a local variable.
	 */
	return pstrdup(path + strlen(SNAPSHOT_EXPORT_DIR) + 1);
}
Exemplo n.º 30
0
Arquivo: pl_bf.c Projeto: jeltz/pl-bf
Datum pl_bf_call_handler(PG_FUNCTION_ARGS)
{
    FmgrInfo flinfo;
    Datum retval, proc_source_datum;
    Form_pg_proc procStruct;
    Form_pg_type typeStruct;
    HeapTuple procTup;
    HeapTuple typeTup;
    bool isnull;
    Oid resultTypeIOParam, returnTypeOID;
    char *proc_source;
    char *a, *p;
    int i;

    // Get the function tuple
    procTup = SearchSysCache(PROCOID, ObjectIdGetDatum(fcinfo->flinfo->fn_oid),
                             0, 0, 0);
    if (!HeapTupleIsValid(procTup)) {
        elog(ERROR, "Cache lookup failed for procedure %u",
             fcinfo->flinfo->fn_oid);
    }
    procStruct = (Form_pg_proc)GETSTRUCT(procTup);
    
    // Get the function source
    proc_source_datum = SysCacheGetAttr(PROCOID, procTup,
                                        Anum_pg_proc_prosrc, &isnull);
    if (isnull) {
        elog(ERROR, "Function source is null");
    }
    proc_source = DatumGetCString(DirectFunctionCall1(textout,
                                                      proc_source_datum));

    // Get the return type tuple
    typeTup = SearchSysCache(TYPEOID, ObjectIdGetDatum(procStruct->prorettype),
                             0, 0, 0);
    if (!HeapTupleIsValid(typeTup)) {
        elog(ERROR, "Cache lookup failed for type %u", procStruct->prorettype);
    }
    typeStruct = (Form_pg_type)GETSTRUCT(typeTup);
    
    resultTypeIOParam = getTypeIOParam(typeTup);
    returnTypeOID = procStruct -> prorettype;

    a = calloc(5000, 1);
    if (!a) {
        elog(ERROR, "BAD A!");
    }

    p = a;
    for (i = 0; i < procStruct->pronargs; i++) {
        p += append_datum(p, fcinfo->arg[i], fcinfo->argnull[i],
                          procStruct->proargtypes.values[i]);
    }
    
    interpret(a, 0, proc_source);

    fmgr_info_cxt(typeStruct->typinput, &flinfo, TopMemoryContext);

    if (returnTypeOID != VOIDOID) {
        if (returnTypeOID == INT4OID) {
            retval = Int32GetDatum(*((int*)a));
        } else {
            SPI_push();
            if (returnTypeOID == BOOLOID)
                retval = InputFunctionCall(&flinfo, a[0] ? "TRUE" : "FALSE",
                                           resultTypeIOParam, -1);
            else {
                retval = InputFunctionCall(&flinfo, pstrdup(a),
                                           resultTypeIOParam, -1);
            }
            SPI_pop();
        }
    }

    ReleaseSysCache(procTup);
    ReleaseSysCache(typeTup);
    free(a);

    SPI_finish();
    
    return retval;
}