]> granicus.if.org Git - postgresql/blob - src/backend/utils/fmgr/dfmgr.c
Add code to allow profiling of backends on Linux: save and restore the
[postgresql] / src / backend / utils / fmgr / dfmgr.c
1 /*-------------------------------------------------------------------------
2  *
3  * dfmgr.c
4  *        Dynamic function manager code.
5  *
6  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/utils/fmgr/dfmgr.c,v 1.54 2001/10/25 05:49:48 momjian Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include <errno.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20
21 #include "dynloader.h"
22 #include "miscadmin.h"
23 #include "utils/dynamic_loader.h"
24
25
26 /*
27  * List of dynamically loaded files (kept in malloc'd memory).
28  */
29
30 typedef struct df_files
31 {
32         struct df_files *next;          /* List link */
33         dev_t           device;                 /* Device file is on */
34         ino_t           inode;                  /* Inode number of file */
35         void       *handle;                     /* a handle for pg_dl* functions */
36         char            filename[1];    /* Full pathname of file */
37
38         /*
39          * we allocate the block big enough for actual length of pathname.
40          * filename[] must be last item in struct!
41          */
42 } DynamicFileList;
43
44 static DynamicFileList *file_list = (DynamicFileList *) NULL;
45 static DynamicFileList *file_tail = (DynamicFileList *) NULL;
46
47 #define SAME_INODE(A,B) ((A).st_ino == (B).inode && (A).st_dev == (B).device)
48
49 char       *Dynamic_library_path;
50
51 static bool file_exists(const char *name);
52 static char *find_in_dynamic_libpath(const char *basename);
53 static char *expand_dynamic_library_name(const char *name);
54 static char *substitute_libpath_macro(const char *name);
55
56 /*
57  * Load the specified dynamic-link library file, and look for a function
58  * named funcname in it.  (funcname can be NULL to just load the file.)
59  *
60  * If the function is not found, we raise an error if signalNotFound is true,
61  * else return (PGFunction) NULL.  Note that errors in loading the library
62  * will provoke elog regardless of signalNotFound.
63  *
64  * If filehandle is not NULL, then *filehandle will be set to a handle
65  * identifying the library file.  The filehandle can be used with
66  * lookup_external_function to lookup additional functions in the same file
67  * at less cost than repeating load_external_function.
68   */
69 PGFunction
70 load_external_function(char *filename, char *funcname,
71                                            bool signalNotFound, void **filehandle)
72 {
73         DynamicFileList *file_scanner;
74         PGFunction      retval;
75         char       *load_error;
76         struct stat stat_buf;
77         char       *fullname;
78
79         fullname = expand_dynamic_library_name(filename);
80         if (!fullname)
81                 fullname = pstrdup(filename);
82         /* at this point fullname is always freshly palloc'd */
83
84         /*
85          * Scan the list of loaded FILES to see if the file has been loaded.
86          */
87         for (file_scanner = file_list;
88                  file_scanner != (DynamicFileList *) NULL &&
89                  strcmp(fullname, file_scanner->filename) != 0;
90                  file_scanner = file_scanner->next)
91                 ;
92         if (file_scanner == (DynamicFileList *) NULL)
93         {
94                 /*
95                  * Check for same files - different paths (ie, symlink or link)
96                  */
97                 if (stat(fullname, &stat_buf) == -1)
98                         elog(ERROR, "stat failed on file '%s': %m", fullname);
99
100                 for (file_scanner = file_list;
101                          file_scanner != (DynamicFileList *) NULL &&
102                          !SAME_INODE(stat_buf, *file_scanner);
103                          file_scanner = file_scanner->next)
104                         ;
105         }
106
107         if (file_scanner == (DynamicFileList *) NULL)
108         {
109                 /*
110                  * File not loaded yet.
111                  */
112                 file_scanner = (DynamicFileList *)
113                         malloc(sizeof(DynamicFileList) + strlen(fullname));
114                 if (file_scanner == NULL)
115                         elog(ERROR, "Out of memory in load_external_function");
116
117                 MemSet((char *) file_scanner, 0, sizeof(DynamicFileList));
118                 strcpy(file_scanner->filename, fullname);
119                 file_scanner->device = stat_buf.st_dev;
120                 file_scanner->inode = stat_buf.st_ino;
121                 file_scanner->next = (DynamicFileList *) NULL;
122
123                 file_scanner->handle = pg_dlopen(fullname);
124                 if (file_scanner->handle == (void *) NULL)
125                 {
126                         load_error = (char *) pg_dlerror();
127                         free((char *) file_scanner);
128                         elog(ERROR, "Load of file %s failed: %s", fullname, load_error);
129                 }
130
131                 /* OK to link it into list */
132                 if (file_list == (DynamicFileList *) NULL)
133                         file_list = file_scanner;
134                 else
135                         file_tail->next = file_scanner;
136                 file_tail = file_scanner;
137         }
138
139         /* Return handle if caller wants it. */
140         if (filehandle)
141                 *filehandle = file_scanner->handle;
142
143         /*
144          * If funcname is NULL, we only wanted to load the file.
145          */
146         if (funcname == (char *) NULL)
147         {
148                 pfree(fullname);
149                 return (PGFunction) NULL;
150         }
151
152         retval = pg_dlsym(file_scanner->handle, funcname);
153
154         if (retval == (PGFunction) NULL && signalNotFound)
155                 elog(ERROR, "Can't find function %s in file %s", funcname, fullname);
156
157         pfree(fullname);
158         return retval;
159 }
160
161 /*
162  * This function loads a shlib file without looking up any particular
163  * function in it.      If the same shlib has previously been loaded,
164  * unload and reload it.
165  */
166 void
167 load_file(char *filename)
168 {
169         DynamicFileList *file_scanner,
170                            *p;
171         struct stat stat_buf;
172         char       *fullname;
173
174         fullname = expand_dynamic_library_name(filename);
175         if (!fullname)
176                 fullname = pstrdup(filename);
177         /* at this point fullname is always freshly palloc'd */
178
179         /*
180          * We need to do stat() in order to determine whether this is the same
181          * file as a previously loaded file; it's also handy so as to give a
182          * good error message if bogus file name given.
183          */
184         if (stat(fullname, &stat_buf) == -1)
185                 elog(ERROR, "LOAD: could not open file '%s': %m", fullname);
186
187         if (file_list != (DynamicFileList *) NULL)
188         {
189                 if (SAME_INODE(stat_buf, *file_list))
190                 {
191                         p = file_list;
192                         file_list = p->next;
193                         pg_dlclose(p->handle);
194                         free((char *) p);
195                 }
196                 else
197                 {
198                         for (file_scanner = file_list;
199                                  file_scanner->next != (DynamicFileList *) NULL;
200                                  file_scanner = file_scanner->next)
201                         {
202                                 if (SAME_INODE(stat_buf, *(file_scanner->next)))
203                                 {
204                                         p = file_scanner->next;
205                                         file_scanner->next = p->next;
206                                         pg_dlclose(p->handle);
207                                         free((char *) p);
208                                         break;
209                                 }
210                         }
211                 }
212         }
213
214         load_external_function(fullname, (char *) NULL, false, (void *) NULL);
215
216         pfree(fullname);
217 }
218
219 /*
220  * Lookup a function whose library file is already loaded.
221  * Return (PGFunction) NULL if not found.
222  */
223 PGFunction
224 lookup_external_function(void *filehandle, char *funcname)
225 {
226         return pg_dlsym(filehandle, funcname);
227 }
228
229
230 static bool
231 file_exists(const char *name)
232 {
233         struct stat st;
234
235         AssertArg(name != NULL);
236
237         if (stat(name, &st) == 0)
238                 return S_ISDIR(st.st_mode) ? false : true;
239         else if (!(errno == ENOENT || errno == ENOTDIR || errno == EACCES))
240                 elog(ERROR, "stat failed on %s: %s", name, strerror(errno));
241
242         return false;
243 }
244
245
246 /* Example format: ".so" */
247 #ifndef DLSUFFIX
248 #error "DLSUFFIX must be defined to compile this file."
249 #endif
250
251 /* Example format: "/usr/local/pgsql/lib" */
252 #ifndef PKGLIBDIR
253 #error "PKGLIBDIR needs to be defined to compile this file."
254 #endif
255
256
257 /*
258  * If name contains a slash, check if the file exists, if so return
259  * the name.  Else (no slash) try to expand using search path (see
260  * find_in_dynamic_libpath below); if that works, return the fully
261  * expanded file name.  If the previous failed, append DLSUFFIX and
262  * try again.  If all fails, return NULL.
263  *
264  * A non-NULL result will always be freshly palloc'd.
265  */
266 static char *
267 expand_dynamic_library_name(const char *name)
268 {
269         bool            have_slash;
270         char       *new;
271         char       *full;
272
273         AssertArg(name);
274
275         have_slash = (strchr(name, '/') != NULL);
276
277         if (!have_slash)
278         {
279                 full = find_in_dynamic_libpath(name);
280                 if (full)
281                         return full;
282         }
283         else
284         {
285                 full = substitute_libpath_macro(name);
286                 if (file_exists(full))
287                         return full;
288                 pfree(full);
289         }
290
291         new = palloc(strlen(name) + strlen(DLSUFFIX) + 1);
292         strcpy(new, name);
293         strcat(new, DLSUFFIX);
294
295         if (!have_slash)
296         {
297                 full = find_in_dynamic_libpath(new);
298                 pfree(new);
299                 if (full)
300                         return full;
301         }
302         else
303         {
304                 full = substitute_libpath_macro(new);
305                 pfree(new);
306                 if (file_exists(full))
307                         return full;
308                 pfree(full);
309         }
310
311         return NULL;
312 }
313
314
315 /*
316  * Substitute for any macros appearing in the given string.
317  * Result is always freshly palloc'd.
318  */
319 static char *
320 substitute_libpath_macro(const char *name)
321 {
322         size_t          macroname_len;
323         char       *replacement = NULL;
324
325         AssertArg(name != NULL);
326
327         if (name[0] != '$')
328                 return pstrdup(name);
329
330         macroname_len = strcspn(name + 1, "/") + 1;
331
332         if (strncmp(name, "$libdir", macroname_len) == 0)
333                 replacement = PKGLIBDIR;
334         else
335                 elog(ERROR, "invalid macro name in dynamic library path");
336
337         if (name[macroname_len] == '\0')
338                 return pstrdup(replacement);
339         else
340         {
341                 char       *new;
342
343                 new = palloc(strlen(replacement) + (strlen(name) - macroname_len) + 1);
344
345                 strcpy(new, replacement);
346                 strcat(new, name + macroname_len);
347
348                 return new;
349         }
350 }
351
352
353 /*
354  * Search for a file called 'basename' in the colon-separated search
355  * path Dynamic_library_path.  If the file is found, the full file name
356  * is returned in freshly palloc'd memory.  If the file is not found,
357  * return NULL.
358  */
359 static char *
360 find_in_dynamic_libpath(const char *basename)
361 {
362         const char *p;
363         size_t          baselen;
364
365         AssertArg(basename != NULL);
366         AssertArg(strchr(basename, '/') == NULL);
367         AssertState(Dynamic_library_path != NULL);
368
369         p = Dynamic_library_path;
370         if (strlen(p) == 0)
371                 return NULL;
372
373         baselen = strlen(basename);
374
375         for (;;)
376         {
377                 size_t          len;
378                 char       *piece;
379                 char       *mangled;
380                 char       *full;
381
382                 len = strcspn(p, ":");
383
384                 if (len == 0)
385                         elog(ERROR, "zero length dynamic_library_path component");
386
387                 piece = palloc(len + 1);
388                 strncpy(piece, p, len);
389                 piece[len] = '\0';
390
391                 mangled = substitute_libpath_macro(piece);
392                 pfree(piece);
393
394                 /* only absolute paths */
395                 if (mangled[0] != '/')
396                         elog(ERROR, "dynamic_library_path component is not absolute");
397
398                 full = palloc(strlen(mangled) + 1 + baselen + 1);
399                 sprintf(full, "%s/%s", mangled, basename);
400                 pfree(mangled);
401
402                 if (DebugLvl > 1)
403                         elog(DEBUG, "find_in_dynamic_libpath: trying %s", full);
404
405                 if (file_exists(full))
406                         return full;
407
408                 pfree(full);
409
410                 if (p[len] == '\0')
411                         break;
412                 else
413                         p += len + 1;
414         }
415
416         return NULL;
417 }