chiark / gitweb /
extras: fix mis-spelling of "environment"
[elogind.git] / extras / scsi_id / scsi_id.c
index c64e203aff06a5dbfebdd81a582bc541b6758002..21b7cbeaec2a36f128efce8e0011199aa25f3e87 100644 (file)
 #include <syslog.h>
 #include <stdarg.h>
 #include <ctype.h>
+#include <getopt.h>
 #include <sys/stat.h>
 
-#include "../../udev.h"
+#include "../../udev/udev.h"
 #include "scsi_id.h"
-#include "scsi_id_version.h"
 
-/* temporary names for mknod  */
-#define TMP_DIR                "/dev"
-#define TMP_PREFIX     "tmp-scsi"
-
-static const char short_options[] = "abd:f:gip:s:uvVx";
+static const struct option options[] = {
+       { "device", required_argument, NULL, 'd' },
+       { "config", required_argument, NULL, 'f' },
+       { "page", required_argument, NULL, 'p' },
+       { "blacklisted", no_argument, NULL, 'b' },
+       { "whitelisted", no_argument, NULL, 'g' },
+       { "replace-whitespace", no_argument, NULL, 'u' },
+       { "sg-version", required_argument, NULL, 's' },
+       { "verbose", no_argument, NULL, 'v' },
+       { "version", no_argument, NULL, 'V' },
+       { "export", no_argument, NULL, 'x' },
+       { "help", no_argument, NULL, 'h' },
+       {}
+};
+
+static const char short_options[] = "d:f:ghip:uvVx";
 static const char dev_short_options[] = "bgp:";
 
 static int all_good;
-static int always_info;
 static int dev_specified;
-static int sys_specified;
-static char config_file[MAX_PATH_LEN] = SCSI_ID_CONFIG_FILE;
-static int display_bus_id;
+static char config_file[MAX_PATH_LEN] = SYSCONFDIR "/scsi_id.config";
 static enum page_code default_page_code;
+static int sg_version = 4;
 static int use_stderr;
 static int debug;
-static int hotplug_mode;
 static int reformat_serial;
 static int export;
 static char vendor_str[64];
@@ -54,64 +62,14 @@ static char model_str[64];
 static char revision_str[16];
 static char type_str[16];
 
-#ifdef USE_LOG
-void log_message(int priority, const char *format, ...)
+static void log_fn(struct udev *udev, int priority,
+                  const char *file, int line, const char *fn,
+                  const char *format, va_list args)
 {
-       va_list args;
-       static int udev_log = -1;
-
-       if (udev_log == -1) {
-               const char *value;
-
-               value = getenv("UDEV_LOG");
-               if (value)
-                       udev_log = log_priority(value);
-               else
-                       udev_log = LOG_ERR;
-       }
-
-       if (priority > udev_log)
-               return;
-
-       va_start(args, format);
        vsyslog(priority, format, args);
-       va_end(args);
-}
-#endif
-
-static void set_str(char *to, const char *from, size_t count)
-{
-       size_t i, j, len;
-
-       /* strip trailing whitespace */
-       len = strnlen(from, count);
-       while (len && isspace(from[len-1]))
-               len--;
-
-       /* strip leading whitespace */
-       i = 0;
-       while (isspace(from[i]) && (i < len))
-               i++;
-
-       j = 0;
-       while (i < len) {
-               /* substitute multiple whitespace */
-               if (isspace(from[i])) {
-                       while (isspace(from[i]))
-                               i++;
-                       to[j++] = '_';
-               }
-               /* skip chars */
-               if (from[i] == '/') {
-                       i++;
-                       continue;
-               }
-               to[j++] = from[i++];
-       }
-       to[j] = '\0';
 }
 
-static void set_type(char *to, const char *from, size_t len)
+static void set_type(const char *from, char *to, size_t len)
 {
        int type_num;
        char *eptr;
@@ -145,37 +103,7 @@ static void set_type(char *to, const char *from, size_t len)
                        break;
                }
        }
