libutil - add getdevpath()
authorMatthew Dillon <dillon@apollo.backplane.com>
Fri, 10 Jul 2009 20:56:56 +0000 (13:56 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Fri, 10 Jul 2009 20:56:56 +0000 (13:56 -0700)
Add the getdevpath() function which translates a device name such as "da0",
or label such as "fubar", into a device path.  This function searches
/usr/local/etc/devtab, /etc/devtab, and /etc/defaults/devtab for labels.
Device names beginning with '/' or '.' are assumed to be paths and passed
through.

lib/libutil/Makefile
lib/libutil/getdevpath.3 [new file with mode: 0644]
lib/libutil/getdevpath.c [new file with mode: 0644]
lib/libutil/libutil.h

index 9f8db8e..9d033c5 100644 (file)
@@ -10,7 +10,8 @@ SRCS= flopen.c login.c login_tty.c logout.c logwtmp.c pty.c \
        login_cap.c login_class.c login_auth.c login_times.c login_ok.c \
        login_crypt.c _secure_path.c uucplock.c property.c auth.c \
        realhostname.c fparseln.c stub.c pidfile.c trimdomain.c \
-       dehumanize_number.c humanize_number.c pw_util.c
+       dehumanize_number.c humanize_number.c pw_util.c \
+       getdevpath.c
 INCS=  libutil.h login_cap.h
 WARNS?=        3
 
@@ -20,7 +21,8 @@ MAN+= flopen.3 login.3 login_auth.3 login_tty.3 logout.3 logwtmp.3 pty.3 \
        login_cap.3 login_class.3 login_times.3 login_ok.3 \
        _secure_path.3 uucplock.3 property.3 auth.3 realhostname.3 \
        realhostname_sa.3 trimdomain.3 fparseln.3 pidfile.3 \
-       humanize_number.3
+       humanize_number.3 \
+       getdevpath.3
 MAN+=  login.conf.5 auth.conf.5
 MLINKS+= property.3 properties_read.3  property.3 properties_free.3
 MLINKS+= property.3 property_find.3
diff --git a/lib/libutil/getdevpath.3 b/lib/libutil/getdevpath.3
new file mode 100644 (file)
index 0000000..1171dbf
--- /dev/null
@@ -0,0 +1,103 @@
+.\"
+.\" Copyright (c) 2009 The DragonFly Project.  All rights reserved.
+.\"
+.\" This code is derived from software contributed to The DragonFly Project
+.\" by Matthew Dillon <dillon@backplane.com>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in
+.\"    the documentation and/or other materials provided with the
+.\"    distribution.
+.\" 3. Neither the name of The DragonFly Project nor the names of its
+.\"    contributors may be used to endorse or promote products derived
+.\"    from this software without specific, prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+.\" FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+.\" COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+.\" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+.\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd July 10, 2009
+.Dt GETDEVPATH 3
+.Os
+.Sh NAME
+.Nm getdevpath
+.Nd retrieve device path given name or label
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In libutil.h
+.Ft char *
+.Fn getdevpath "const char *devname" "int flags"
+.Sh DESCRIPTION
+.Fn getdevpath
+Takes a device name such as "da0", a device identifier such as "V21JYQ0G",
+a device path beginning with "/" or ".", or a device label from one of the
+.Pa devtab
+files, and returns an allocated path which may be used to open the device.
+.Pp
+Device names prefixed with "/" or "." are assumed to be device paths and an
+exact allocated copy is simply returned.  However, flags may modify the
+operation.
+.Pp
+Device names specified with a type prefix, such as "serno:V21JYQ0G" are
+assumed to be typed device identifiers and are directly translated to
+the appropriate path in
+.Pa /dev ,
+for example "/dev/serno/V21JYQ0G".
+.Pp
+Device labels are directly checked against
+.Pa /dev
+first, using lstat(), and if not found will be searched for in one of the
+.Pa devtab
+files.
+.Pp
+.Sh FLAGS
+.Bl -tag -width XXX -offset XXX
+.It Li GETDEVPATH_RAWDEV
+Normally
+.Fn getdevpath
+returns a high level devfs path which often winds up being a softlink in
+devfs.
+If this flag is specified and the device path represents a softlink,
+.Fn getdevpath
+will do a readlink and return the actual raw device path instead.
+If the device path cannot be lstat()'d this option will return
+.Dv NULL .
+.El
+.Pp
+.Sh RETURN VALUES
+.Fn getdevpath
+returns a pointer to a malloc()'d path on success and
+.Dv NULL
+on failure.
+If a failure occurs
+.Va errno
+will be set appropriately.
+Note that a malloc()'d path may be returned even if it does not exist
+in the filesystem.  Callers should not assume that the device is accessible
+unless they can also open the device.
+.Sh SEE ALSO
+.Xr fstab 5 ,
+.Xr devtab 5 ,
+.Xr devfs 8
+.Sh HISTORY
+The
+.Nm
+function call appeared in
+.Dx 2.4 .
diff --git a/lib/libutil/getdevpath.c b/lib/libutil/getdevpath.c
new file mode 100644 (file)
index 0000000..b2f7c52
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <paths.h>
+#include <limits.h>
+
+#include "libutil.h"
+
+static void finddevlabel(char **pathp, const char *devname);
+static int xlatedevpath(char **pathp, struct stat *st);
+static char *dodequote(char *base);
+
+/*
+ * Acquire device path.
+ *
+ */
+char *
+getdevpath(const char *devname, int flags)
+{
+       const char *ptr;
+       struct stat st;
+       char *path = NULL;
+       int stgood = 0;
+
+       if (devname[0] == '/' || devname[0] == '.') {
+               asprintf(&path, "%s", devname);
+       } else if ((ptr = strchr(devname, ':')) != NULL) {
+               asprintf(&path, "/dev/%*.*s/%s",
+                       ptr - devname, ptr - devname, devname,
+                       ptr + 1);
+       } else {
+               asprintf(&path, "/dev/%s", devname);
+               if (lstat(path, &st) < 0) {
+                       free(path);
+                       path = NULL;
+                       finddevlabel(&path, devname);
+               } else {
+                       stgood = 1;
+               }
+       }
+
+       /*
+        * Translate softlinks if requested.  If the lstat() of the
+        * pre-translated path fails NULL is expected to be returned.
+        * lstat() is not called on the post-translated path.
+        */
+       if ((flags & GETDEVPATH_RAWDEV) && path) {
+               if (stgood == 0 && lstat(path, &st) == 0)
+                       stgood = 1;
+               if (stgood)
+                       stgood = xlatedevpath(&path, &st);
+               if (stgood == 0) {
+                       free(path);
+                       path = NULL;
+               }
+
+       }
+       if (path == NULL)
+               errno = ENOENT;
+       return(path);
+}
+
+static void
+finddevlabel(char **pathp, const char *devname)
+{
+       const char *prefix = _PATH_DEVTAB_PATHS;
+       const char *ptr1;
+       const char *ptr2;
+       char *ptr3;
+       char *dtpath;
+       char *bufp;
+       char buf[256];
+       FILE *fp;
+       size_t len;
+
+       while (*prefix && *pathp == NULL) {
+               ptr1 = strchr(prefix, ':');
+               len = (ptr1) ? (size_t)(ptr1 - prefix) : strlen(prefix);
+               asprintf(&dtpath, "%*.*s/devtab", len, len, prefix);
+               if ((fp = fopen(dtpath, "r")) != NULL) {
+                       while (fgets(buf, sizeof(buf), fp) != NULL) {
+                               ptr1 = strtok_r(buf, " \t\r\n", &bufp);
+                               if (ptr1 == NULL || *ptr1 == 0 || *ptr1 == '#')
+                                       continue;
+                               if (strcmp(devname, ptr1) != 0)
+                                       continue;
+                               ptr2 = strtok_r(NULL, " \t\r\n", &bufp);
+                               ptr3 = strtok_r(NULL, " \t\r\n", &bufp);
+                               if (ptr2 == NULL || ptr3 == NULL)
+                                       continue;
+                               if (*ptr2 == 0 || *ptr3 == 0)
+                                       continue;
+                               ptr3 = dodequote(ptr3);
+                               if (strcmp(ptr2, "path") == 0) {
+                                       asprintf(pathp, "%s", ptr3);
+                               } else {
+                                       asprintf(pathp, "/dev/%s/%s",
+                                                ptr2, ptr3);
+                               }
+                               break;
+                       }
+                       fclose(fp);
+               }
+               free(dtpath);
+               prefix += len;
+               if (*prefix == ':')
+                       ++prefix;
+       }
+}
+
+static int
+xlatedevpath(char **pathp, struct stat *st)
+{
+       char *path;
+       int n;
+       int len;
+
+       /*
+        * If not a softlink return unchanged.
+        */
+       if (!S_ISLNK(st->st_mode))
+               return(1);
+
+       /*
+        * If the softlink isn't reasonable return bad (0)
+        */
+       len = (int)st->st_size;
+       if (len < 0 || len > PATH_MAX)
+               return(0);
+
+       /*
+        * Read the link, return if the result is not what we expected.
+        */
+       path = malloc(len + 1);
+       n = readlink(*pathp, path, len);
+       if (n < 0 || n > len) {
+               free(path);
+               return(0);
+       }
+
+       /*
+        * Success, replace (*pathp).
+        */
+       path[n] = 0;
+       free(*pathp);
+       *pathp = path;
+       return(1);
+}
+
+static char *
+dodequote(char *base)
+{
+       int len = strlen(base);
+
+       if (len && base[0] == '\"' && base[len-1] == '\"') {
+               base[len - 1] = 0;
+               ++base;
+       }
+       return(base);
+}
index e00676c..b90d7d2 100644 (file)
@@ -103,6 +103,7 @@ struct passwd *pw_scan(const char *_line, int _flags);
 const char *pw_tempname(void);
 int    pw_tmp(int _mfd);
 #endif
+char   *getdevpath(const char *devname, int flags);
 __END_DECLS
 
 #define UU_LOCK_INUSE (1)
@@ -140,4 +141,8 @@ __END_DECLS
 #define HN_GETSCALE            0x10 
 #define HN_AUTOSCALE           0x20 
 
+/* getdevpath(3) */
+#define _HAVE_GETDEVPATH       1       /* allow code conditionalization */
+#define GETDEVPATH_RAWDEV      0x0001
+
 #endif /* !_LIBUTIL_H_ */