@@ -171,9 +171,184 @@ static char *strtrim (char *s, const char *trim)
171
171
return * s ? s : NULL ;
172
172
}
173
173
174
+ /* Return local URI from either FLUX_URI environment variable, or
175
+ * if that is not set, the builtin config. Caller must free result.
176
+ */
177
+ static char * get_local_uri (void )
178
+ {
179
+ const char * uri ;
180
+ char * result = NULL ;
181
+
182
+ if ((uri = getenv ("FLUX_URI" )))
183
+ return strdup (uri );
184
+ if (asprintf (& result ,
185
+ "local://%s/local" ,
186
+ flux_conf_builtin_get ("rundir" , FLUX_CONF_INSTALLED )) < 0 )
187
+ result = NULL ;
188
+ return result ;
189
+ }
190
+
191
+ /* Return the current instance level as an integer using the
192
+ * instance-level broker attribute.
193
+ */
194
+ static int get_instance_level (flux_t * h )
195
+ {
196
+ long l ;
197
+ char * endptr ;
198
+ const char * level ;
199
+
200
+ if (!(level = flux_attr_get (h , "instance-level" )))
201
+ return -1 ;
202
+
203
+ errno = 0 ;
204
+ l = strtol (level , & endptr , 10 );
205
+ if (errno != 0 || * endptr != '\0' ) {
206
+ errno = EINVAL ;
207
+ return -1 ;
208
+ }
209
+ if (l < 0 || l > INT_MAX ) {
210
+ errno = ERANGE ;
211
+ return -1 ;
212
+ }
213
+ return (int ) l ;
214
+ }
215
+
216
+ /* Resolve the URI of an ancestor `n` levels up the hierarchy.
217
+ * If n is 0, then the current default uri (FLUX_URI or builtin) is returned.
218
+ * If n >= instance-level, then the URI of the root instance is returned.
219
+ */
220
+ static char * resolve_ancestor_uri (flux_t * h , int n , flux_error_t * errp )
221
+ {
222
+ int level ;
223
+ int depth = 0 ;
224
+ const char * uri = NULL ;
225
+ char * result = NULL ;
226
+
227
+ if ((level = get_instance_level (h )) < 0 ) {
228
+ errprintf (errp ,
229
+ "Failed to get instance-level attribute: %s" ,
230
+ strerror (errno ));
231
+ return NULL ;
232
+ }
233
+
234
+ /* If n is 0 (resolve current uri) or level is 0 (we're already
235
+ * in the root instance), return copy of local URI immediately:
236
+ */
237
+ if (n == 0 || level == 0 ) {
238
+ if (!(result = get_local_uri ())) {
239
+ errprintf (errp , "Failed to get local URI" );
240
+ return NULL ;
241
+ }
242
+ return result ;
243
+ }
244
+
245
+ /* Resolve to depth 0 unless n is > 0 (but ensure depth not < 0)
246
+ */
247
+ if (n > 0 && (depth = level - n ) < 0 )
248
+ depth = 0 ;
249
+
250
+ /* Take a reference on h since it will be closed below
251
+ */
252
+ flux_incref (h );
253
+
254
+ while (!result ) {
255
+ flux_t * parent_h ;
256
+
257
+ if (!(uri = flux_attr_get (h , "parent-uri" ))
258
+ || !(parent_h = flux_open_ex (uri , 0 , errp )))
259
+ goto out ;
260
+
261
+ if (-- level == depth ) {
262
+ if (!(result = strdup (uri ))) {
263
+ errprintf (errp , "Out of memory" );
264
+ goto out ;
265
+ }
266
+ }
267
+ flux_close (h );
268
+ h = parent_h ;
269
+ }
270
+ out :
271
+ flux_close (h );
272
+ return result ;
273
+ }
274
+
275
+ /* Count the number of parent elements ".." separated by one or more "/"
276
+ * in `path`. It is an error if anything but ".." or "." appears in each
277
+ * element ("." is ignored and indicates "current instance").
278
+ *
279
+ * `path` should have already been checked for a leading slash (an error).
280
+ */
281
+ static int count_parents (const char * path , flux_error_t * errp )
282
+ {
283
+ char * copy ;
284
+ char * str ;
285
+ char * s ;
286
+ char * sp = NULL ;
287
+ int n = 0 ;
288
+ int rc = -1 ;
289
+
290
+ if (!(copy = strdup (path ))) {
291
+ errprintf (errp , "Out of memory" );
292
+ return -1 ;
293
+ }
294
+ str = copy ;
295
+ while ((s = strtok_r (str , "/" , & sp ))) {
296
+ if (streq (s , ".." ))
297
+ n ++ ;
298
+ else if (!streq (s , "." )) {
299
+ errprintf (errp , "%s: invalid URI path element '%s'" , path , s );
300
+ errno = EINVAL ;
301
+ goto out ;
302
+ }
303
+ str = NULL ;
304
+ }
305
+ rc = n ;
306
+ out :
307
+ ERRNO_SAFE_WRAP (free , copy );
308
+ return rc ;
309
+ }
310
+
311
+ /* Resolve a path-like string where one or more ".." separated by "/" specify
312
+ * to resolve parent instance URIs (possibly multiple levels up), "."
313
+ * indicates the current instance, and a single slash ("/") specifies the
314
+ * root instance URI.
315
+ */
316
+ static char * resolve_path_uri (const char * path , flux_error_t * errp )
317
+ {
318
+ char * uri = NULL ;
319
+ int nparents ;
320
+ flux_t * h ;
321
+
322
+ /* Always start from current enclosing instance
323
+ */
324
+ if (!(h = flux_open_ex (NULL , 0 , errp )))
325
+ return NULL ;
326
+
327
+ if (path [0 ] == '/' ) {
328
+ /* Leading slash only allowed if it is the only character in the
329
+ * uri path string:
330
+ */
331
+ if (!streq (path , "/" )) {
332
+ errprintf (errp , "%s: invalid URI" , path );
333
+ errno = EINVAL ;
334
+ goto out ;
335
+ }
336
+ /* nparents < 0 means resolve to root
337
+ */
338
+ nparents = -1 ;
339
+ }
340
+ else if ((nparents = count_parents (path , errp )) < 0 )
341
+ goto out ;
342
+
343
+ uri = resolve_ancestor_uri (h , nparents , errp );
344
+ out :
345
+ flux_close (h );
346
+ return uri ;
347
+ }
348
+
174
349
flux_t * flux_open_ex (const char * uri , int flags , flux_error_t * errp )
175
350
{
176
- char * default_uri = NULL ;
351
+ char * tmp = NULL ;
177
352
char * path = NULL ;
178
353
char * scheme = NULL ;
179
354
void * dso = NULL ;
@@ -197,15 +372,20 @@ flux_t *flux_open_ex (const char *uri, int flags, flux_error_t *errp)
197
372
198
373
/* Try to get URI from (in descending precedence):
199
374
* argument > environment > builtin
375
+ *
376
+ * If supplied argument starts with "." or "/", then treat it as
377
+ * a path-like argument which may reference an ancestor (as processed
378
+ * by resolve_path_uri())
200
379
*/
201
- if (!uri )
202
- uri = getenv ("FLUX_URI" );
203
380
if (!uri ) {
204
- if (asprintf (& default_uri , "local://%s/local" ,
205
- flux_conf_builtin_get ("rundir" ,
206
- FLUX_CONF_INSTALLED )) < 0 )
381
+ if (!(tmp = get_local_uri ()))
382
+ goto error ;
383
+ uri = tmp ;
384
+ }
385
+ else if (uri [0 ] == '.' || uri [0 ] == '/' ) {
386
+ if (!(tmp = resolve_path_uri (uri , errp )))
207
387
goto error ;
208
- uri = default_uri ;
388
+ uri = tmp ;
209
389
}
210
390
if (!(scheme = strdup (uri )))
211
391
goto error ;
@@ -244,13 +424,13 @@ flux_t *flux_open_ex (const char *uri, int flags, flux_error_t *errp)
244
424
goto error_handle ;
245
425
}
246
426
free (scheme );
247
- free (default_uri );
427
+ free (tmp );
248
428
return h ;
249
429
error_handle :
250
430
flux_handle_destroy (h );
251
431
error :
252
432
ERRNO_SAFE_WRAP (free , scheme );
253
- ERRNO_SAFE_WRAP (free , default_uri );
433
+ ERRNO_SAFE_WRAP (free , tmp );
254
434
/*
255
435
* Fill errp->text with strerror only when a more specific error has not
256
436
* already been set.
0 commit comments