-       strncpy(to, type, len);
-       to[len-1] = '\0';
-}
-
-static int create_tmp_dev(const char *devpath, char *tmpdev, int dev_type)
-{
-       unsigned int maj, min;
-       const char *attr;
-
-       dbg("%s", devpath);
-       attr = sysfs_attr_get_value(devpath, "dev");
-       if (attr == NULL) {
-               dbg("%s: could not get dev attribute: %s", devpath, strerror(errno));
-               return -1;
-       }
-
-       dbg("dev value %s", attr);
-       if (sscanf(attr, "%u:%u", &maj, &min) != 2) {
-               err("%s: invalid dev major/minor", devpath);
-               return -1;
-       }
-
-       snprintf(tmpdev, MAX_PATH_LEN, "%s/%s-maj%d-min%d-%u",
-                TMP_DIR, TMP_PREFIX, maj, min, getpid());
-
-       dbg("tmpdev '%s'", tmpdev);
-       if (mknod(tmpdev, 0600 | dev_type, makedev(maj, min))) {
-               err("mknod failed: %s", strerror(errno));
-               return -1;
-       }
-       return 0;
+       util_strlcpy(to, type, len);
 }
 
 /*
@@ -234,7 +162,8 @@ static int argc_count(char *opts)
  *
  * vendor and model can end in '\n'.
  */
