1 /*
2 * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
3 * University Research and Technology
4 * Corporation. All rights reserved.
5 * Copyright (c) 2004-2007 The University of Tennessee and The University
6 * of Tennessee Research Foundation. All rights
7 * reserved.
8 * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
9 * University of Stuttgart. All rights reserved.
10 * Copyright (c) 2004-2005 The Regents of the University of California.
11 * All rights reserved.
12 * Copyright (c) 2009-2014 Cisco Systems, Inc. All rights reserved.
13 * Copyright (c) 2010 IBM Corporation. All rights reserved.
14 * Copyright (c) 2012-2013 Los Alamos National Security, LLC.
15 * All rights reserved.
16 * Copyright (c) 2014 Intel, Inc. All rights reserved.
17 * Copyright (c) 2016 University of Houston. All rights reserved.
18 * Copyright (c) 2016 Research Organization for Information Science
19 * and Technology (RIST). All rights reserved.
20 * $COPYRIGHT$
21 *
22 * Additional copyrights may follow
23 *
24 * $HEADER$
25 */
26
27 #include "opal_config.h"
28 #include <stdlib.h>
29 #include <string.h>
30 #include <errno.h>
31 #ifdef HAVE_UNISTD_H
32 #include <unistd.h>
33 #endif
34 #ifdef HAVE_SHLWAPI_H
35 #include <shlwapi.h>
36 #endif
37 #ifdef HAVE_SYS_PARAM_H
38 #include <sys/param.h>
39 #endif
40 #ifdef HAVE_SYS_MOUNT_H
41 #include <sys/mount.h>
42 #endif
43 #ifdef HAVE_SYS_TYPES_H
44 #include <sys/types.h>
45 #endif
46 #ifdef HAVE_SYS_STAT_H
47 #include <sys/stat.h>
48 #endif
49 #ifdef HAVE_SYS_VFS_H
50 #include <sys/vfs.h>
51 #endif
52 #ifdef HAVE_SYS_STATFS_H
53 #include <sys/statfs.h>
54 #endif
55 #ifdef HAVE_SYS_STATVFS_H
56 #include <sys/statvfs.h>
57 #endif
58 #ifdef HAVE_SYS_MOUNT_H
59 #include <sys/mount.h>
60 #endif
61 #ifdef HAVE_MNTENT_H
62 #include <mntent.h>
63 #endif
64 #ifdef HAVE_PATHS_H
65 #include <paths.h>
66 #endif
67
68 #ifdef _PATH_MOUNTED
69 #define MOUNTED_FILE _PATH_MOUNTED
70 #else
71 #define MOUNTED_FILE "/etc/mtab"
72 #endif
73
74
75 #include "opal_stdint.h"
76 #include "opal/util/output.h"
77 #include "opal/util/path.h"
78 #include "opal/util/os_path.h"
79 #include "opal/util/argv.h"
80
81 /*
82 * Sanity check to ensure we have either statfs or statvfs
83 */
84 #if !defined(HAVE_STATFS) && !defined(HAVE_STATVFS)
85 #error Must have either statfs() or statvfs()
86 #endif
87
88 /*
89 * Note that some OS's (e.g., NetBSD and Solaris) have statfs(), but
90 * no struct statfs (!). So check to make sure we have struct statfs
91 * before allowing the use of statfs().
92 */
93 #if defined(HAVE_STATFS) && (defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) || \
94 defined(HAVE_STRUCT_STATFS_F_TYPE))
95 #define USE_STATFS 1
96 #endif
97
98 static void path_env_load(char *path, int *pargc, char ***pargv);
99 static char *list_env_get(char *var, char **list);
100
opal_path_is_absolute(const char * path)101 bool opal_path_is_absolute( const char *path )
102 {
103 if( OPAL_PATH_SEP[0] == *path ) {
104 return true;
105 }
106 return false;
107 }
108
109 /**
110 * Locates a file with certain permissions
111 */
opal_path_find(char * fname,char ** pathv,int mode,char ** envv)112 char *opal_path_find(char *fname, char **pathv, int mode, char **envv)
113 {
114 char *fullpath;
115 char *delimit;
116 char *env;
117 char *pfix;
118 int i;
119
120 /* If absolute path is given, return it without searching. */
121 if( opal_path_is_absolute(fname) ) {
122 return opal_path_access(fname, NULL, mode);
123 }
124
125 /* Initialize. */
126
127 fullpath = NULL;
128 i = 0;
129
130 /* Consider each directory until the file is found. Thus, the
131 order of directories is important. */
132
133 while (pathv[i] && NULL == fullpath) {
134
135 /* Replace environment variable at the head of the string. */
136 if ('$' == *pathv[i]) {
137 delimit = strchr(pathv[i], OPAL_PATH_SEP[0]);
138 if (delimit) {
139 *delimit = '\0';
140 }
141 env = list_env_get(pathv[i]+1, envv);
142 if (delimit) {
143 *delimit = OPAL_PATH_SEP[0];
144 }
145 if (NULL != env) {
146 if (!delimit) {
147 fullpath = opal_path_access(fname, env, mode);
148 } else {
149 pfix = (char*) malloc(strlen(env) + strlen(delimit) + 1);
150 if (NULL == pfix) {
151 return NULL;
152 }
153 strcpy(pfix, env);
154 strcat(pfix, delimit);
155 fullpath = opal_path_access(fname, pfix, mode);
156 free(pfix);
157 }
158 }
159 }
160 else {
161 fullpath = opal_path_access(fname, pathv[i], mode);
162 }
163 i++;
164 }
165 return opal_make_filename_os_friendly(fullpath);
166 }
167
168 /*
169 * Locates a file with certain permissions from a list of search paths
170 */
opal_path_findv(char * fname,int mode,char ** envv,char * wrkdir)171 char *opal_path_findv(char *fname, int mode, char **envv, char *wrkdir)
172 {
173 char **dirv;
174 char *fullpath;
175 char *path;
176 int dirc;
177 int i;
178 bool found_dot = false;
179
180 /* Set the local search paths. */
181
182 dirc = 0;
183 dirv = NULL;
184
185 if (NULL != (path = list_env_get("PATH", envv))) {
186 path_env_load(path, &dirc, &dirv);
187 }
188
189 /* Replace the "." path by the working directory. */
190
191 if (NULL != wrkdir) {
192 for (i = 0; i < dirc; ++i) {
193 if (0 == strcmp(dirv[i], ".")) {
194 found_dot = true;
195 free(dirv[i]);
196 dirv[i] = strdup(wrkdir);
197 if (NULL == dirv[i]){
198 return NULL;
199 }
200 }
201 }
202 }
203
204 /* If we didn't find "." in the path and we have a wrkdir, append
205 the wrkdir to the end of the path */
206
207 if (!found_dot && NULL != wrkdir) {
208 opal_argv_append(&dirc, &dirv, wrkdir);
209 }
210
211 if(NULL == dirv)
212 return NULL;
213 fullpath = opal_path_find(fname, dirv, mode, envv);
214 opal_argv_free(dirv);
215 return fullpath;
216 }
217
218
219 /**
220 * Forms a complete pathname and checks it for existance and
221 * permissions
222 *
223 * Accepts:
224 * -fname File name
225 * -path Path prefix
226 * -mode Target permissions which must be satisfied
227 *
228 * Returns:
229 * -Full pathname of located file Success
230 * -NULL Failure
231 */
opal_path_access(char * fname,char * path,int mode)232 char *opal_path_access(char *fname, char *path, int mode)
233 {
234 char *fullpath = NULL;
235 struct stat buf;
236 bool relative;
237
238 /* Allocate space for the full pathname. */
239 if (NULL == path) {
240 fullpath = opal_os_path(false, fname, NULL);
241 } else {
242 relative = !opal_path_is_absolute(path);
243 fullpath = opal_os_path(relative, path, fname, NULL);
244 }
245 if (NULL == fullpath) {
246 return NULL;
247 }
248 /* first check to see - is this a file or a directory? We
249 * only want files
250 */
251 if (0 != stat(fullpath, &buf)) {
252 /* couldn't stat the path - obviously, this also meets the
253 * existence check, if that was requested
254 */
255 free(fullpath);
256 return NULL;
257 }
258
259 if (!(S_IFREG & buf.st_mode) &&
260 !(S_IFLNK & buf.st_mode)) {
261 /* this isn't a regular file or a symbolic link, so
262 * ignore it
263 */
264 free(fullpath);
265 return NULL;
266 }
267
268 /* check the permissions */
269 if ((X_OK & mode) && !(S_IXUSR & buf.st_mode)) {
270 /* if they asked us to check executable permission,
271 * and that isn't set, then return NULL
272 */
273 free(fullpath);
274 return NULL;
275 }
276 if ((R_OK & mode) && !(S_IRUSR & buf.st_mode)) {
277 /* if they asked us to check read permission,
278 * and that isn't set, then return NULL
279 */
280 free(fullpath);
281 return NULL;
282 }
283 if ((W_OK & mode) && !(S_IWUSR & buf.st_mode)) {
284 /* if they asked us to check write permission,
285 * and that isn't set, then return NULL
286 */
287 free(fullpath);
288 return NULL;
289 }
290
291 /* must have met all criteria! */
292 return fullpath;
293 }
294
295
296 /**
297 *
298 * Loads argument array with $PATH env var.
299 *
300 * Accepts
301 * -path String contiaing the $PATH
302 * -argc Pointer to argc
303 * -argv Pointer to list of argv
304 */
path_env_load(char * path,int * pargc,char *** pargv)305 static void path_env_load(char *path, int *pargc, char ***pargv)
306 {
307 char *p;
308 char saved;
309
310 if (NULL == path) {
311 *pargc = 0;
312 return;
313 }
314
315 /* Loop through the paths (delimited by PATHENVSEP), adding each
316 one to argv. */
317
318 while ('\0' != *path) {
319
320 /* Locate the delimiter. */
321
322 for (p = path; *p && (*p != OPAL_ENV_SEP); ++p) {
323 continue;
324 }
325
326 /* Add the path. */
327
328 if (p != path) {
329 saved = *p;
330 *p = '\0';
331 opal_argv_append(pargc, pargv, path);
332 *p = saved;
333 path = p;
334 }
335
336 /* Skip past the delimiter, if present. */
337
338 if (*path) {
339 ++path;
340 }
341 }
342 }
343
344
345 /**
346 * Gets value of variable in list or environment. Looks in the list first
347 *
348 * Accepts:
349 * -var String variable
350 * -list Pointer to environment list
351 *
352 * Returns:
353 * -List Pointer to environment list Success
354 * -NULL Failure
355 */
list_env_get(char * var,char ** list)356 static char *list_env_get(char *var, char **list)
357 {
358 size_t n;
359
360 if (NULL != list) {
361 n = strlen(var);
362
363 while (NULL != *list) {
364 if ((0 == strncmp(var, *list, n)) && ('=' == (*list)[n])) {
365 return (*list + n + 1);
366 }
367 ++list;
368 }
369 }
370 return getenv(var);
371 }
372
373 /**
374 * Try to figure out the absolute path based on the application name
375 * (usually argv[0]). If the path is already absolute return a copy, if
376 * it start with . look into the current directory, if not dig into
377 * the $PATH.
378 * In case of error or if executable was not found (as an example if
379 * the application did a cwd between the start and this call), the
380 * function will return NULL. Otherwise, an newly allocated string
381 * will be returned.
382 */
opal_find_absolute_path(char * app_name)383 char* opal_find_absolute_path( char* app_name )
384 {
385 char* abs_app_name;
386 char cwd[OPAL_PATH_MAX], *pcwd;
387
388 if( opal_path_is_absolute(app_name) ) { /* already absolute path */
389 abs_app_name = app_name;
390 } else if ( '.' == app_name[0] ||
391 NULL != strchr(app_name, OPAL_PATH_SEP[0])) {
392 /* the app is in the current directory or below it */
393 pcwd = getcwd( cwd, OPAL_PATH_MAX );
394 if( NULL == pcwd ) {
395 /* too bad there is no way we can get the app absolute name */
396 return NULL;
397 }
398 abs_app_name = opal_os_path( false, pcwd, app_name, NULL );
399 } else {
400 /* Otherwise try to search for the application in the PATH ... */
401 abs_app_name = opal_path_findv( app_name, X_OK, NULL, NULL );
402 }
403
404 if( NULL != abs_app_name ) {
405 char* resolved_path = (char*)malloc(OPAL_PATH_MAX);
406 realpath( abs_app_name, resolved_path );
407 if( abs_app_name != app_name ) free(abs_app_name);
408 return resolved_path;
409 }
410 return NULL;
411 }
412
413 /**
414 * Read real FS type from /etc/mtab, needed to translate autofs fs type into real fs type
415 * TODO: solaris? OSX?
416 * Limitations: autofs on solaris/osx will be assumed as "nfs" type
417 */
418
opal_check_mtab(char * dev_path)419 static char *opal_check_mtab(char *dev_path)
420 {
421
422 #ifdef HAVE_MNTENT_H
423 FILE * mtab = NULL;
424 struct mntent * part = NULL;
425
426 if ((mtab = setmntent(MOUNTED_FILE, "r")) != NULL) {
427 while (NULL != (part = getmntent(mtab))) {
428 if ((NULL != part->mnt_dir) &&
429 (NULL != part->mnt_type) &&
430 (0 == strcmp(part->mnt_dir, dev_path)))
431 {
432 endmntent(mtab);
433 return strdup(part->mnt_type);
434 }
435 }
436 endmntent(mtab);
437 }
438 #endif
439 return NULL;
440 }
441
442
443 /**
444 * @brief Figure out, whether fname is on network file system
445 *
446 * Try to figure out, whether the file name specified through fname is
447 * on any network file system (currently NFS, Lustre, Panasas and GPFS).
448 *
449 * If the file is not created, the parent directory is checked.
450 * This allows checking for NFS prior to opening the file.
451 *
452 * @fname[in] File name to check
453 * @fstype[out] File system type if retval is true
454 *
455 * @retval true If fname is on NFS, Lustre, Panasas or GPFS
456 * @retval false otherwise
457 *
458 *
459 * Linux:
460 * statfs(const char *path, struct statfs *buf);
461 * with fsid_t f_fsid; (in kernel struct{ int val[2] };)
462 * return 0 success, -1 on failure with errno set.
463 * statvfs (const char *path, struct statvfs *buf);
464 * with unsigned long f_fsid; -- returns wrong info
465 * return 0 success, -1 on failure with errno set.
466 * Solaris:
467 * statvfs (const char *path, struct statvfs *buf);
468 * with f_basetype, contains a string of length FSTYPSZ
469 * return 0 success, -1 on failure with errno set.
470 * FreeBSD:
471 * statfs(const char *path, struct statfs *buf);
472 * with f_fstypename, contains a string of length MFSNAMELEN
473 * return 0 success, -1 on failure with errno set.
474 * compliant with: 4.4BSD.
475 * NetBSD:
476 * statvfs (const char *path, struct statvfs *buf);
477 * with f_fstypename, contains a string of length VFS_NAMELEN
478 * return 0 success, -1 on failure with errno set.
479 * Mac OSX (10.6.2 through 10.9):
480 * statvfs(const char * restrict path, struct statvfs * restrict buf);
481 * with fsid Not meaningful in this implementation.
482 * is just a wrapper around statfs()
483 * statfs(const char *path, struct statfs *buf);
484 * with f_fstypename, contains a string of length MFSTYPENAMELEN
485 * return 0 success, -1 on failure with errno set.
486 */
487 #ifndef LL_SUPER_MAGIC
488 #define LL_SUPER_MAGIC 0x0BD00BD0 /* Lustre magic number */
489 #endif
490 #ifndef NFS_SUPER_MAGIC
491 #define NFS_SUPER_MAGIC 0x6969
492 #endif
493 #ifndef PAN_KERNEL_FS_CLIENT_SUPER_MAGIC
494 #define PAN_KERNEL_FS_CLIENT_SUPER_MAGIC 0xAAD7AAEA /* Panasas FS */
495 #endif
496 #ifndef GPFS_SUPER_MAGIC
497 #define GPFS_SUPER_MAGIC 0x47504653 /* Thats GPFS in ASCII */
498 #endif
499 #ifndef AUTOFS_SUPER_MAGIC
500 #define AUTOFS_SUPER_MAGIC 0x0187
501 #endif
502 #ifndef PVFS2_SUPER_MAGIC
503 #define PVFS2_SUPER_MAGIC 0x20030528
504 #endif
505
506 #define MASK2 0xffff
507 #define MASK4 0xffffffff
508
opal_path_nfs(char * fname,char ** ret_fstype)509 bool opal_path_nfs(char *fname, char **ret_fstype)
510 {
511 int i;
512 int fsrc = -1;
513 int vfsrc = -1;
514 int trials;
515 char * file = strdup (fname);
516 #if defined(USE_STATFS)
517 struct statfs fsbuf;
518 #endif
519 #if defined(HAVE_STATVFS)
520 struct statvfs vfsbuf;
521 #endif
522 /*
523 * Be sure to update the test (test/util/opal_path_nfs.c)
524 * while adding a new Network/Cluster Filesystem here
525 */
526 static struct fs_types_t {
527 unsigned long long f_fsid;
528 unsigned long long f_mask;
529 const char * f_fsname;
530 } fs_types[] = {
531 {LL_SUPER_MAGIC, MASK4, "lustre"},
532 {NFS_SUPER_MAGIC, MASK2, "nfs"},
533 {AUTOFS_SUPER_MAGIC, MASK2, "autofs"},
534 {PAN_KERNEL_FS_CLIENT_SUPER_MAGIC, MASK4, "panfs"},
535 {GPFS_SUPER_MAGIC, MASK4, "gpfs"},
536 {PVFS2_SUPER_MAGIC, MASK4, "pvfs2"}
537 };
538 #define FS_TYPES_NUM (int)(sizeof (fs_types)/sizeof (fs_types[0]))
539
540 /*
541 * First, get the OS-dependent struct stat(v)fs buf. This may
542 * return the ESTALE error on NFS, if the underlying file/path has
543 * changed.
544 */
545 again:
546 #if defined(USE_STATFS)
547 trials = 5;
548 do {
549 fsrc = statfs(file, &fsbuf);
550 } while (-1 == fsrc && ESTALE == errno && (0 < --trials));
551 #endif
552 #if defined(HAVE_STATVFS)
553 trials = 5;
554 do {
555 vfsrc = statvfs(file, &vfsbuf);
556 } while (-1 == vfsrc && ESTALE == errno && (0 < --trials));
557 #endif
558
559 /* In case some error with the current filename, try the parent
560 directory */
561 if (-1 == fsrc && -1 == vfsrc) {
562 char * last_sep;
563
564 OPAL_OUTPUT_VERBOSE((10, 0, "opal_path_nfs: stat(v)fs on file:%s failed errno:%d directory:%s\n",
565 fname, errno, file));
566 if (EPERM == errno) {
567 free(file);
568 if ( NULL != ret_fstype ) {
569 *ret_fstype = NULL;
570 }
571 return false;
572 }
573
574 last_sep = strrchr(file, OPAL_PATH_SEP[0]);
575 /* Stop the search, when we have searched past root '/' */
576 if (NULL == last_sep || (1 == strlen(last_sep) &&
577 OPAL_PATH_SEP[0] == *last_sep)) {
578 free (file);
579 if ( NULL != ret_fstype ) {
580 *ret_fstype=NULL;
581 }
582 return false;
583 }
584 *last_sep = '\0';
585
586 goto again;
587 }
588
589 /* Next, extract the magic value */
590 for (i = 0; i < FS_TYPES_NUM; i++) {
591 #if defined(USE_STATFS)
592 /* These are uses of struct statfs */
593 # if defined(HAVE_STRUCT_STATFS_F_FSTYPENAME)
594 if (0 == fsrc &&
595 0 == strncasecmp(fs_types[i].f_fsname, fsbuf.f_fstypename,
596 sizeof(fsbuf.f_fstypename))) {
597 goto found;
598 }
599 # endif
600 # if defined(HAVE_STRUCT_STATFS_F_TYPE)
601 if (0 == fsrc &&
602 fs_types[i].f_fsid == (fsbuf.f_type & fs_types[i].f_mask)) {
603 goto found;
604 }
605 # endif
606 #endif
607
608 #if defined(HAVE_STATVFS)
609 /* These are uses of struct statvfs */
610 # if defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
611 if (0 == vfsrc &&
612 0 == strncasecmp(fs_types[i].f_fsname, vfsbuf.f_basetype,
613 sizeof(vfsbuf.f_basetype))) {
614 goto found;
615 }
616 # endif
617 # if defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME)
618 if (0 == vfsrc &&
619 0 == strncasecmp(fs_types[i].f_fsname, vfsbuf.f_fstypename,
620 sizeof(vfsbuf.f_fstypename))) {
621 goto found;
622 }
623 # endif
624 #endif
625 }
626
627 free (file);
628 if ( NULL != ret_fstype ) {
629 *ret_fstype=NULL;
630 }
631 return false;
632
633 found:
634
635 free (file);
636 if (AUTOFS_SUPER_MAGIC == fs_types[i].f_fsid) {
637 char *fs_type = opal_check_mtab(fname);
638 int x;
639 if (NULL != fs_type) {
640 for (x = 0; x < FS_TYPES_NUM; x++) {
641 if (AUTOFS_SUPER_MAGIC == fs_types[x].f_fsid) {
642 continue;
643 }
644 if (0 == strcasecmp(fs_types[x].f_fsname, fs_type)) {
645 OPAL_OUTPUT_VERBOSE((10, 0, "opal_path_nfs: file:%s on fs:%s\n", fname, fs_type));
646 free(fs_type);
647 if ( NULL != ret_fstype ) {
648 *ret_fstype = strdup(fs_types[x].f_fsname);
649 }
650 return true;
651 }
652 }
653 free(fs_type);
654 if ( NULL != ret_fstype ) {
655 *ret_fstype=NULL;
656 }
657 return false;
658 }
659 }
660
661 OPAL_OUTPUT_VERBOSE((10, 0, "opal_path_nfs: file:%s on fs:%s\n",
662 fname, fs_types[i].f_fsname));
663 if ( NULL != ret_fstype ) {
664 *ret_fstype = strdup (fs_types[i].f_fsname);
665 }
666 return true;
667
668 #undef FS_TYPES_NUM
669 }
670
671 int
opal_path_df(const char * path,uint64_t * out_avail)672 opal_path_df(const char *path,
673 uint64_t *out_avail)
674 {
675 int rc = -1;
676 int trials = 5;
677 int err = 0;
678 #if defined(USE_STATFS)
679 struct statfs buf;
680 #elif defined(HAVE_STATVFS)
681 struct statvfs buf;
682 #endif
683
684 if (NULL == path || NULL == out_avail) {
685 return OPAL_ERROR;
686 }
687 *out_avail = 0;
688
689 do {
690 #if defined(USE_STATFS)
691 rc = statfs(path, &buf);
692 #elif defined(HAVE_STATVFS)
693 rc = statvfs(path, &buf);
694 #endif
695 err = errno;
696 } while (-1 == rc && ESTALE == err && (--trials > 0));
697
698 if (-1 == rc) {
699 OPAL_OUTPUT_VERBOSE((10, 2, "opal_path_df: stat(v)fs on "
700 "path: %s failed with errno: %d (%s)\n",
701 path, err, strerror(err)));
702 return OPAL_ERROR;
703 }
704
705 /* now set the amount of free space available on path */
706 /* sometimes buf.f_bavail is negative */
707 *out_avail = (uint64_t)buf.f_bsize * (uint64_t)((long)buf.f_bavail < 0 ? 0 : buf.f_bavail);
708
709 OPAL_OUTPUT_VERBOSE((10, 2, "opal_path_df: stat(v)fs states "
710 "path: %s has %"PRIu64 " B of free space.",
711 path, *out_avail));
712
713 return OPAL_SUCCESS;
714 }
715