dfmgr.c 12.0 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * dfmgr.c
4
 *	  Dynamic function manager code.
5
 *
6
 * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
B
Add:  
Bruce Momjian 已提交
7
 * Portions Copyright (c) 1994, Regents of the University of California
8 9 10
 *
 *
 * IDENTIFICATION
11
 *	  $PostgreSQL: pgsql/src/backend/utils/fmgr/dfmgr.c,v 1.84 2006/05/30 21:21:30 tgl Exp $
12 13 14
 *
 *-------------------------------------------------------------------------
 */
15 16
#include "postgres.h"

17 18
#include <sys/stat.h>

B
Bruce Momjian 已提交
19
#include "dynloader.h"
20
#include "miscadmin.h"
21
#include "utils/dynamic_loader.h"
22

23 24

/*
25
 * List of dynamically loaded files (kept in malloc'd memory).
26 27 28 29 30 31
 */

typedef struct df_files
{
	struct df_files *next;		/* List link */
	dev_t		device;			/* Device file is on */
B
Bruce Momjian 已提交
32 33
#ifndef WIN32					/* ensures we never again depend on this under
								 * win32 */
34
	ino_t		inode;			/* Inode number of file */
35
#endif
36 37
	void	   *handle;			/* a handle for pg_dl* functions */
	char		filename[1];	/* Full pathname of file */
B
Bruce Momjian 已提交
38 39 40

	/*
	 * we allocate the block big enough for actual length of pathname.
41 42 43 44
	 * filename[] must be last item in struct!
	 */
} DynamicFileList;

45 46
static DynamicFileList *file_list = NULL;
static DynamicFileList *file_tail = NULL;
47

48 49
/* stat() call under Win32 returns an st_ino field, but it has no meaning */
#ifndef WIN32
50
#define SAME_INODE(A,B) ((A).st_ino == (B).inode && (A).st_dev == (B).device)
51 52 53
#else
#define SAME_INODE(A,B) false
#endif
54

55
char	   *Dynamic_library_path;
56 57

static bool file_exists(const char *name);
58 59 60
static char *find_in_dynamic_libpath(const char *basename);
static char *expand_dynamic_library_name(const char *name);
static char *substitute_libpath_macro(const char *name);
61

62
/* Magic structure that module needs to match to be accepted */
63 64
static const Pg_magic_struct magic_data = PG_MODULE_MAGIC_DATA;

65

66 67
/*
 * Load the specified dynamic-link library file, and look for a function
68 69 70 71
 * named funcname in it.  (funcname can be NULL to just load the file.)
 *
 * If the function is not found, we raise an error if signalNotFound is true,
 * else return (PGFunction) NULL.  Note that errors in loading the library
72
 * will provoke ereport() regardless of signalNotFound.
73 74 75 76 77 78
 *
 * If filehandle is not NULL, then *filehandle will be set to a handle
 * identifying the library file.  The filehandle can be used with
 * lookup_external_function to lookup additional functions in the same file
 * at less cost than repeating load_external_function.
  */
79
PGFunction
80
load_external_function(char *filename, char *funcname,
81
					   bool signalNotFound, void **filehandle)