-static int get_file_options(const char *vendor, const char *model,
+static int get_file_options(struct udev *udev,
+                           const char *vendor, const char *model,
                            int *argc, char ***newargv)
 {
        char *buffer;
@@ -246,14 +175,14 @@ static int get_file_options(const char *vendor, const char *model,
        int c;
        int retval = 0;
 
-       dbg("vendor='%s'; model='%s'\n", vendor, model);
+       dbg(udev, "vendor='%s'; model='%s'\n", vendor, model);
        fd = fopen(config_file, "r");
        if (fd == NULL) {
-               dbg("can't open %s\n", config_file);
+               dbg(udev, "can't open %s\n", config_file);
                if (errno == ENOENT) {
                        return 1;
                } else {
-                       err("can't open %s: %s", config_file, strerror(errno));
+                       err(udev, "can't open %s: %s\n", config_file, strerror(errno));
                        return -1;
                }
        }
@@ -265,7 +194,7 @@ static int get_file_options(const char *vendor, const char *model,
         */
        buffer = malloc(MAX_BUFFER_LEN);
        if (!buffer) {
-               err("Can't allocate memory.");
+               err(udev, "can't allocate memory\n");
                return -1;
        }
 
@@ -279,7 +208,7 @@ static int get_file_options(const char *vendor, const char *model,
                        break;
                lineno++;
                if (buf[strlen(buffer) - 1] != '\n') {
-                       info("Config file line %d too long.\n", lineno);
+                       err(udev, "Config file line %d too long\n", lineno);
                        break;
                }
 
@@ -294,7 +223,7 @@ static int get_file_options(const char *vendor, const char *model,
                if (*buf == '#')
                        continue;
 
-               dbg("lineno %d: '%s'\n", lineno, buf);
+               dbg(udev, "lineno %d: '%s'\n", lineno, buf);
                str1 = strsep(&buf, "=");
                if (str1 && strcasecmp(str1, "VENDOR") == 0) {
                        str1 = get_value(&buf);
@@ -324,20 +253,20 @@ static int get_file_options(const char *vendor, const char *model,
                        }
                        options_in = str1;
                }
-               dbg("config file line %d:"
+               dbg(udev, "config file line %d:\n"
                        " vendor '%s'; model '%s'; options '%s'\n",
                        lineno, vendor_in, model_in, options_in);
                /*
                 * Only allow: [vendor=foo[,model=bar]]options=stuff
                 */
                if (!options_in || (!vendor_in && model_in)) {
-                       info("Error parsing config file line %d '%s'", lineno, buffer);
+                       err(udev, "Error parsing config file line %d '%s'\n", lineno, buffer);
                        retval = -1;
                        break;
                }
                if (vendor == NULL) {
                        if (vendor_in == NULL) {
-                               dbg("matched global option\n");
+                               dbg(udev, "matched global option\n");
                                break;
                        }
                } else if ((vendor_in && strncmp(vendor, vendor_in,
@@ -351,10 +280,10 @@ static int get_file_options(const char *vendor, const char *model,
                                 * give a partial match (that is FOO
                                 * matches FOOBAR).
                                 */
-                               dbg("matched vendor/model\n");
+                               dbg(udev, "matched vendor/model\n");
                                break;
                } else {
-                       dbg("no match\n");
+                       dbg(udev, "no match\n");
                }
        }
 
@@ -369,7 +298,7 @@ static int get_file_options(const char *vendor, const char *model,
                        c = argc_count(buffer) + 2;
                        *newargv = calloc(c, sizeof(**newargv));
                        if (!*newargv) {
-                               err("Can't allocate memory.");
+                               err(udev, "can't allocate memory\n");
                                retval = -1;
                        } else {
                                *argc = c;
@@ -377,11 +306,11 @@ static int get_file_options(const char *vendor, const char *model,
                                /*
                                 * argv[0] at 0 is skipped by getopt, but
                                 * store the buffer address there for
-                                * alter freeing.
+                                * later freeing
                                 */
                                (*newargv)[c] = buffer;
                                for (c = 1; c < *argc; c++)
-                                       (*newargv)[c] = strsep(&buffer, " ");
+                                       (*newargv)[c] = strsep(&buffer, " \t");
                        }
                } else {
                        /* No matches  */
@@ -394,8 +323,9 @@ static int get_file_options(const char *vendor, const char *model,
        return retval;
 }
 
-static int set_options(int argc, char **argv, const char *short_opts,
-                      char *target, char *maj_min_dev)
+static int set_options(struct udev *udev,
+                      int argc, char **argv, const char *short_opts,
+                      char *maj_min_dev)
 {
        int option;
 
@@ -406,27 +336,23 @@ static int set_options(int argc, char **argv, const char *short_opts,
         */
        optind = 1;
        while (1) {
-               option = getopt(argc, argv, short_opts);
+               option = getopt_long(argc, argv, short_opts, options, NULL);
                if (option == -1)
                        break;
 
                if (optarg)
-                       dbg("option '%c' arg '%s'\n", option, optarg);
+                       dbg(udev, "option '%c' arg '%s'\n", option, optarg);
                else
-                       dbg("option '%c'\n", option);
+                       dbg(udev, "option '%c'\n", option);
 
                switch (option) {
-               case 'a':
-                       always_info = 1;
-                       break;
                case 'b':
                        all_good = 0;
                        break;
 
                case 'd':
                        dev_specified = 1;
-                       strncpy(maj_min_dev, optarg, MAX_PATH_LEN);
-                       maj_min_dev[MAX_PATH_LEN-1] = '\0';
+                       util_strlcpy(maj_min_dev, optarg, MAX_PATH_LEN);
                        break;
 
                case 'e':
@@ -434,17 +360,27 @@ static int set_options(int argc, char **argv, const char *short_opts,
                        break;
 
                case 'f':
-                       strncpy(config_file, optarg, MAX_PATH_LEN);
-                       config_file[MAX_PATH_LEN-1] = '\0';
+                       util_strlcpy(config_file, optarg, MAX_PATH_LEN);
                        break;
 
                case 'g':
                        all_good = 1;
                        break;
 
-               case 'i':
-                       display_bus_id = 1;
-                       break;
+               case 'h':
+                       printf("Usage: scsi_id OPTIONS <device>\n"
+                              "  --device=                     device node for SG_IO commands\n"
+                              "  --config=                     location of config file\n"
+                              "  --page=0x80|0x83|pre-spc3-83  SCSI page (0x80, 0x83, pre-spc3-83)\n"
+                              "  --sg-version=3|4              use SGv3 or SGv4\n"
+                              "  --blacklisted                 threat device as blacklisted\n"
+                              "  --whitelisted                 threat device as whitelisted\n"
+                              "  --replace-whitespace          replace all whitespaces by underscores\n"
+                              "  --verbose                     verbose logging\n"
+                              "  --version                     print version\n"
+                              "  --export                      print values as environment keys\n"
+                              "  --help                        print this help text\n\n");
+                       exit(0);
 
                case 'p':
                        if (strcmp(optarg, "0x80") == 0) {
@@ -454,15 +390,17 @@ static int set_options(int argc, char **argv, const char *short_opts,
                        } else if (strcmp(optarg, "pre-spc3-83") == 0) {
                                default_page_code = PAGE_83_PRE_SPC3; 
                        } else {
-                               info("Unknown page code '%s'", optarg);
+                               err(udev, "Unknown page code '%s'\n", optarg);
                                return -1;
                        }
                        break;
 
                case 's':
-                       sys_specified = 1;
-                       strncpy(target, optarg, MAX_PATH_LEN);
-                       target[MAX_PATH_LEN-1] = '\0';
+                       sg_version = atoi(optarg);
+                       if (sg_version < 3 || sg_version > 4) {
+                               err(udev, "Unknown SG version '%s'\n", optarg);
+                               return -1;
+                       }
                        break;
 
                case 'u':
@@ -478,69 +416,44 @@ static int set_options(int argc, char **argv, const char *short_opts,
                        break;
 
                case 'V':
-                       info("scsi_id version: %s\n", SCSI_ID_VERSION);
+                       printf("%s\n", VERSION);
                        exit(0);
                        break;
 
                default:
-                       info("Unknown or bad option '%c' (0x%x)", option, option);
-                       return -1;
+                       exit(1);
                }
        }
+       if (optind < argc && !dev_specified) {
+               dev_specified = 1;
+               util_strlcpy(maj_min_dev, argv[optind], MAX_PATH_LEN);
+       }
        return 0;
 }
 
-static int per_dev_options(struct sysfs_device *dev_scsi, int *good_bad, int *page_code)
+static int per_dev_options(struct udev *udev,
+                          struct scsi_id_device *dev_scsi, int *good_bad, int *page_code)
 {
        int retval;
        int newargc;
        char **newargv = NULL;
-       const char *vendor, *model, *type;
        int option;
 
        *good_bad = all_good;
        *page_code = default_page_code;
 
-       vendor = sysfs_attr_get_value(dev_scsi->devpath, "vendor");
-       if (!vendor) {
-               info("%s: cannot get vendor attribute", dev_scsi->devpath);
-               return -1;
-       }
-       set_str(vendor_str, vendor, sizeof(vendor_str)-1);
-
-       model = sysfs_attr_get_value(dev_scsi->devpath, "model");
-       if (!model) {
-               info("%s: cannot get model attribute\n", dev_scsi->devpath);
-               return -1;
-       }
-       set_str(model_str, model, sizeof(model_str)-1);
-
-       type = sysfs_attr_get_value(dev_scsi->devpath, "type");
-       if (!type) {
-               info("%s: cannot get type attribute", dev_scsi->devpath);
-               return -1;
-       }
-       set_type(type_str, type, sizeof(type_str));
-
-       type = sysfs_attr_get_value(dev_scsi->devpath, "rev");
-       if (!type) {
-               info("%s: cannot get type attribute\n", dev_scsi->devpath);
-               return -1;
-       }
-       set_str(revision_str, type, sizeof(revision_str)-1);
-
-       retval = get_file_options(vendor, model, &newargc, &newargv);
+       retval = get_file_options(udev, vendor_str, model_str, &newargc, &newargv);
 
        optind = 1; /* reset this global extern */
        while (retval == 0) {
-               option = getopt(newargc, newargv, dev_short_options);
+               option = getopt_long(newargc, newargv, dev_short_options, options, NULL);
                if (option == -1)
                        break;
 
                if (optarg)
-                       dbg("option '%c' arg '%s'\n", option, optarg);
+                       dbg(udev, "option '%c' arg '%s'\n", option, optarg);
                else
-                       dbg("option '%c'\n", option);
+                       dbg(udev, "option '%c'\n", option);
 
                switch (option) {
                case 'b':
@@ -559,13 +472,13 @@ static int per_dev_options(struct sysfs_device *dev_scsi, int *good_bad, int *pa
                        } else if (strcmp(optarg, "pre-spc3-83") == 0) {
                                *page_code = PAGE_83_PRE_SPC3; 
                        } else {
-                               info("Unknown page code '%s'", optarg);
+                               err(udev, "Unknown page code '%s'\n", optarg);
                                retval = -1;
                        }
                        break;
 
                default:
-                       info("Unknown or bad option '%c' (0x%x)", option, option);
+                       err(udev, "Unknown or bad option '%c' (0x%x)\n", option, option);
                        retval = -1;
                        break;
                }
@@ -578,6 +491,26 @@ static int per_dev_options(struct sysfs_device *dev_scsi, int *good_bad, int *pa
        return retval;
 }
 
+static int set_inq_values(struct udev *udev, struct scsi_id_device *dev_scsi, const char *path)
+{
+       int retval;
+
+       dev_scsi->use_sg = sg_version;
+
+       retval = scsi_std_inquiry(udev, dev_scsi, path);
+       if (retval)
+               return retval;
+
+       udev_util_replace_whitespace(dev_scsi->vendor, vendor_str, sizeof(vendor_str));
+       udev_util_replace_chars(vendor_str, NULL);
+       udev_util_replace_whitespace(dev_scsi->model, model_str, sizeof(model_str));
+       udev_util_replace_chars(model_str, NULL);
+       set_type(dev_scsi->type, type_str, sizeof(type_str));
+       udev_util_replace_whitespace(dev_scsi->revision, revision_str, sizeof(revision_str));
+       udev_util_replace_chars(revision_str, NULL);
+       return 0;
+}
+
 /*
  * format_serial: replace to whitespaces by underscores for calling
  * programs that use the serial for device naming (multipath, Suse
@@ -605,134 +538,88 @@ static void format_serial(char *serial)
 
 /*
  * scsi_id: try to get an id, if one is found, printf it to stdout.
- * returns a value passed to exit() - 0 if printed an id, else 1. This
- * could be expanded, for example, if we want to report a failure like no
- * memory etc. return 2, and return 1 for expected cases (like broken
- * device found) that do not print an id.
+ * returns a value passed to exit() - 0 if printed an id, else 1.
  */
-static int scsi_id(const char *devpath, char *maj_min_dev)
+static int scsi_id(struct udev *udev, char *maj_min_dev)
 {
-       int retval;
-       int dev_type = 0;
-       char *serial, *unaligned_buf;
-       struct sysfs_device *dev;
-       struct sysfs_device *dev_scsi;
+       struct scsi_id_device dev_scsi;
        int good_dev;
        int page_code;
+       int retval = 0;
 
-       dbg("devpath %s\n", devpath);
-
-       dev = sysfs_device_get(devpath);
-       if (dev == NULL) {
-               err("unable to access '%s'", devpath);
-               return 1;
-       }
-
-       if (strcmp(dev->subsystem, "block") == 0)
-               dev_type = S_IFBLK;
-       else
-               dev_type = S_IFCHR;
-
-       /* get scsi parent device */
-       dev_scsi = sysfs_device_get_parent_with_subsystem(dev, "scsi");
-       if (dev_scsi == NULL) {
-               err("unable to access parent device of '%s'", devpath);
-               return 1;
-       }
+       memset(&dev_scsi, 0x00, sizeof(struct scsi_id_device));
 
-       /* mknod a temp dev to communicate with the device */
-       if (!dev_specified && create_tmp_dev(dev->devpath, maj_min_dev, dev_type)) {
-               dbg("create_tmp_dev failed\n");
-               return 1;
+       if (set_inq_values(udev, &dev_scsi, maj_min_dev) < 0) {
+               retval = 1;
+               goto out;
        }
 
        /* get per device (vendor + model) options from the config file */
-       retval = per_dev_options(dev_scsi, &good_dev, &page_code);
-       dbg("per dev options: good %d; page code 0x%x", good_dev, page_code);
-
-#define ALIGN   512
-       unaligned_buf = malloc(MAX_SERIAL_LEN + ALIGN);
-       serial = (char*) (((unsigned long) unaligned_buf + (ALIGN - 1))
-                         & ~(ALIGN - 1));
-       dbg("buffer unaligned 0x%p; aligned 0x%p\n", unaligned_buf, serial);
-#undef ALIGN
-
+       per_dev_options(udev, &dev_scsi, &good_dev, &page_code);
+       dbg(udev, "per dev options: good %d; page code 0x%x\n", good_dev, page_code);
        if (!good_dev) {
                retval = 1;
-       } else if (scsi_get_serial(dev_scsi, maj_min_dev, page_code,
-                                  serial, MAX_SERIAL_LEN)) {
-               retval = always_info?0:1;
-       } else {
-               retval = 0;
+               goto out;
        }
-       if (!retval) {
-               if (export) {
-                       static char serial_str[64];
-                       printf("ID_VENDOR=%s\n", vendor_str);
-                       printf("ID_MODEL=%s\n", model_str);
-                       printf("ID_REVISION=%s\n", revision_str);
-                       set_str(serial_str, serial, sizeof(serial_str));
+
+       /* read serial number from mode pages (no values for optical drives) */
+       scsi_get_serial(udev, &dev_scsi, maj_min_dev, page_code, MAX_SERIAL_LEN);
+
+       if (export) {
+               char serial_str[MAX_SERIAL_LEN];
+
+               printf("ID_VENDOR=%s\n", vendor_str);
+               printf("ID_MODEL=%s\n", model_str);
+               printf("ID_REVISION=%s\n", revision_str);
+               printf("ID_TYPE=%s\n", type_str);
+               if (dev_scsi.serial[0] != '\0') {
+                       udev_util_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str));
+                       udev_util_replace_chars(serial_str, NULL);
                        printf("ID_SERIAL=%s\n", serial_str);
-                       printf("ID_TYPE=%s\n", type_str);
-                       printf("ID_BUS=scsi\n");
-               } else {
-                       if (reformat_serial)
-                               format_serial(serial);
-                       if (display_bus_id)
-                               printf("%s: ", dev_scsi->kernel);
-                       printf("%s\n", serial);
+                       udev_util_replace_whitespace(dev_scsi.serial_short, serial_str, sizeof(serial_str));
+                       udev_util_replace_chars(serial_str, NULL);
+                       printf("ID_SERIAL_SHORT=%s\n", serial_str);
                }
-               dbg("%s\n", serial);
-               retval = 0;
+               goto out;
        }
 
-       if (!dev_specified)
-               unlink(maj_min_dev);
-
+       if (dev_scsi.serial[0] == '\0') {
+               retval = 1;
+               goto out;
+       }
+       if (reformat_serial)
+               format_serial(dev_scsi.serial);
+       printf("%s\n", dev_scsi.serial);
+out:
        return retval;
 }
 
 int main(int argc, char **argv)
 {
+       struct udev *udev;
        int retval = 0;
-       char devpath[MAX_PATH_LEN];
        char maj_min_dev[MAX_PATH_LEN];
        int newargc;
-       const char *env;
        char **newargv;
 
+       udev = udev_new();
+       if (udev == NULL)
+               goto exit;
+
        logging_init("scsi_id");
-       sysfs_init();
-       dbg("argc is %d\n", argc);
-
-       /* sysfs path can be overridden for testing */
-       env = getenv("SYSFS_PATH");
-       if (env) {
-               strncpy(sysfs_path, env, sizeof(sysfs_path));
-               sysfs_path[sizeof(sysfs_path)-1] = '\0';
-       } else
-               strcpy(sysfs_path, "/sys");
-
-       env = getenv("DEVPATH");
-       if (env) {
-               hotplug_mode = 1;
-               sys_specified = 1;
-               strncpy(devpath, env, MAX_PATH_LEN);
-               devpath[sizeof(devpath)-1] = '\0';
-       }
+       udev_set_log_fn(udev, log_fn);
 
        /*
         * Get config file options.
         */
        newargv = NULL;
-       retval = get_file_options(NULL, NULL, &newargc, &newargv);
+       retval = get_file_options(udev, NULL, NULL, &newargc, &newargv);
        if (retval < 0) {
                retval = 1;
                goto exit;
        }
        if (newargv && (retval == 0)) {
-               if (set_options(newargc, newargv, short_options, devpath,
-                               maj_min_dev) < 0) {
+               if (set_options(udev, newargc, newargv, short_options, maj_min_dev) < 0) {
                        retval = 2;
                        goto exit;
                }
@@ -740,22 +627,21 @@ int main(int argc, char **argv)
        }
 
        /*
-        * Get command line options (overriding any config file or DEVPATH
-        * settings).
+        * Get command line options (overriding any config file settings).
         */
-       if (set_options(argc, argv, short_options, devpath, maj_min_dev) < 0)
+       if (set_options(udev, argc, argv, short_options, maj_min_dev) < 0)
                exit(1);
 
-       if (!sys_specified) {
-               info("-s must be specified\n");
+       if (!dev_specified) {
+               err(udev, "no device specified\n");
                retval = 1;
                goto exit;
        }
 
-       retval = scsi_id(devpath, maj_min_dev);
+       retval = scsi_id(udev, maj_min_dev);
 
 exit:
-       sysfs_cleanup();
+       udev_unref(udev);
        logging_close();
        return retval;
 }