/src/netcdf-c/libdispatch/dplugins.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2018, University Corporation for Atmospheric Research |
3 | | * See netcdf/COPYRIGHT file for copying and redistribution conditions. |
4 | | */ |
5 | | |
6 | | /**************************************************/ |
7 | | /* Global state plugin path implementation */ |
8 | | |
9 | | /** |
10 | | * @file |
11 | | * Functions for working with plugins. |
12 | | */ |
13 | | |
14 | | #include "config.h" |
15 | | #include <stdlib.h> |
16 | | #include <stdio.h> |
17 | | #include <string.h> |
18 | | #ifdef _MSC_VER |
19 | | #include <io.h> |
20 | | #endif |
21 | | |
22 | | #include "netcdf.h" |
23 | | #include "netcdf_filter.h" |
24 | | #include "ncdispatch.h" |
25 | | #include "nc4internal.h" |
26 | | #include "nclog.h" |
27 | | #include "ncbytes.h" |
28 | | #include "ncplugins.h" |
29 | | #include "netcdf_aux.h" |
30 | | |
31 | | /* |
32 | | Unified plugin related code |
33 | | */ |
34 | | /**************************************************/ |
35 | | /* Plugin-path API */ |
36 | | |
37 | | /* list of environment variables to check for plugin roots */ |
38 | 1 | #define PLUGIN_ENV "HDF5_PLUGIN_PATH" |
39 | | |
40 | | /* Control path verification */ |
41 | 1 | #define PLUGINPATHVERIFY "NC_PLUGIN_PATH_VERIFY" |
42 | | |
43 | | /*Forward*/ |
44 | | static int buildinitialpluginpath(NCPluginList* dirs); |
45 | | |
46 | | static int NC_plugin_path_initialized = 0; |
47 | | static int NC_plugin_path_verify = 1; |
48 | | |
49 | | /** |
50 | | * This function is called as part of nc_initialize. |
51 | | * Its purpose is to initialize the plugin paths state. |
52 | | * |
53 | | * @return NC_NOERR |
54 | | * |
55 | | * @author Dennis Heimbigner |
56 | | */ |
57 | | |
58 | | EXTERNL int |
59 | | nc_plugin_path_initialize(void) |
60 | 1 | { |
61 | 1 | int stat = NC_NOERR; |
62 | 1 | struct NCglobalstate* gs = NULL; |
63 | 1 | NCPluginList dirs = {0,NULL}; |
64 | | #ifdef USE_HDF5 |
65 | | int hdf5found = 0; /* 1 => we got a legit plugin path set from HDF5 */ |
66 | | #endif |
67 | | |
68 | 1 | if(!NC_initialized) nc_initialize(); |
69 | 1 | if(NC_plugin_path_initialized != 0) goto done; |
70 | 1 | NC_plugin_path_initialized = 1; |
71 | | |
72 | 1 | if(getenv(PLUGINPATHVERIFY) != NULL) NC_plugin_path_verify = 1; |
73 | | |
74 | 1 | gs = NC_getglobalstate(); |
75 | | |
76 | | /** |
77 | | * When the netcdf-c library initializes itself (at runtime), it chooses an |
78 | | * initial global plugin path using the following rules, which are those used |
79 | | * by the HDF5 library, except as modified for plugin install (which HDF5 does not support). |
80 | | */ |
81 | | |
82 | | /* Initialize the implementations */ |
83 | | #ifdef NETCDF_ENABLE_NCZARR_FILTERS |
84 | | if((stat = NCZ_plugin_path_initialize())) goto done; |
85 | | #endif |
86 | | #ifdef USE_HDF5 |
87 | | if((stat = NC4_hdf5_plugin_path_initialize())) goto done; |
88 | | #endif |
89 | | |
90 | | /* Compute the initial global plugin path */ |
91 | 1 | assert(dirs.ndirs == 0 && dirs.dirs == NULL); |
92 | 1 | if((stat = buildinitialpluginpath(&dirs))) goto done; /* Construct a default */ |
93 | | |
94 | | /* Sync to the actual implementations */ |
95 | | #ifdef USE_HDF5 |
96 | | if(!hdf5found) |
97 | | {if((stat = NC4_hdf5_plugin_path_set(&dirs))) goto done;} |
98 | | #endif |
99 | | #ifdef NETCDF_ENABLE_NCZARR_FILTERS |
100 | | if((stat = NCZ_plugin_path_set(&dirs))) goto done; |
101 | | #endif |
102 | | /* Set the global plugin dirs sequence */ |
103 | 1 | assert(gs->pluginpaths == NULL); |
104 | 1 | gs->pluginpaths = nclistnew(); |
105 | 1 | if(dirs.ndirs > 0) { |
106 | 1 | size_t i; |
107 | 1 | char** dst; |
108 | 1 | nclistsetlength(gs->pluginpaths,dirs.ndirs); |
109 | 1 | dst = (char**)nclistcontents(gs->pluginpaths); |
110 | 1 | assert(dst != NULL); |
111 | 3 | for(i=0;i<dirs.ndirs;i++) |
112 | 2 | dst[i] = strdup(dirs.dirs[i]); |
113 | 1 | } |
114 | 1 | done: |
115 | 1 | ncaux_plugin_path_clear(&dirs); |
116 | 1 | return NCTHROW(stat); |
117 | 1 | } |
118 | | |
119 | | /** |
120 | | * This function is called as part of nc_finalize() |
121 | | * Its purpose is to clean-up plugin path state. |
122 | | * |
123 | | * @return NC_NOERR |
124 | | * |
125 | | * @author Dennis Heimbigner |
126 | | */ |
127 | | |
128 | | int |
129 | | nc_plugin_path_finalize(void) |
130 | 1 | { |
131 | 1 | int stat = NC_NOERR; |
132 | 1 | struct NCglobalstate* gs = NC_getglobalstate(); |
133 | | |
134 | 1 | if(NC_plugin_path_initialized == 0) goto done; |
135 | 1 | NC_plugin_path_initialized = 0; |
136 | | |
137 | 1 | NC_plugin_path_verify = 0; |
138 | | |
139 | | /* Finalize the actual implementatios */ |
140 | | #ifdef NETCDF_ENABLE_NCZARR_FILTERS |
141 | | if((stat = NCZ_plugin_path_finalize())) goto done; |
142 | | #endif |
143 | | #ifdef USE_HDF5 |
144 | | if((stat = NC4_hdf5_plugin_path_finalize())) goto done; |
145 | | #endif |
146 | | |
147 | 1 | nclistfreeall(gs->pluginpaths); gs->pluginpaths = NULL; |
148 | 1 | done: |
149 | 1 | return NCTHROW(stat); |
150 | 1 | } |
151 | | |
152 | | /** |
153 | | * Return the length of the current sequence of directories |
154 | | * in the internal global plugin path list. |
155 | | * @param ndirsp length is returned here |
156 | | * @return NC_NOERR | NC_EXXX |
157 | | * |
158 | | * @author Dennis Heimbigner |
159 | | */ |
160 | | |
161 | | int |
162 | | nc_plugin_path_ndirs(size_t* ndirsp) |
163 | 0 | { |
164 | 0 | int stat = NC_NOERR; |
165 | 0 | size_t ndirs = 0; |
166 | 0 | struct NCglobalstate* gs = NC_getglobalstate(); |
167 | |
|
168 | 0 | if(gs->pluginpaths == NULL) gs->pluginpaths = nclistnew(); /* suspenders and belt */ |
169 | 0 | ndirs = nclistlength(gs->pluginpaths); |
170 | | |
171 | | /* Verify that the implementation plugin paths are consistent in length*/ |
172 | 0 | if(NC_plugin_path_verify) { |
173 | | #ifdef NETCDF_ENABLE_HDF5 |
174 | | { |
175 | | size_t ndirs5 = 0; |
176 | | if((stat=NC4_hdf5_plugin_path_ndirs(&ndirs5))) goto done; |
177 | | assert(ndirs5 == ndirs); |
178 | | } |
179 | | #endif /*NETCDF_ENABLE_HDF5*/ |
180 | | #ifdef NETCDF_ENABLE_NCZARR_FILTERS |
181 | | { |
182 | | size_t ndirsz = 0; |
183 | | if((stat=NCZ_plugin_path_ndirs(&ndirsz))) goto done; |
184 | | assert(ndirsz == ndirs); |
185 | | } |
186 | | #endif /*NETCDF_ENABLE_NCZARR_FILTERS*/ |
187 | 0 | } |
188 | 0 | if(ndirsp) *ndirsp = ndirs; |
189 | 0 | done: |
190 | 0 | return NCTHROW(stat); |
191 | 0 | } |
192 | | |
193 | | /** |
194 | | * Return the current sequence of directories in the internal global |
195 | | * plugin path list. Since this function does not modify the plugin path, |
196 | | * it can be called at any time. |
197 | | * @param dirs pointer to an NCPluginList object |
198 | | * @return NC_NOERR | NC_EXXX |
199 | | * @author Dennis Heimbigner |
200 | | * |
201 | | * WARNING: if dirs->dirs is NULL, then space for the directory |
202 | | * vector will be allocated. If not NULL, then the specified space will |
203 | | * be overwritten with the vector. |
204 | | * |
205 | | * @author: Dennis Heimbigner |
206 | | */ |
207 | | |
208 | | int |
209 | | nc_plugin_path_get(NCPluginList* dirs) |
210 | 0 | { |
211 | 0 | int stat = NC_NOERR; |
212 | 0 | struct NCglobalstate* gs = NC_getglobalstate(); |
213 | 0 | size_t i; |
214 | |
|
215 | 0 | if(gs->pluginpaths == NULL) gs->pluginpaths = nclistnew(); /* suspenders and belt */ |
216 | 0 | if(dirs == NULL) goto done; |
217 | 0 | dirs->ndirs = nclistlength(gs->pluginpaths); |
218 | 0 | if(dirs->ndirs > 0 && dirs->dirs == NULL) { |
219 | 0 | if((dirs->dirs = (char**)calloc(dirs->ndirs,sizeof(char*)))==NULL) |
220 | 0 | {stat = NC_ENOMEM; goto done;} |
221 | 0 | } |
222 | 0 | for(i=0;i<dirs->ndirs;i++) { |
223 | 0 | const char* dir = nclistget(gs->pluginpaths,i); |
224 | 0 | dirs->dirs[i] = nulldup(dir); |
225 | 0 | } |
226 | | |
227 | | /* Verify that the implementation plugin paths are consistent */ |
228 | 0 | if(NC_plugin_path_verify) { |
229 | | #ifdef NETCDF_ENABLE_HDF5 |
230 | | { |
231 | | size_t i; |
232 | | NCPluginList l5 = {0,NULL}; |
233 | | if((stat=NC4_hdf5_plugin_path_get(&l5))) goto done; |
234 | | assert(l5.ndirs == nclistlength(gs->pluginpaths)); |
235 | | for(i=0;i<l5.ndirs;i++) { |
236 | | assert(strcmp(dirs->dirs[i],l5.dirs[i])==0); |
237 | | nullfree(l5.dirs[i]); |
238 | | } |
239 | | nullfree(l5.dirs); |
240 | | } |
241 | | #endif /*NETCDF_ENABLE_HDF5*/ |
242 | | #ifdef NETCDF_ENABLE_NCZARR_FILTERS |
243 | | { |
244 | | size_t i; |
245 | | NCPluginList lz = {0,NULL}; |
246 | | if((stat=NCZ_plugin_path_get(&lz))) goto done; |
247 | | assert(lz.ndirs == nclistlength(gs->pluginpaths)); |
248 | | for(i=0;i<lz.ndirs;i++) { |
249 | | assert(strcmp(dirs->dirs[i],lz.dirs[i])==0); |
250 | | nullfree(lz.dirs[i]); |
251 | | } |
252 | | nullfree(lz.dirs); |
253 | | } |
254 | | #endif /*NETCDF_ENABLE_NCZARR_FILTERS*/ |
255 | 0 | } |
256 | 0 | done: |
257 | 0 | return NCTHROW(stat); |
258 | 0 | } |
259 | | |
260 | | /** |
261 | | * Empty the current internal path sequence |
262 | | * and replace with the sequence of directories argument. |
263 | | * Using a dirs->ndirs argument of 0 will clear the set of plugin dirs. |
264 | | * |
265 | | * @param dirs to overwrite the current internal dir list |
266 | | * @return NC_NOERR | NC_EXXX |
267 | | * |
268 | | * @author Dennis Heimbigner |
269 | | */ |
270 | | int |
271 | | nc_plugin_path_set(NCPluginList* dirs) |
272 | 0 | { |
273 | 0 | int stat = NC_NOERR; |
274 | 0 | struct NCglobalstate* gs = NC_getglobalstate(); |
275 | |
|
276 | 0 | if(dirs == NULL) {stat = NC_EINVAL; goto done;} |
277 | | |
278 | | /* Clear the current dir list */ |
279 | 0 | nclistfreeall(gs->pluginpaths); |
280 | 0 | gs->pluginpaths = nclistnew(); |
281 | |
|
282 | 0 | if(dirs->ndirs > 0) { |
283 | 0 | size_t i; |
284 | 0 | assert(gs->pluginpaths != NULL); |
285 | 0 | for(i=0;i<dirs->ndirs;i++) { |
286 | 0 | nclistpush(gs->pluginpaths,nulldup(dirs->dirs[i])); |
287 | 0 | } |
288 | 0 | } |
289 | | |
290 | | /* Sync the global plugin path set to the individual implementations */ |
291 | | #ifdef NETCDF_ENABLE_HDF5 |
292 | | if((stat = NC4_hdf5_plugin_path_set(dirs))) goto done; |
293 | | #endif |
294 | | #ifdef NETCDF_ENABLE_NCZARR_FILTERS |
295 | | if((stat = NCZ_plugin_path_set(dirs))) goto done; |
296 | | #endif |
297 | |
|
298 | 0 | done: |
299 | 0 | return NCTHROW(stat); |
300 | 0 | } |
301 | | |
302 | | /** |
303 | | * When the netcdf-c library initializes itself (at runtime), it chooses an |
304 | | * initial global plugin path using the following rules, which are those used |
305 | | * by the HDF5 library, except as modified for plugin install (which HDF5 does not support). |
306 | | * |
307 | | * Note: In the following, PLATFORMPATH is: |
308 | | * -- /usr/local/lhdf5/lib/plugin if platform is *nix* |
309 | | * -- %ALLUSERSPROFILE%/hdf5/lib/plugin if platform is Windows or Mingw |
310 | | * and in the following, PLATFORMSEP is: |
311 | | * -- ":" if platform is *nix* |
312 | | * -- ";" if platform is Windows or Mingw |
313 | | * and |
314 | | * NETCDF_PLUGIN_SEARCH_PATH is the value constructed at build-time (see configure.ac) |
315 | | * |
316 | | * Table showing the computation of the initial global plugin path |
317 | | * ================================================= |
318 | | * | HDF5_PLUGIN_PATH | Initial global plugin path | |
319 | | * ================================================= |
320 | | * | undefined | NETCDF_PLUGIN_SEARCH_PATH | |
321 | | * ------------------------------------------------- |
322 | | * | <path1;...pathn> | <path1;...pathn> | |
323 | | * ------------------------------------------------- |
324 | | */ |
325 | | static int |
326 | | buildinitialpluginpath(NCPluginList* dirs) |
327 | 1 | { |
328 | 1 | int stat = NC_NOERR; |
329 | 1 | const char* hdf5path = NULL; |
330 | | /* Find the plugin directory root(s) */ |
331 | 1 | hdf5path = getenv(PLUGIN_ENV); /* HDF5_PLUGIN_PATH */ |
332 | 1 | if(hdf5path == NULL) { |
333 | | /* Use NETCDF_PLUGIN_SEARCH_PATH */ |
334 | 1 | hdf5path = NETCDF_PLUGIN_SEARCH_PATH; |
335 | 1 | } |
336 | 1 | if((stat = ncaux_plugin_path_parse(hdf5path,'\0',dirs))) goto done; |
337 | | |
338 | 1 | done: |
339 | 1 | return stat; |
340 | 1 | } |