82
{
83 84
	DynamicFileList *file_scanner;
	PGFunction	retval;
85
	PGModuleMagicFunction magic_func;
86 87
	char	   *load_error;
	struct stat stat_buf;
88 89 90
	char	   *fullname;

	fullname = expand_dynamic_library_name(filename);
91 92 93
	if (!fullname)
		fullname = pstrdup(filename);
	/* at this point fullname is always freshly palloc'd */
94 95

	/*
96
	 * Scan the list of loaded FILES to see if the file has been loaded.
97
	 */
98
	for (file_scanner = file_list;
99
		 file_scanner != NULL &&
100
		 strcmp(fullname, file_scanner->filename) != 0;
101 102
		 file_scanner = file_scanner->next)
		;
103
	if (file_scanner == NULL)
104
	{
105 106 107
		/*
		 * Check for same files - different paths (ie, symlink or link)
		 */
108
		if (stat(fullname, &stat_buf) == -1)
109 110
			ereport(ERROR,
					(errcode_for_file_access(),
111 112
					 errmsg("could not access file \"%s\": %m",
							fullname)));
113

114
		for (file_scanner = file_list;
115
			 file_scanner != NULL &&
B
Bruce Momjian 已提交
116
			 !SAME_INODE(stat_buf, *file_scanner);
117 118 119 120
			 file_scanner = file_scanner->next)
			;
	}

121
	if (file_scanner == NULL)
122
	{
123 124 125 126
		/*
		 * File not loaded yet.
		 */
		file_scanner = (DynamicFileList *)
127
			malloc(sizeof(DynamicFileList) + strlen(fullname));
128
		if (file_scanner == NULL)
129
			ereport(ERROR,
130
					(errcode(ERRCODE_OUT_OF_MEMORY),
131
					 errmsg("out of memory")));
132

133
		MemSet(file_scanner, 0, sizeof(DynamicFileList));
134
		strcpy(file_scanner->filename, fullname);
135
		file_scanner->device = stat_buf.st_dev;
136
#ifndef WIN32
137
		file_scanner->inode = stat_buf.st_ino;
138
#endif
139
		file_scanner->next = NULL;
140

141
		file_scanner->handle = pg_dlopen(fullname);
142
		if (file_scanner->handle == NULL)
143 144 145
		{
			load_error = (char *) pg_dlerror();
			free((char *) file_scanner);
146 147 148 149 150
			/* errcode_for_file_access might not be appropriate here? */
			ereport(ERROR,
					(errcode_for_file_access(),
					 errmsg("could not load library \"%s\": %s",
							fullname, load_error)));
151 152
		}

153 154 155 156
		/* Check the magic function to determine compatibility */
		magic_func = (PGModuleMagicFunction)
			pg_dlsym(file_scanner->handle, PG_MAGIC_FUNCTION_NAME_STRING);
		if (magic_func)
157
		{
158 159 160 161
			const Pg_magic_struct *magic_data_ptr = (*magic_func) ();

			if (magic_data_ptr->len != magic_data.len ||
			    memcmp(magic_data_ptr, &magic_data, magic_data.len) != 0)
162
			{
163 164 165 166 167 168 169 170 171 172 173 174 175
				/* copy data block before unlinking library */
				Pg_magic_struct module_magic_data = *magic_data_ptr;

				/* try to unlink library */
				pg_dlclose(file_scanner->handle);
				free((char *) file_scanner);

				/*
				 * Report suitable error.  It's probably not worth writing
				 * a separate error message for each field; only the most
				 * common case of wrong major version gets its own message.
				 */
				if (module_magic_data.version != magic_data.version)
176
					ereport(ERROR,
177 178 179 180 181 182 183 184 185
							(errmsg("incompatible library \"%s\": version mismatch",
									fullname),
							 errdetail("Server is version %d.%d, library is version %d.%d.",
									   magic_data.version/100,
									   magic_data.version % 100,
									   module_magic_data.version/100,
									   module_magic_data.version % 100)));
				ereport(ERROR,
						(errmsg("incompatible library \"%s\": magic block mismatch",
186 187 188 189
								fullname)));
			}
		}
		else
190 191 192 193 194 195 196 197 198 199
		{
			/*
			 * Currently we do not reject modules for not having a
			 * magic block, it would break every external module in
			 * existence. At some point though, this will become an ERROR.
			 */
			ereport(LOG,
					(errmsg("library \"%s\" does not have a magic block",
							fullname)));
		}
200
		
201
		/* OK to link it into list */
202
		if (file_list == NULL)
203 204 205
			file_list = file_scanner;
		else
			file_tail->next = file_scanner;
206 207 208
		file_tail = file_scanner;
	}

209 210 211 212
	/* Return handle if caller wants it. */
	if (filehandle)
		*filehandle = file_scanner->handle;

213 214 215
	/*
	 * If funcname is NULL, we only wanted to load the file.
	 */
216
	if (funcname == NULL)
217 218
	{
		pfree(fullname);
219
		return NULL;
220
	}
221 222

	retval = pg_dlsym(file_scanner->handle, funcname);
223

224
	if (retval == NULL && signalNotFound)
225 226 227 228
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_FUNCTION),
				 errmsg("could not find function \"%s\" in file \"%s\"",
						funcname, fullname)));
