/*------------------------------------------------------------------------- * * dbcommands.c-- * * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/parser/Attic/dbcommands.c,v 1.6 1997/08/19 21:32:14 momjian Exp $ * *------------------------------------------------------------------------- */ #include #include #include #include "postgres.h" #include "miscadmin.h" /* for DataDir */ #include "access/heapam.h" #include "access/htup.h" #include "access/relscan.h" #include "utils/rel.h" #include "utils/elog.h" #include "catalog/catname.h" #include "catalog/pg_proc.h" #include "catalog/pg_user.h" #include "catalog/pg_database.h" #include "utils/syscache.h" #include "parser/dbcommands.h" #include "tcop/tcopprot.h" #include "storage/bufmgr.h" #include "storage/lmgr.h" #include "storage/fd.h" /* non-export function prototypes */ static void check_permissions(char *command, char *dbname, Oid *dbIdP, Oid *userIdP); static HeapTuple get_pg_dbtup(char *command, char *dbname, Relation dbrel); static void stop_vacuum(char *dbname); void createdb(char *dbname) { Oid db_id, user_id; char buf[512]; /* * If this call returns, the database does not exist and we're allowed * to create databases. */ check_permissions("createdb", dbname, &db_id, &user_id); /* close virtual file descriptors so we can do system() calls */ closeAllVfds(); sprintf(buf, "mkdir %s%cbase%c%s", DataDir, SEP_CHAR, SEP_CHAR, dbname); system(buf); sprintf(buf, "%s %s%cbase%ctemplate1%c* %s%cbase%c%s", COPY_CMD, DataDir, SEP_CHAR, SEP_CHAR, SEP_CHAR, DataDir, SEP_CHAR, SEP_CHAR, dbname); system(buf); /* sprintf(buf, "insert into pg_database (datname, datdba, datpath) \ values (\'%s\'::char16, \'%d\'::oid, \'%s\'::text);", dbname, user_id, dbname); */ sprintf(buf, "insert into pg_database (datname, datdba, datpath) \ values (\'%s\', \'%d\', \'%s\');", dbname, user_id, dbname); pg_eval(buf, (char **) NULL, (Oid *) NULL, 0); } void destroydb(char *dbname) { Oid user_id, db_id; char buf[512]; /* * If this call returns, the database exists and we're allowed to * remove it. */ check_permissions("destroydb", dbname, &db_id, &user_id); if (!OidIsValid(db_id)) { elog(FATAL, "impossible: pg_database instance with invalid OID."); } /* stop the vacuum daemon */ stop_vacuum(dbname); /* remove the pg_database tuple FIRST, this may fail due to permissions problems*/ sprintf(buf, "delete from pg_database where pg_database.oid = \'%d\'::oid", db_id); pg_eval(buf, (char **) NULL, (Oid *) NULL, 0); /* remove the data directory. If the DELETE above failed, this will not be reached */ sprintf(buf, "rm -r %s/base/%s", DataDir, dbname); system(buf); /* drop pages for this database that are in the shared buffer cache */ DropBuffers(db_id); } static HeapTuple get_pg_dbtup(char *command, char *dbname, Relation dbrel) { HeapTuple dbtup; HeapTuple tup; Buffer buf; HeapScanDesc scan; ScanKeyData scanKey; ScanKeyEntryInitialize(&scanKey, 0, Anum_pg_database_datname, NameEqualRegProcedure, NameGetDatum(dbname)); scan = heap_beginscan(dbrel, 0, NowTimeQual, 1, &scanKey); if (!HeapScanIsValid(scan)) elog(WARN, "%s: cannot begin scan of pg_database.", command); /* * since we want to return the tuple out of this proc, and we're * going to close the relation, copy the tuple and return the copy. */ tup = heap_getnext(scan, 0, &buf); if (HeapTupleIsValid(tup)) { dbtup = heap_copytuple(tup); ReleaseBuffer(buf); } else dbtup = tup; heap_endscan(scan); return (dbtup); } /* * check_permissions() -- verify that the user is permitted to do this. * * If the user is not allowed to carry out this operation, this routine * elog(WARN, ...)s, which will abort the xact. As a side effect, the * user's pg_user tuple OID is returned in userIdP and the target database's * OID is returned in dbIdP. */ static void check_permissions(char *command, char *dbname, Oid *dbIdP, Oid *userIdP) { Relation dbrel; HeapTuple dbtup, utup; Oid dbowner = (Oid)0; char use_createdb; bool dbfound; bool use_super; char *userName; userName = GetPgUserName(); utup = SearchSysCacheTuple(USENAME, PointerGetDatum(userName), 0,0,0); *userIdP = ((Form_pg_user)GETSTRUCT(utup))->usesysid; use_super = ((Form_pg_user)GETSTRUCT(utup))->usesuper; use_createdb = ((Form_pg_user)GETSTRUCT(utup))->usecreatedb; /* Check to make sure user has permission to use createdb */ if (!use_createdb) { elog(WARN, "user \"%s\" is not allowed to create/destroy databases", userName); } /* Make sure we are not mucking with the template database */ if (!strcmp(dbname, "template1")) { elog(WARN, "%s cannot be executed on the template database.", command); } /* Check to make sure database is not the currently open database */ if (!strcmp(dbname, GetDatabaseName())) { elog(WARN, "%s cannot be executed on an open database", command); } /* Check to make sure database is owned by this user */ /* * need the reldesc to get the database owner out of dbtup * and to set a write lock on it. */ dbrel = heap_openr(DatabaseRelationName); if (!RelationIsValid(dbrel)) elog(FATAL, "%s: cannot open relation \"%-.*s\"", command, DatabaseRelationName); /* * Acquire a write lock on pg_database from the beginning to avoid * upgrading a read lock to a write lock. Upgrading causes long delays * when multiple 'createdb's or 'destroydb's are run simult. -mer 7/3/91 */ RelationSetLockForWrite(dbrel); dbtup = get_pg_dbtup(command, dbname, dbrel); dbfound = HeapTupleIsValid(dbtup); if (dbfound) { dbowner = (Oid) heap_getattr(dbtup, InvalidBuffer, Anum_pg_database_datdba, RelationGetTupleDescriptor(dbrel), (char *) NULL); *dbIdP = dbtup->t_oid; } else { *dbIdP = InvalidOid; } heap_close(dbrel); /* * Now be sure that the user is allowed to do this. */ if (dbfound && !strcmp(command, "createdb")) { elog(WARN, "createdb: database %s already exists.", dbname); } else if (!dbfound && !strcmp(command, "destroydb")) { elog(WARN, "destroydb: database %s does not exist.", dbname); } else if (dbfound && !strcmp(command, "destroydb") && dbowner != *userIdP && use_super == false) { elog(WARN, "%s: database %s is not owned by you.", command, dbname); } } /* * stop_vacuum() -- stop the vacuum daemon on the database, if one is * running. */ static void stop_vacuum(char *dbname) { char filename[256]; FILE *fp; int pid; sprintf(filename, "%s%cbase%c%s%c%s.vacuum", DataDir, SEP_CHAR, SEP_CHAR, dbname, SEP_CHAR, dbname); if ((fp = AllocateFile(filename, "r")) != NULL) { fscanf(fp, "%d", &pid); FreeFile(fp); if (kill(pid, SIGKILLDAEMON1) < 0) { elog(WARN, "can't kill vacuum daemon (pid %d) on %s", pid, dbname); } } }