Header And Logo

PostgreSQL
| The world's most advanced open source database.

adminpack.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * adminpack.c
00004  *
00005  *
00006  * Copyright (c) 2002-2013, PostgreSQL Global Development Group
00007  *
00008  * Author: Andreas Pflug <[email protected]>
00009  *
00010  * IDENTIFICATION
00011  *    contrib/adminpack/adminpack.c
00012  *
00013  *-------------------------------------------------------------------------
00014  */
00015 #include "postgres.h"
00016 
00017 #include <sys/file.h>
00018 #include <sys/stat.h>
00019 #include <unistd.h>
00020 
00021 #include "catalog/pg_type.h"
00022 #include "funcapi.h"
00023 #include "miscadmin.h"
00024 #include "postmaster/syslogger.h"
00025 #include "storage/fd.h"
00026 #include "utils/builtins.h"
00027 #include "utils/datetime.h"
00028 
00029 
00030 #ifdef WIN32
00031 
00032 #ifdef rename
00033 #undef rename
00034 #endif
00035 
00036 #ifdef unlink
00037 #undef unlink
00038 #endif
00039 #endif
00040 
00041 PG_MODULE_MAGIC;
00042 
00043 Datum       pg_file_write(PG_FUNCTION_ARGS);
00044 Datum       pg_file_rename(PG_FUNCTION_ARGS);
00045 Datum       pg_file_unlink(PG_FUNCTION_ARGS);
00046 Datum       pg_logdir_ls(PG_FUNCTION_ARGS);
00047 
00048 PG_FUNCTION_INFO_V1(pg_file_write);
00049 PG_FUNCTION_INFO_V1(pg_file_rename);
00050 PG_FUNCTION_INFO_V1(pg_file_unlink);
00051 PG_FUNCTION_INFO_V1(pg_logdir_ls);
00052 
00053 typedef struct
00054 {
00055     char       *location;
00056     DIR        *dirdesc;
00057 } directory_fctx;
00058 
00059 /*-----------------------
00060  * some helper functions
00061  */
00062 
00063 /*
00064  * Convert a "text" filename argument to C string, and check it's allowable.
00065  *
00066  * Filename may be absolute or relative to the DataDir, but we only allow
00067  * absolute paths that match DataDir or Log_directory.
00068  */
00069 static char *
00070 convert_and_check_filename(text *arg, bool logAllowed)
00071 {
00072     char       *filename = text_to_cstring(arg);
00073 
00074     canonicalize_path(filename);    /* filename can change length here */
00075 
00076     if (is_absolute_path(filename))
00077     {
00078         /* Disallow '/a/b/data/..' */
00079         if (path_contains_parent_reference(filename))
00080             ereport(ERROR,
00081                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00082             (errmsg("reference to parent directory (\"..\") not allowed"))));
00083 
00084         /*
00085          * Allow absolute paths if within DataDir or Log_directory, even
00086          * though Log_directory might be outside DataDir.
00087          */
00088         if (!path_is_prefix_of_path(DataDir, filename) &&
00089             (!logAllowed || !is_absolute_path(Log_directory) ||
00090              !path_is_prefix_of_path(Log_directory, filename)))
00091             ereport(ERROR,
00092                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00093                      (errmsg("absolute path not allowed"))));
00094     }
00095     else if (!path_is_relative_and_below_cwd(filename))
00096         ereport(ERROR,
00097                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00098                  (errmsg("path must be in or below the current directory"))));
00099 
00100     return filename;
00101 }
00102 
00103 
00104 /*
00105  * check for superuser, bark if not.
00106  */
00107 static void
00108 requireSuperuser(void)
00109 {
00110     if (!superuser())
00111         ereport(ERROR,
00112                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00113               (errmsg("only superuser may access generic file functions"))));
00114 }
00115 
00116 
00117 
00118 /* ------------------------------------
00119  * generic file handling functions
00120  */
00121 
00122 Datum
00123 pg_file_write(PG_FUNCTION_ARGS)
00124 {
00125     FILE       *f;
00126     char       *filename;
00127     text       *data;
00128     int64       count = 0;
00129 
00130     requireSuperuser();
00131 
00132     filename = convert_and_check_filename(PG_GETARG_TEXT_P(0), false);
00133     data = PG_GETARG_TEXT_P(1);
00134 
00135     if (!PG_GETARG_BOOL(2))
00136     {
00137         struct stat fst;
00138 
00139         if (stat(filename, &fst) >= 0)
00140             ereport(ERROR,
00141                     (ERRCODE_DUPLICATE_FILE,
00142                      errmsg("file \"%s\" exists", filename)));
00143 
00144         f = fopen(filename, "wb");
00145     }
00146     else
00147         f = fopen(filename, "ab");
00148 
00149     if (!f)
00150         ereport(ERROR,
00151                 (errcode_for_file_access(),
00152                  errmsg("could not open file \"%s\" for writing: %m",
00153                         filename)));
00154 
00155     if (VARSIZE(data) != 0)
00156     {
00157         count = fwrite(VARDATA(data), 1, VARSIZE(data) - VARHDRSZ, f);
00158 
00159         if (count != VARSIZE(data) - VARHDRSZ)
00160             ereport(ERROR,
00161                     (errcode_for_file_access(),
00162                      errmsg("could not write file \"%s\": %m", filename)));
00163     }
00164     fclose(f);
00165 
00166     PG_RETURN_INT64(count);
00167 }
00168 
00169 
00170 Datum
00171 pg_file_rename(PG_FUNCTION_ARGS)
00172 {
00173     char       *fn1,
00174                *fn2,
00175                *fn3;
00176     int         rc;
00177 
00178     requireSuperuser();
00179 
00180     if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
00181         PG_RETURN_NULL();
00182 
00183     fn1 = convert_and_check_filename(PG_GETARG_TEXT_P(0), false);
00184     fn2 = convert_and_check_filename(PG_GETARG_TEXT_P(1), false);
00185     if (PG_ARGISNULL(2))
00186         fn3 = 0;
00187     else
00188         fn3 = convert_and_check_filename(PG_GETARG_TEXT_P(2), false);
00189 
00190     if (access(fn1, W_OK) < 0)
00191     {
00192         ereport(WARNING,
00193                 (errcode_for_file_access(),
00194                  errmsg("file \"%s\" is not accessible: %m", fn1)));
00195 
00196         PG_RETURN_BOOL(false);
00197     }
00198 
00199     if (fn3 && access(fn2, W_OK) < 0)
00200     {
00201         ereport(WARNING,
00202                 (errcode_for_file_access(),
00203                  errmsg("file \"%s\" is not accessible: %m", fn2)));
00204 
00205         PG_RETURN_BOOL(false);
00206     }
00207 
00208     rc = access(fn3 ? fn3 : fn2, 2);
00209     if (rc >= 0 || errno != ENOENT)
00210     {
00211         ereport(ERROR,
00212                 (ERRCODE_DUPLICATE_FILE,
00213                  errmsg("cannot rename to target file \"%s\"",
00214                         fn3 ? fn3 : fn2)));
00215     }
00216 
00217     if (fn3)
00218     {
00219         if (rename(fn2, fn3) != 0)
00220         {
00221             ereport(ERROR,
00222                     (errcode_for_file_access(),
00223                      errmsg("could not rename \"%s\" to \"%s\": %m",
00224                             fn2, fn3)));
00225         }
00226         if (rename(fn1, fn2) != 0)
00227         {
00228             ereport(WARNING,
00229                     (errcode_for_file_access(),
00230                      errmsg("could not rename \"%s\" to \"%s\": %m",
00231                             fn1, fn2)));
00232 
00233             if (rename(fn3, fn2) != 0)
00234             {
00235                 ereport(ERROR,
00236                         (errcode_for_file_access(),
00237                          errmsg("could not rename \"%s\" back to \"%s\": %m",
00238                                 fn3, fn2)));
00239             }
00240             else
00241             {
00242                 ereport(ERROR,
00243                         (ERRCODE_UNDEFINED_FILE,
00244                          errmsg("renaming \"%s\" to \"%s\" was reverted",
00245                                 fn2, fn3)));
00246             }
00247         }
00248     }
00249     else if (rename(fn1, fn2) != 0)
00250     {
00251         ereport(ERROR,
00252                 (errcode_for_file_access(),
00253                  errmsg("could not rename \"%s\" to \"%s\": %m", fn1, fn2)));
00254     }
00255 
00256     PG_RETURN_BOOL(true);
00257 }
00258 
00259 
00260 Datum
00261 pg_file_unlink(PG_FUNCTION_ARGS)
00262 {
00263     char       *filename;
00264 
00265     requireSuperuser();
00266 
00267     filename = convert_and_check_filename(PG_GETARG_TEXT_P(0), false);
00268 
00269     if (access(filename, W_OK) < 0)
00270     {
00271         if (errno == ENOENT)
00272             PG_RETURN_BOOL(false);
00273         else
00274             ereport(ERROR,
00275                     (errcode_for_file_access(),
00276                      errmsg("file \"%s\" is not accessible: %m", filename)));
00277     }
00278 
00279     if (unlink(filename) < 0)
00280     {
00281         ereport(WARNING,
00282                 (errcode_for_file_access(),
00283                  errmsg("could not unlink file \"%s\": %m", filename)));
00284 
00285         PG_RETURN_BOOL(false);
00286     }
00287     PG_RETURN_BOOL(true);
00288 }
00289 
00290 
00291 Datum
00292 pg_logdir_ls(PG_FUNCTION_ARGS)
00293 {
00294     FuncCallContext *funcctx;
00295     struct dirent *de;
00296     directory_fctx *fctx;
00297 
00298     if (!superuser())
00299         ereport(ERROR,
00300                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00301                  (errmsg("only superuser can list the log directory"))));
00302 
00303     if (strcmp(Log_filename, "postgresql-%Y-%m-%d_%H%M%S.log") != 0)
00304         ereport(ERROR,
00305                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00306                  (errmsg("the log_filename parameter must equal 'postgresql-%%Y-%%m-%%d_%%H%%M%%S.log'"))));
00307 
00308     if (SRF_IS_FIRSTCALL())
00309     {
00310         MemoryContext oldcontext;
00311         TupleDesc   tupdesc;
00312 
00313         funcctx = SRF_FIRSTCALL_INIT();
00314         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
00315 
00316         fctx = palloc(sizeof(directory_fctx));
00317 
00318         tupdesc = CreateTemplateTupleDesc(2, false);
00319         TupleDescInitEntry(tupdesc, (AttrNumber) 1, "starttime",
00320                            TIMESTAMPOID, -1, 0);
00321         TupleDescInitEntry(tupdesc, (AttrNumber) 2, "filename",
00322                            TEXTOID, -1, 0);
00323 
00324         funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
00325 
00326         fctx->location = pstrdup(Log_directory);
00327         fctx->dirdesc = AllocateDir(fctx->location);
00328 
00329         if (!fctx->dirdesc)
00330             ereport(ERROR,
00331                     (errcode_for_file_access(),
00332                      errmsg("could not read directory \"%s\": %m",
00333                             fctx->location)));
00334 
00335         funcctx->user_fctx = fctx;
00336         MemoryContextSwitchTo(oldcontext);
00337     }
00338 
00339     funcctx = SRF_PERCALL_SETUP();
00340     fctx = (directory_fctx *) funcctx->user_fctx;
00341 
00342     while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL)
00343     {
00344         char       *values[2];
00345         HeapTuple   tuple;
00346         char        timestampbuf[32];
00347         char       *field[MAXDATEFIELDS];
00348         char        lowstr[MAXDATELEN + 1];
00349         int         dtype;
00350         int         nf,
00351                     ftype[MAXDATEFIELDS];
00352         fsec_t      fsec;
00353         int         tz = 0;
00354         struct pg_tm date;
00355 
00356         /*
00357          * Default format: postgresql-YYYY-MM-DD_HHMMSS.log
00358          */
00359         if (strlen(de->d_name) != 32
00360             || strncmp(de->d_name, "postgresql-", 11) != 0
00361             || de->d_name[21] != '_'
00362             || strcmp(de->d_name + 28, ".log") != 0)
00363             continue;
00364 
00365         /* extract timestamp portion of filename */
00366         strcpy(timestampbuf, de->d_name + 11);
00367         timestampbuf[17] = '\0';
00368 
00369         /* parse and decode expected timestamp to verify it's OK format */
00370         if (ParseDateTime(timestampbuf, lowstr, MAXDATELEN, field, ftype, MAXDATEFIELDS, &nf))
00371             continue;
00372 
00373         if (DecodeDateTime(field, ftype, nf, &dtype, &date, &fsec, &tz))
00374             continue;
00375 
00376         /* Seems the timestamp is OK; prepare and return tuple */
00377 
00378         values[0] = timestampbuf;
00379         values[1] = palloc(strlen(fctx->location) + strlen(de->d_name) + 2);
00380         sprintf(values[1], "%s/%s", fctx->location, de->d_name);
00381 
00382         tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
00383 
00384         SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
00385     }
00386 
00387     FreeDir(fctx->dirdesc);
00388     SRF_RETURN_DONE(funcctx);
00389 }