229

230
	pfree(fullname);
231
	return retval;
232 233 234
}

/*
235
 * This function loads a shlib file without looking up any particular
B
Bruce Momjian 已提交
236
 * function in it.	If the same shlib has previously been loaded,
237
 * unload and reload it.
238 239 240 241
 */
void
load_file(char *filename)
{
242
	DynamicFileList *file_scanner,
243 244
			   *prv,
			   *nxt;
245
	struct stat stat_buf;
246 247 248
	char	   *fullname;

	fullname = expand_dynamic_library_name(filename);
249 250 251
	if (!fullname)
		fullname = pstrdup(filename);
	/* at this point fullname is always freshly palloc'd */
252

253
	/*
B
Bruce Momjian 已提交
254
	 * We need to do stat() in order to determine whether this is the same
B
Bruce Momjian 已提交
255 256
	 * file as a previously loaded file; it's also handy so as to give a good
	 * error message if bogus file name given.
257
	 */
258
	if (stat(fullname, &stat_buf) == -1)
259 260 261
		ereport(ERROR,
				(errcode_for_file_access(),
				 errmsg("could not access file \"%s\": %m", fullname)));
262

263
	/*
B
Bruce Momjian 已提交
264 265
	 * We have to zap all entries in the list that match on either filename or
	 * inode, else load_external_function() won't do anything.
266 267 268
	 */
	prv = NULL;
	for (file_scanner = file_list; file_scanner != NULL; file_scanner = nxt)
269
	{
270 271 272
		nxt = file_scanner->next;
		if (strcmp(fullname, file_scanner->filename) == 0 ||
			SAME_INODE(stat_buf, *file_scanner))
273
		{
274 275 276 277
			if (prv)
				prv->next = nxt;
			else
				file_list = nxt;
278
			clear_external_function_hash(file_scanner->handle);
279 280 281
			pg_dlclose(file_scanner->handle);
			free((char *) file_scanner);
			/* prv does not change */
282
		}
283
		else
284
			prv = file_scanner;
285 286
	}

287
	load_external_function(fullname, NULL, false, NULL);
288 289

	pfree(fullname);
290
}
291

292 293 294 295 296 297 298 299 300
/*
 * Lookup a function whose library file is already loaded.
 * Return (PGFunction) NULL if not found.
 */
PGFunction
lookup_external_function(void *filehandle, char *funcname)
{
	return pg_dlsym(filehandle, funcname);
}
301 302 303 304 305 306 307 308 309 310


static bool
file_exists(const char *name)
{
	struct stat st;

	AssertArg(name != NULL);

	if (stat(name, &st) == 0)
311
		return S_ISDIR(st.st_mode) ? false : true;
312
	else if (!(errno == ENOENT || errno == ENOTDIR || errno == EACCES))
313 314 315
		ereport(ERROR,
				(errcode_for_file_access(),
				 errmsg("could not access file \"%s\": %m", name)));
316 317 318 319 320 321 322 323 324 325 326 327 328 329

	return false;
}


/* Example format: ".so" */
#ifndef DLSUFFIX
#error "DLSUFFIX must be defined to compile this file."
#endif

/*
 * If name contains a slash, check if the file exists, if so return
 * the name.  Else (no slash) try to expand using search path (see
 * find_in_dynamic_libpath below); if that works, return the fully
330
 * expanded file name.	If the previous failed, append DLSUFFIX and
331
 * try again.  If all fails, return NULL.
332 333
 *
 * A non-NULL result will always be freshly palloc'd.
334 335 336 337
 */
