2 * Copyright (c) 2004, 2005 Christophe Varoqui
3 * Copyright (c) 2005 Benjamin Marzinski, Redhat
4 * Copyright (c) 2005 Edward Goggin, EMC
22 #include "blacklist.h"
25 #include "devmapper.h"
26 #include "mpath_cmd.h"
31 * We don't support re-initialization after
32 * libmultipath_exit().
34 static bool libmultipath_exit_called;
35 static pthread_once_t _init_once = PTHREAD_ONCE_INIT;
36 static pthread_once_t _exit_once = PTHREAD_ONCE_INIT;
39 static void _udev_init(void)
46 condlog(0, "%s: failed to initialize udev", __func__);
49 static bool _is_libmultipath_initialized(void)
51 return !libmultipath_exit_called && !!udev;
54 int libmultipath_init(void)
56 pthread_once(&_init_once, _udev_init);
57 return !_is_libmultipath_initialized();
60 static void _libmultipath_exit(void)
62 libmultipath_exit_called = true;
70 void libmultipath_exit(void)
72 pthread_once(&_exit_once, _libmultipath_exit);
75 static struct config __internal_config;
76 struct config *libmp_get_multipath_config(void)
78 if (!__internal_config.hwtable)
81 return &__internal_config;
84 struct config *get_multipath_config(void)
85 __attribute__((alias("libmp_get_multipath_config")));
87 void libmp_put_multipath_config(void *conf __attribute__((unused)))
92 void put_multipath_config(void *conf)
93 __attribute__((alias("libmp_put_multipath_config")));
96 hwe_strmatch (const struct hwentry *hwe1, const struct hwentry *hwe2)
98 if ((hwe2->vendor && !hwe1->vendor) ||
99 (hwe1->vendor && (!hwe2->vendor ||
100 strcmp(hwe1->vendor, hwe2->vendor))))
103 if ((hwe2->product && !hwe1->product) ||
104 (hwe1->product && (!hwe2->product ||
105 strcmp(hwe1->product, hwe2->product))))
108 if ((hwe2->revision && !hwe1->revision) ||
109 (hwe1->revision && (!hwe2->revision ||
110 strcmp(hwe1->revision, hwe2->revision))))
116 static struct hwentry *
117 find_hwe_strmatch (const struct _vector *hwtable, const struct hwentry *hwe)
120 struct hwentry *tmp, *ret = NULL;
122 vector_foreach_slot (hwtable, tmp, i) {
123 if (hwe_strmatch(tmp, hwe))
132 hwe_regmatch (const struct hwentry *hwe1, const char *vendor,
133 const char *product, const char *revision)
135 regex_t vre, pre, rre;
139 regcomp(&vre, hwe1->vendor, REG_EXTENDED|REG_NOSUB))
143 regcomp(&pre, hwe1->product, REG_EXTENDED|REG_NOSUB))
146 if (hwe1->revision &&
147 regcomp(&rre, hwe1->revision, REG_EXTENDED|REG_NOSUB))
150 if ((vendor || product || revision) &&
151 (!hwe1->vendor || !vendor ||
152 !regexec(&vre, vendor, 0, NULL, 0)) &&
153 (!hwe1->product || !product ||
154 !regexec(&pre, product, 0, NULL, 0)) &&
155 (!hwe1->revision || !revision ||
156 !regexec(&rre, revision, 0, NULL, 0)))
171 static void _log_match(const char *fn, const struct hwentry *h,
172 const char *vendor, const char *product,
173 const char *revision)
175 condlog(4, "%s: found match /%s:%s:%s/ for '%s:%s:%s'", fn,
176 h->vendor, h->product, h->revision,
177 vendor, product, revision);
179 #define log_match(h, v, p, r) _log_match(__func__, (h), (v), (p), (r))
182 find_hwe (const struct _vector *hwtable,
183 const char * vendor, const char * product, const char * revision,
190 * Search backwards here, and add forward.
191 * User modified entries are attached at the end of
192 * the list, so we have to check them first before
193 * continuing to the generic entries
195 vector_reset(result);
196 vector_foreach_slot_backwards (hwtable, tmp, i) {
197 if (hwe_regmatch(tmp, vendor, product, revision))
199 if (vector_alloc_slot(result)) {
200 vector_set_slot(result, tmp);
203 log_match(tmp, vendor, product, revision);
205 condlog(n > 1 ? 3 : 4, "%s: found %d hwtable matches for %s:%s:%s",
206 __func__, n, vendor, product, revision);
210 struct mpentry *find_mpe(vector mptable, char *wwid)
213 struct mpentry * mpe;
218 vector_foreach_slot (mptable, mpe, i)
219 if (mpe->wwid && !strcmp(mpe->wwid, wwid))
225 const char *get_mpe_wwid(const struct _vector *mptable, const char *alias)
228 struct mpentry * mpe;
233 vector_foreach_slot (mptable, mpe, i)
234 if (mpe->alias && strcmp(mpe->alias, alias) == 0)
241 free_pctable (vector pctable)
246 vector_foreach_slot(pctable, pce, i)
249 vector_free(pctable);
253 free_hwe (struct hwentry * hwe)
267 if (hwe->uid_attribute)
268 free(hwe->uid_attribute);
274 free(hwe->hwhandler);
279 if (hwe->checker_name)
280 free(hwe->checker_name);
283 free(hwe->prio_name);
286 free(hwe->prio_args);
288 if (hwe->alias_prefix)
289 free(hwe->alias_prefix);
292 free(hwe->bl_product);
295 free_pctable(hwe->pctable);
301 free_hwtable (vector hwtable)
304 struct hwentry * hwe;
309 vector_foreach_slot (hwtable, hwe, i)
312 vector_free(hwtable);
316 free_mpe (struct mpentry * mpe)
327 if (mpe->uid_attribute)
328 free(mpe->uid_attribute);
334 free(mpe->prio_name);
337 free(mpe->prio_args);
343 free_mptable (vector mptable)
346 struct mpentry * mpe;
351 vector_foreach_slot (mptable, mpe, i)
354 vector_free(mptable);
360 struct mpentry * mpe = (struct mpentry *)
361 calloc(1, sizeof(struct mpentry));
369 struct hwentry * hwe = (struct hwentry *)
370 calloc(1, sizeof(struct hwentry));
378 struct pcentry *pce = (struct pcentry *)
379 calloc(1, sizeof(struct pcentry));
380 pce->type = PCE_INVALID;
385 set_param_str(const char * str)
398 dst = (char *)calloc(1, len + 1);
407 #define merge_str(s) \
408 if (!dst->s && src->s && strlen(src->s)) { \
413 #define merge_num(s) \
414 if (!dst->s && src->s) \
418 merge_pce(struct pcentry *dst, struct pcentry *src)
420 merge_num(fast_io_fail);
422 merge_num(eh_deadline);
426 merge_hwe (struct hwentry * dst, struct hwentry * src)
428 char id[SCSI_VENDOR_SIZE+PATH_PRODUCT_SIZE];
432 merge_str(uid_attribute);
434 merge_str(hwhandler);
436 merge_str(checker_name);
437 merge_str(prio_name);
438 merge_str(prio_args);
439 merge_str(alias_prefix);
440 merge_str(bl_product);
442 merge_num(pgfailback);
443 merge_num(rr_weight);
444 merge_num(no_path_retry);
447 merge_num(flush_on_last_del);
448 merge_num(fast_io_fail);
450 merge_num(eh_deadline);
451 merge_num(user_friendly_names);
452 merge_num(retain_hwhandler);
453 merge_num(detect_prio);
454 merge_num(detect_checker);
455 merge_num(detect_pgpolicy);
456 merge_num(detect_pgpolicy_use_tpg);
457 merge_num(deferred_remove);
458 merge_num(delay_watch_checks);
459 merge_num(delay_wait_checks);
460 merge_num(skip_kpartx);
461 merge_num(max_sectors_kb);
462 merge_num(ghost_delay);
463 merge_num(all_tg_pt);
464 merge_num(recheck_wwid);
465 merge_num(vpd_vendor_id);
466 merge_num(san_path_err_threshold);
467 merge_num(san_path_err_forget_rate);
468 merge_num(san_path_err_recovery_time);
469 merge_num(marginal_path_err_sample_time);
470 merge_num(marginal_path_err_rate_threshold);
471 merge_num(marginal_path_err_recheck_gap_time);
472 merge_num(marginal_path_double_failed_time);
474 snprintf(id, sizeof(id), "%s/%s", dst->vendor, dst->product);
475 reconcile_features_with_options(id, &dst->features,
477 &dst->retain_hwhandler);
481 merge_mpe(struct mpentry *dst, struct mpentry *src)
484 merge_str(uid_attribute);
487 merge_str(prio_name);
488 merge_str(prio_args);
490 if (dst->prkey_source == PRKEY_SOURCE_NONE &&
491 src->prkey_source != PRKEY_SOURCE_NONE) {
492 dst->prkey_source = src->prkey_source;
493 dst->sa_flags = src->sa_flags;
494 memcpy(&dst->reservation_key, &src->reservation_key,
495 sizeof(dst->reservation_key));
499 merge_num(pgfailback);
500 merge_num(rr_weight);
501 merge_num(no_path_retry);
504 merge_num(flush_on_last_del);
505 merge_num(attribute_flags);
506 merge_num(user_friendly_names);
507 merge_num(deferred_remove);
508 merge_num(delay_watch_checks);
509 merge_num(delay_wait_checks);
510 merge_num(san_path_err_threshold);
511 merge_num(san_path_err_forget_rate);
512 merge_num(san_path_err_recovery_time);
513 merge_num(marginal_path_err_sample_time);
514 merge_num(marginal_path_err_rate_threshold);
515 merge_num(marginal_path_err_recheck_gap_time);
516 merge_num(marginal_path_double_failed_time);
517 merge_num(skip_kpartx);
518 merge_num(max_sectors_kb);
519 merge_num(ghost_delay);
525 static int wwid_compar(const void *p1, const void *p2)
527 const char *wwid1 = (*(struct mpentry * const *)p1)->wwid;
528 const char *wwid2 = (*(struct mpentry * const *)p2)->wwid;
530 return strcmp(wwid1, wwid2);
533 void merge_mptable(vector mptable)
535 struct mpentry *mp1, *mp2;
538 vector_foreach_slot(mptable, mp1, i) {
539 /* drop invalid multipath configs */
541 condlog(0, "multipaths config section missing wwid");
542 vector_del_slot(mptable, i--);
547 vector_sort(mptable, wwid_compar);
548 vector_foreach_slot(mptable, mp1, i) {
550 vector_foreach_slot_after(mptable, mp2, j) {
551 if (strcmp(mp1->wwid, mp2->wwid))
553 condlog(1, "%s: duplicate multipath config section for %s",
554 __func__, mp1->wwid);
557 vector_del_slot(mptable, i);
565 store_hwe (vector hwtable, struct hwentry * dhwe)
567 struct hwentry * hwe;
569 if (find_hwe_strmatch(hwtable, dhwe))
572 if (!(hwe = alloc_hwe()))
575 if (!dhwe->vendor || !(hwe->vendor = set_param_str(dhwe->vendor)))
578 if (!dhwe->product || !(hwe->product = set_param_str(dhwe->product)))
581 if (dhwe->revision && !(hwe->revision = set_param_str(dhwe->revision)))
584 if (dhwe->uid_attribute && !(hwe->uid_attribute = set_param_str(dhwe->uid_attribute)))
587 if (dhwe->features && !(hwe->features = set_param_str(dhwe->features)))
590 if (dhwe->hwhandler && !(hwe->hwhandler = set_param_str(dhwe->hwhandler)))
593 if (dhwe->selector && !(hwe->selector = set_param_str(dhwe->selector)))
596 if (dhwe->checker_name && !(hwe->checker_name = set_param_str(dhwe->checker_name)))
599 if (dhwe->prio_name && !(hwe->prio_name = set_param_str(dhwe->prio_name)))
602 if (dhwe->prio_args && !(hwe->prio_args = set_param_str(dhwe->prio_args)))
605 if (dhwe->alias_prefix && !(hwe->alias_prefix = set_param_str(dhwe->alias_prefix)))
608 hwe->pgpolicy = dhwe->pgpolicy;
609 hwe->pgfailback = dhwe->pgfailback;
610 hwe->rr_weight = dhwe->rr_weight;
611 hwe->no_path_retry = dhwe->no_path_retry;
612 hwe->minio = dhwe->minio;
613 hwe->minio_rq = dhwe->minio_rq;
614 hwe->flush_on_last_del = dhwe->flush_on_last_del;
615 hwe->fast_io_fail = dhwe->fast_io_fail;
616 hwe->dev_loss = dhwe->dev_loss;
617 hwe->eh_deadline = dhwe->eh_deadline;
618 hwe->user_friendly_names = dhwe->user_friendly_names;
619 hwe->retain_hwhandler = dhwe->retain_hwhandler;
620 hwe->detect_prio = dhwe->detect_prio;
621 hwe->detect_checker = dhwe->detect_checker;
622 hwe->detect_pgpolicy = dhwe->detect_pgpolicy;
623 hwe->detect_pgpolicy_use_tpg = dhwe->detect_pgpolicy_use_tpg;
624 hwe->ghost_delay = dhwe->ghost_delay;
625 hwe->vpd_vendor_id = dhwe->vpd_vendor_id;
627 if (dhwe->bl_product && !(hwe->bl_product = set_param_str(dhwe->bl_product)))
630 if (!vector_alloc_slot(hwtable))
633 vector_set_slot(hwtable, hwe);
641 validate_pctable(struct hwentry *ovr, int idx, const char *table_desc)
645 if (!ovr || !ovr->pctable)
648 vector_foreach_slot_after(ovr->pctable, pce, idx) {
649 if (pce->type == PCE_INVALID) {
650 condlog(0, "protocol section in %s missing type",
652 vector_del_slot(ovr->pctable, idx--);
657 if (VECTOR_SIZE(ovr->pctable) == 0) {
658 vector_free(ovr->pctable);
664 merge_pctable(struct hwentry *ovr)
666 struct pcentry *pce1, *pce2;
669 if (!ovr || !ovr->pctable)
672 vector_foreach_slot(ovr->pctable, pce1, i) {
674 vector_foreach_slot_after(ovr->pctable, pce2, j) {
675 if (pce1->type != pce2->type)
677 merge_pce(pce2,pce1);
678 vector_del_slot(ovr->pctable, i--);
686 factorize_hwtable (vector hw, int n, const char *table_desc)
688 struct hwentry *hwe1, *hwe2;
692 vector_foreach_slot(hw, hwe1, i) {
693 /* drop invalid device configs */
694 if (i >= n && (!hwe1->vendor || !hwe1->product)) {
695 condlog(0, "device config in %s missing vendor or product parameter",
697 vector_del_slot(hw, i--);
701 j = n > i + 1 ? n : i + 1;
702 vector_foreach_slot_after(hw, hwe2, j) {
703 if (hwe_strmatch(hwe2, hwe1) == 0) {
704 condlog(i >= n ? 1 : 3,
705 "%s: duplicate device section for %s:%s:%s in %s",
706 __func__, hwe1->vendor, hwe1->product,
707 hwe1->revision, table_desc);
708 vector_del_slot(hw, i);
709 merge_hwe(hwe2, hwe1);
714 * Play safe here; we have modified
715 * the original vector so the outer
716 * vector_foreach_slot() might
726 static struct config *alloc_config (void)
728 return (struct config *)calloc(1, sizeof(struct config));
731 static void _uninit_config(struct config *conf)
737 conf = &__internal_config;
740 free(conf->selector);
742 if (conf->uid_attribute)
743 free(conf->uid_attribute);
745 vector_foreach_slot(&conf->uid_attrs, ptr, i)
747 vector_reset(&conf->uid_attrs);
750 free(conf->features);
753 free(conf->hwhandler);
756 free(conf->prio_name);
758 if (conf->alias_prefix)
759 free(conf->alias_prefix);
760 if (conf->partition_delim)
761 free(conf->partition_delim);
764 free(conf->prio_args);
766 if (conf->checker_name)
767 free(conf->checker_name);
769 if (conf->enable_foreign)
770 free(conf->enable_foreign);
772 free_blacklist(conf->blist_devnode);
773 free_blacklist(conf->blist_wwid);
774 free_blacklist(conf->blist_property);
775 free_blacklist(conf->blist_protocol);
776 free_blacklist_device(conf->blist_device);
778 free_blacklist(conf->elist_devnode);
779 free_blacklist(conf->elist_wwid);
780 free_blacklist(conf->elist_property);
781 free_blacklist(conf->elist_protocol);
782 free_blacklist_device(conf->elist_device);
784 free_mptable(conf->mptable);
785 free_hwtable(conf->hwtable);
786 free_hwe(conf->overrides);
787 free_keywords(conf->keywords);
789 memset(conf, 0, sizeof(*conf));
792 void uninit_config(void)
794 _uninit_config(&__internal_config);
797 void free_config(struct config *conf)
801 else if (conf == &__internal_config) {
802 condlog(0, "ERROR: %s called for internal config. Use uninit_config() instead",
807 _uninit_config(conf);
811 /* if multipath fails to process the config directory, it should continue,
812 * with just a warning message */
814 process_config_dir(struct config *conf, char *dir)
816 struct dirent **namelist;
817 struct scandir_result sr;
820 int old_hwtable_size;
821 int old_pctable_size = 0;
824 condlog(1, "config_dir '%s' must be a fully qualified path",
828 n = scandir(dir, &namelist, NULL, alphasort);
831 condlog(3, "No configuration dir '%s'", dir);
833 condlog(0, "couldn't open configuration dir '%s': %s",
834 dir, strerror(errno));
840 pthread_cleanup_push_cast(free_scandir_result, &sr);
841 for (i = 0; i < n; i++) {
842 char *ext = strrchr(namelist[i]->d_name, '.');
844 if (!ext || strcmp(ext, ".conf"))
847 old_hwtable_size = VECTOR_SIZE(conf->hwtable);
848 old_pctable_size = conf->overrides ?
849 VECTOR_SIZE(conf->overrides->pctable) : 0;
850 snprintf(path, LINE_MAX, "%s/%s", dir, namelist[i]->d_name);
851 path[LINE_MAX-1] = '\0';
852 process_file(conf, path);
853 factorize_hwtable(conf->hwtable, old_hwtable_size,
854 namelist[i]->d_name);
855 validate_pctable(conf->overrides, old_pctable_size,
856 namelist[i]->d_name);
858 pthread_cleanup_pop(1);
862 static void set_max_checkint_from_watchdog(struct config *conf)
864 char *envp = getenv("WATCHDOG_USEC");
865 unsigned long checkint;
867 if (envp && sscanf(envp, "%lu", &checkint) == 1) {
868 /* Value is in microseconds */
870 if (checkint < 1 || checkint > UINT_MAX) {
871 condlog(1, "invalid value for WatchdogSec: \"%s\"", envp);
874 if (conf->max_checkint == 0 || conf->max_checkint > checkint)
875 conf->max_checkint = checkint;
876 condlog(3, "enabling watchdog, interval %ld", checkint);
877 conf->use_watchdog = true;
882 static int _init_config (const char *file, struct config *conf);
884 int init_config(const char *file)
886 return _init_config(file, &__internal_config);
889 struct config *load_config(const char *file)
891 struct config *conf = alloc_config();
893 if (conf && !_init_config(file, conf))
900 int _init_config (const char *file, struct config *conf)
904 conf = &__internal_config;
907 * Processing the config file will overwrite conf->verbosity if set
908 * When we return, we'll copy the config value back
910 conf->verbosity = libmp_verbosity;
915 get_sys_max_fds(&conf->max_fds);
916 conf->attribute_flags = 0;
917 conf->reassign_maps = DEFAULT_REASSIGN_MAPS;
918 conf->checkint = CHECKINT_UNDEF;
919 conf->use_watchdog = false;
920 conf->max_checkint = 0;
921 conf->force_sync = DEFAULT_FORCE_SYNC;
922 conf->partition_delim = (default_partition_delim != NULL ?
923 strdup(default_partition_delim) : NULL);
924 conf->processed_main_config = 0;
925 conf->find_multipaths = DEFAULT_FIND_MULTIPATHS;
926 conf->uxsock_timeout = DEFAULT_REPLY_TIMEOUT;
927 conf->retrigger_tries = DEFAULT_RETRIGGER_TRIES;
928 conf->retrigger_delay = DEFAULT_RETRIGGER_DELAY;
929 conf->uev_wait_timeout = DEFAULT_UEV_WAIT_TIMEOUT;
930 conf->remove_retries = 0;
931 conf->ghost_delay = DEFAULT_GHOST_DELAY;
932 conf->all_tg_pt = DEFAULT_ALL_TG_PT;
933 conf->recheck_wwid = DEFAULT_RECHECK_WWID;
935 * preload default hwtable
937 conf->hwtable = vector_alloc();
940 if (setup_default_hwtable(conf->hwtable))
943 #ifdef CHECK_BUILTIN_HWTABLE
944 factorize_hwtable(conf->hwtable, 0, "builtin");
947 * read the config file
949 conf->keywords = vector_alloc();
950 init_keywords(conf->keywords);
951 if (filepresent(file)) {
952 int builtin_hwtable_size;
954 builtin_hwtable_size = VECTOR_SIZE(conf->hwtable);
955 if (process_file(conf, file)) {
956 condlog(0, "error parsing config file");
959 factorize_hwtable(conf->hwtable, builtin_hwtable_size, file);
960 validate_pctable(conf->overrides, 0, file);
963 conf->processed_main_config = 1;
964 process_config_dir(conf, CONFIG_DIR);
967 * fill the voids left in the config file
970 set_max_checkint_from_watchdog(conf);
972 if (conf->max_checkint == 0) {
973 if (conf->checkint == CHECKINT_UNDEF)
974 conf->checkint = DEFAULT_CHECKINT;
975 conf->max_checkint = (conf->checkint < UINT_MAX / 4 ?
976 conf->checkint * 4 : UINT_MAX);
977 } else if (conf->checkint == CHECKINT_UNDEF)
978 conf->checkint = (conf->max_checkint >= 4 ?
979 conf->max_checkint / 4 : 1);
980 else if (conf->checkint > conf->max_checkint)
981 conf->checkint = conf->max_checkint;
982 condlog(3, "polling interval: %d, max: %d",
983 conf->checkint, conf->max_checkint);
985 if (conf->blist_devnode == NULL) {
986 conf->blist_devnode = vector_alloc();
988 if (!conf->blist_devnode)
991 if (conf->blist_wwid == NULL) {
992 conf->blist_wwid = vector_alloc();
994 if (!conf->blist_wwid)
997 if (conf->blist_device == NULL) {
998 conf->blist_device = vector_alloc();
1000 if (!conf->blist_device)
1003 if (conf->blist_property == NULL) {
1004 conf->blist_property = vector_alloc();
1006 if (!conf->blist_property)
1009 if (conf->blist_protocol == NULL) {
1010 conf->blist_protocol = vector_alloc();
1012 if (!conf->blist_protocol)
1016 if (conf->elist_devnode == NULL) {
1017 conf->elist_devnode = vector_alloc();
1019 if (!conf->elist_devnode)
1022 if (conf->elist_wwid == NULL) {
1023 conf->elist_wwid = vector_alloc();
1025 if (!conf->elist_wwid)
1029 if (conf->elist_device == NULL) {
1030 conf->elist_device = vector_alloc();
1032 if (!conf->elist_device)
1036 if (conf->elist_property == NULL) {
1037 conf->elist_property = vector_alloc();
1039 if (!conf->elist_property)
1042 if (conf->elist_protocol == NULL) {
1043 conf->elist_protocol = vector_alloc();
1045 if (!conf->elist_protocol)
1049 if (setup_default_blist(conf))
1052 if (conf->mptable == NULL) {
1053 conf->mptable = vector_alloc();
1058 merge_pctable(conf->overrides);
1059 merge_mptable(conf->mptable);
1060 merge_blacklist(conf->blist_devnode);
1061 merge_blacklist(conf->blist_property);
1062 merge_blacklist(conf->blist_wwid);
1063 merge_blacklist_device(conf->blist_device);
1064 merge_blacklist(conf->elist_devnode);
1065 merge_blacklist(conf->elist_property);
1066 merge_blacklist(conf->elist_wwid);
1067 merge_blacklist_device(conf->elist_device);
1069 libmp_verbosity = conf->verbosity;
1072 _uninit_config(conf);
1076 const char *get_uid_attribute_by_attrs(const struct config *conf,
1077 const char *path_dev)
1079 const struct _vector *uid_attrs = &conf->uid_attrs;
1083 vector_foreach_slot(uid_attrs, att, j) {
1084 col = strrchr(att, ':');
1087 if (!strncmp(path_dev, att, col - att))
1093 int parse_uid_attrs(char *uid_attrs, struct config *conf)
1095 vector attrs = &conf->uid_attrs;
1096 char *uid_attr_record, *tmp;
1102 count = get_word(uid_attrs, &uid_attr_record);
1103 while (uid_attr_record) {
1104 tmp = strchr(uid_attr_record, ':');
1106 condlog(2, "invalid record in uid_attrs: %s",
1108 free(uid_attr_record);
1110 } else if (!vector_alloc_slot(attrs)) {
1111 free(uid_attr_record);
1114 vector_set_slot(attrs, uid_attr_record);
1118 count = get_word(uid_attrs, &uid_attr_record);