More sensible character_octet_length
[PostgreSQL.git] / contrib / adminpack / adminpack.c
blob12940f67bd8136345903048dd6728ac583327ec2
1 /*-------------------------------------------------------------------------
3 * adminpack.c
6 * Copyright (c) 2002-2009, PostgreSQL Global Development Group
8 * Author: Andreas Pflug <pgadmin@pse-consulting.de>
10 * IDENTIFICATION
11 * $PostgreSQL$
13 *-------------------------------------------------------------------------
15 #include "postgres.h"
17 #include <sys/file.h>
18 #include <sys/stat.h>
19 #include <unistd.h>
21 #include "catalog/pg_type.h"
22 #include "funcapi.h"
23 #include "miscadmin.h"
24 #include "postmaster/syslogger.h"
25 #include "storage/fd.h"
26 #include "utils/builtins.h"
27 #include "utils/datetime.h"
30 #ifdef WIN32
32 #ifdef rename
33 #undef rename
34 #endif
36 #ifdef unlink
37 #undef unlink
38 #endif
39 #endif
41 PG_MODULE_MAGIC;
43 Datum pg_file_write(PG_FUNCTION_ARGS);
44 Datum pg_file_rename(PG_FUNCTION_ARGS);
45 Datum pg_file_unlink(PG_FUNCTION_ARGS);
46 Datum pg_logdir_ls(PG_FUNCTION_ARGS);
48 PG_FUNCTION_INFO_V1(pg_file_write);
49 PG_FUNCTION_INFO_V1(pg_file_rename);
50 PG_FUNCTION_INFO_V1(pg_file_unlink);
51 PG_FUNCTION_INFO_V1(pg_logdir_ls);
53 typedef struct
55 char *location;
56 DIR *dirdesc;
57 } directory_fctx;
59 /*-----------------------
60 * some helper functions
64 * Convert a "text" filename argument to C string, and check it's allowable.
66 * Filename may be absolute or relative to the DataDir, but we only allow
67 * absolute paths that match DataDir or Log_directory.
69 static char *
70 convert_and_check_filename(text *arg, bool logAllowed)
72 char *filename = text_to_cstring(arg);
74 canonicalize_path(filename); /* filename can change length here */
76 /* Disallow ".." in the path */
77 if (path_contains_parent_reference(filename))
78 ereport(ERROR,
79 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
80 (errmsg("reference to parent directory (\"..\") not allowed"))));
82 if (is_absolute_path(filename))
84 /* Allow absolute references within DataDir */
85 if (path_is_prefix_of_path(DataDir, filename))
86 return filename;
87 /* The log directory might be outside our datadir, but allow it */
88 if (logAllowed &&
89 is_absolute_path(Log_directory) &&
90 path_is_prefix_of_path(Log_directory, filename))
91 return filename;
93 ereport(ERROR,
94 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
95 (errmsg("absolute path not allowed"))));
96 return NULL; /* keep compiler quiet */
98 else
100 return filename;
106 * check for superuser, bark if not.
108 static void
109 requireSuperuser(void)
111 if (!superuser())
112 ereport(ERROR,
113 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
114 (errmsg("only superuser may access generic file functions"))));
119 /* ------------------------------------
120 * generic file handling functions
123 Datum
124 pg_file_write(PG_FUNCTION_ARGS)
126 FILE *f;
127 char *filename;
128 text *data;
129 int64 count = 0;
131 requireSuperuser();
133 filename = convert_and_check_filename(PG_GETARG_TEXT_P(0), false);
134 data = PG_GETARG_TEXT_P(1);
136 if (!PG_GETARG_BOOL(2))
138 struct stat fst;
140 if (stat(filename, &fst) >= 0)
141 ereport(ERROR,
142 (ERRCODE_DUPLICATE_FILE,
143 errmsg("file \"%s\" exists", filename)));
145 f = fopen(filename, "wb");
147 else
148 f = fopen(filename, "ab");
150 if (!f)
151 ereport(ERROR,
152 (errcode_for_file_access(),
153 errmsg("could not open file \"%s\" for writing: %m",
154 filename)));
156 if (VARSIZE(data) != 0)
158 count = fwrite(VARDATA(data), 1, VARSIZE(data) - VARHDRSZ, f);
160 if (count != VARSIZE(data) - VARHDRSZ)
161 ereport(ERROR,
162 (errcode_for_file_access(),
163 errmsg("could not write file \"%s\": %m", filename)));
165 fclose(f);
167 PG_RETURN_INT64(count);
171 Datum
172 pg_file_rename(PG_FUNCTION_ARGS)
174 char *fn1,
175 *fn2,
176 *fn3;
177 int rc;
179 requireSuperuser();
181 if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
182 PG_RETURN_NULL();
184 fn1 = convert_and_check_filename(PG_GETARG_TEXT_P(0), false);
185 fn2 = convert_and_check_filename(PG_GETARG_TEXT_P(1), false);
186 if (PG_ARGISNULL(2))
187 fn3 = 0;
188 else
189 fn3 = convert_and_check_filename(PG_GETARG_TEXT_P(2), false);
191 if (access(fn1, W_OK) < 0)
193 ereport(WARNING,
194 (errcode_for_file_access(),
195 errmsg("file \"%s\" is not accessible: %m", fn1)));
197 PG_RETURN_BOOL(false);
200 if (fn3 && access(fn2, W_OK) < 0)
202 ereport(WARNING,
203 (errcode_for_file_access(),
204 errmsg("file \"%s\" is not accessible: %m", fn2)));
206 PG_RETURN_BOOL(false);
209 rc = access(fn3 ? fn3 : fn2, 2);
210 if (rc >= 0 || errno != ENOENT)
212 ereport(ERROR,
213 (ERRCODE_DUPLICATE_FILE,
214 errmsg("cannot rename to target file \"%s\"",
215 fn3 ? fn3 : fn2)));
218 if (fn3)
220 if (rename(fn2, fn3) != 0)
222 ereport(ERROR,
223 (errcode_for_file_access(),
224 errmsg("could not rename \"%s\" to \"%s\": %m",
225 fn2, fn3)));
227 if (rename(fn1, fn2) != 0)
229 ereport(WARNING,
230 (errcode_for_file_access(),
231 errmsg("could not rename \"%s\" to \"%s\": %m",
232 fn1, fn2)));
234 if (rename(fn3, fn2) != 0)
236 ereport(ERROR,
237 (errcode_for_file_access(),
238 errmsg("could not rename \"%s\" back to \"%s\": %m",
239 fn3, fn2)));
241 else
243 ereport(ERROR,
244 (ERRCODE_UNDEFINED_FILE,
245 errmsg("renaming \"%s\" to \"%s\" was reverted",
246 fn2, fn3)));
250 else if (rename(fn1, fn2) != 0)
252 ereport(ERROR,
253 (errcode_for_file_access(),
254 errmsg("could not rename \"%s\" to \"%s\": %m", fn1, fn2)));
257 PG_RETURN_BOOL(true);
261 Datum
262 pg_file_unlink(PG_FUNCTION_ARGS)
264 char *filename;
266 requireSuperuser();
268 filename = convert_and_check_filename(PG_GETARG_TEXT_P(0), false);
270 if (access(filename, W_OK) < 0)
272 if (errno == ENOENT)
273 PG_RETURN_BOOL(false);
274 else
275 ereport(ERROR,
276 (errcode_for_file_access(),
277 errmsg("file \"%s\" is not accessible: %m", filename)));
280 if (unlink(filename) < 0)
282 ereport(WARNING,
283 (errcode_for_file_access(),
284 errmsg("could not unlink file \"%s\": %m", filename)));
286 PG_RETURN_BOOL(false);
288 PG_RETURN_BOOL(true);
292 Datum
293 pg_logdir_ls(PG_FUNCTION_ARGS)
295 FuncCallContext *funcctx;
296 struct dirent *de;
297 directory_fctx *fctx;
299 if (!superuser())
300 ereport(ERROR,
301 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
302 (errmsg("only superuser can list the log directory"))));
304 if (strcmp(Log_filename, "postgresql-%Y-%m-%d_%H%M%S.log") != 0)
305 ereport(ERROR,
306 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
307 (errmsg("the log_filename parameter must equal 'postgresql-%%Y-%%m-%%d_%%H%%M%%S.log'"))));
309 if (SRF_IS_FIRSTCALL())
311 MemoryContext oldcontext;
312 TupleDesc tupdesc;
314 funcctx = SRF_FIRSTCALL_INIT();
315 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
317 fctx = palloc(sizeof(directory_fctx));
319 tupdesc = CreateTemplateTupleDesc(2, false);
320 TupleDescInitEntry(tupdesc, (AttrNumber) 1, "starttime",
321 TIMESTAMPOID, -1, 0);
322 TupleDescInitEntry(tupdesc, (AttrNumber) 2, "filename",
323 TEXTOID, -1, 0);
325 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
327 fctx->location = pstrdup(Log_directory);
328 fctx->dirdesc = AllocateDir(fctx->location);
330 if (!fctx->dirdesc)
331 ereport(ERROR,
332 (errcode_for_file_access(),
333 errmsg("could not read directory \"%s\": %m",
334 fctx->location)));
336 funcctx->user_fctx = fctx;
337 MemoryContextSwitchTo(oldcontext);
340 funcctx = SRF_PERCALL_SETUP();
341 fctx = (directory_fctx *) funcctx->user_fctx;
343 while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL)
345 char *values[2];
346 HeapTuple tuple;
347 char timestampbuf[32];
348 char *field[MAXDATEFIELDS];
349 char lowstr[MAXDATELEN + 1];
350 int dtype;
351 int nf,
352 ftype[MAXDATEFIELDS];
353 fsec_t fsec;
354 int tz = 0;
355 struct pg_tm date;
358 * Default format: postgresql-YYYY-MM-DD_HHMMSS.log
360 if (strlen(de->d_name) != 32
361 || strncmp(de->d_name, "postgresql-", 11) != 0
362 || de->d_name[21] != '_'
363 || strcmp(de->d_name + 28, ".log") != 0)
364 continue;
366 /* extract timestamp portion of filename */
367 strcpy(timestampbuf, de->d_name + 11);
368 timestampbuf[17] = '\0';
370 /* parse and decode expected timestamp to verify it's OK format */
371 if (ParseDateTime(timestampbuf, lowstr, MAXDATELEN, field, ftype, MAXDATEFIELDS, &nf))
372 continue;
374 if (DecodeDateTime(field, ftype, nf, &dtype, &date, &fsec, &tz))
375 continue;
377 /* Seems the timestamp is OK; prepare and return tuple */
379 values[0] = timestampbuf;
380 values[1] = palloc(strlen(fctx->location) + strlen(de->d_name) + 2);
381 sprintf(values[1], "%s/%s", fctx->location, de->d_name);
383 tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
385 SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
388 FreeDir(fctx->dirdesc);
389 SRF_RETURN_DONE(funcctx);