static char *
expand_dynamic_library_name(const char *name)
{
338 339 340
	bool		have_slash;
	char	   *new;
	char	   *full;
341 342 343

	AssertArg(name);

344
	have_slash = (first_dir_separator(name) != NULL);
345 346 347 348 349 350 351 352 353

	if (!have_slash)
	{
		full = find_in_dynamic_libpath(name);
		if (full)
			return full;
	}
	else
	{
354 355 356
		full = substitute_libpath_macro(name);
		if (file_exists(full))
			return full;
357
		pfree(full);
358 359
	}

360
	new = palloc(strlen(name) + strlen(DLSUFFIX) + 1);
361
	strcpy(new, name);
362
	strcat(new, DLSUFFIX);
363 364 365 366 367 368 369 370 371 372

	if (!have_slash)
	{
		full = find_in_dynamic_libpath(new);
		pfree(new);
		if (full)
			return full;
	}
	else
	{
373
		full = substitute_libpath_macro(new);
374
		pfree(new);
375 376
		if (file_exists(full))
			return full;
377
		pfree(full);
378
	}
379

380 381 382 383
	return NULL;
}


384 385 386 387
/*
 * Substitute for any macros appearing in the given string.
 * Result is always freshly palloc'd.
 */
388
static char *
389
substitute_libpath_macro(const char *name)
390
{
391 392
	const char *sep_ptr;
	char	   *ret;
B
Bruce Momjian 已提交
393

394 395
	AssertArg(name != NULL);

396
	if (name[0] != '$')
397 398
		return pstrdup(name);

399
	if ((sep_ptr = first_dir_separator(name)) == NULL)
400
		sep_ptr = name + strlen(name);
B
Bruce Momjian 已提交
401

402 403
	if (strlen("$libdir") != sep_ptr - name ||
		strncmp(name, "$libdir", strlen("$libdir")) != 0)
404 405
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_NAME),
B
Bruce Momjian 已提交
406
			errmsg("invalid macro name in dynamic library path: %s", name)));
407

408
	ret = palloc(strlen(pkglib_path) + strlen(sep_ptr) + 1);
409

410 411
	strcpy(ret, pkglib_path);
	strcat(ret, sep_ptr);
412

413
	return ret;
414 415 416
}


417 418
/*
 * Search for a file called 'basename' in the colon-separated search
419 420 421
 * path Dynamic_library_path.  If the file is found, the full file name
 * is returned in freshly palloc'd memory.  If the file is not found,
 * return NULL.
422 423
 */
static char *
424
find_in_dynamic_libpath(const char *basename)
425 426
{
	const char *p;
427
	size_t		baselen;
428 429

	AssertArg(basename != NULL);
430
	AssertArg(first_dir_separator(basename) == NULL);
431 432 433 434 435 436 437 438
	AssertState(Dynamic_library_path != NULL);

	p = Dynamic_library_path;
	if (strlen(p) == 0)
		return NULL;

	baselen = strlen(basename);

439 440
	for (;;)
	{
441 442 443 444
		size_t		len;
		char	   *piece;
		char	   *mangled;
		char	   *full;
445

446
		piece = first_path_separator(p);
B
Bruce Momjian 已提交
447
		if (piece == p)
448 449
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_NAME),
B
Bruce Momjian 已提交
450
					 errmsg("zero-length component in parameter \"dynamic_library_path\"")));
451

B
Bruce Momjian 已提交
452 453
		if (piece == 0)
			len = strlen(p);
454
		else
B
Bruce Momjian 已提交
455
			len = piece - p;
456

457 458 459
		piece = palloc(len + 1);
		strncpy(piece, p, len);
		piece[len] = '\0';
460

461
		mangled = substitute_libpath_macro(piece);
462
		pfree(piece);
463

464 465
		canonicalize_path(mangled);

466
		/* only absolute paths */
467
		if (!is_absolute_path(mangled))
468 469
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_NAME),
B
Bruce Momjian 已提交
470
					 errmsg("component in parameter \"dynamic_library_path\" is not an absolute path")));
471

472 473
		full = palloc(strlen(mangled) + 1 + baselen + 1);
		sprintf(full, "%s/%s", mangled, basename);
474
		pfree(mangled);
475

476
		elog(DEBUG3, "find_in_dynamic_libpath: trying \"%s\"", full);
477 478 479 480 481

		if (file_exists(full))
			return full;

		pfree(full);
482

483 484 485 486
		if (p[len] == '\0')
			break;
		else
			p += len + 1;
487
	}
488 489 490

	return NULL;
}