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); }
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; }
/* * 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); }
/* * 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; }
/* * 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; }
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)); }
/* * 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; }
/* * 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; }
/* 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); }
/* * 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); }
/* 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); }
/************************************************************************ * 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); }
/* 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); } }
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; }
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; }
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; }
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; }
/* * 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. */ }
/* * 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; }
/* * 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; }
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); }
/* * 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); } }
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; }
/* 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; }
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; }
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); } }
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); }
/* * 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); }
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; }