diff --git a/nfs-utils-2.5.2-rc3.patch b/nfs-utils-2.5.2-rc3.patch new file mode 100644 index 0000000..1ae2690 --- /dev/null +++ b/nfs-utils-2.5.2-rc3.patch @@ -0,0 +1,2982 @@ +diff --git a/aclocal/libevent.m4 b/aclocal/libevent.m4 +index b5ac00f..e0b820b 100644 +--- a/aclocal/libevent.m4 ++++ b/aclocal/libevent.m4 +@@ -1,12 +1,12 @@ + dnl Checks for libevent + AC_DEFUN([AC_LIBEVENT], [ + +- dnl Check for libevent, but do not add -levent to LIBS +- AC_CHECK_LIB([event], [event_dispatch], [LIBEVENT=-levent], ++ dnl Check for libevent, but do not add -levent_core to LIBS ++ AC_CHECK_LIB([event_core], [event_base_dispatch], [LIBEVENT=-levent_core], + [AC_MSG_ERROR([libevent not found.])]) + AC_SUBST(LIBEVENT) + +- AC_CHECK_HEADERS([event.h], , ++ AC_CHECK_HEADERS([event2/event.h], , + [AC_MSG_ERROR([libevent headers not found.])]) + + ])dnl +diff --git a/configure.ac b/configure.ac +index 942f3c0..dbb795f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -638,13 +638,12 @@ my_am_cflags="\ + -Werror=parentheses \ + -Werror=aggregate-return \ + -Werror=unused-result \ +- -Wno-cast-function-type \ + -fno-strict-aliasing \ + " + + AC_DEFUN([CHECK_CCSUPPORT], [ + my_save_cflags="$CFLAGS" +- CFLAGS=$1 ++ CFLAGS="-Werror $1" + AC_MSG_CHECKING([whether CC supports $1]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])], + [AC_MSG_RESULT([yes])] +@@ -658,9 +657,10 @@ CHECK_CCSUPPORT([-Werror=format-overflow=2], [flg1]) + CHECK_CCSUPPORT([-Werror=int-conversion], [flg2]) + CHECK_CCSUPPORT([-Werror=incompatible-pointer-types], [flg3]) + CHECK_CCSUPPORT([-Werror=misleading-indentation], [flg4]) ++CHECK_CCSUPPORT([-Wno-cast-function-type], [flg5]) + AX_GCC_FUNC_ATTRIBUTE([format]) + +-AC_SUBST([AM_CFLAGS], ["$my_am_cflags $flg1 $flg2 $flg3 $flg4"]) ++AC_SUBST([AM_CFLAGS], ["$my_am_cflags $flg1 $flg2 $flg3 $flg4 $flg5"]) + + # Make sure that $ACLOCAL_FLAGS are used during a rebuild + AC_SUBST([ACLOCAL_AMFLAGS], ["-I $ac_macro_dir \$(ACLOCAL_FLAGS)"]) +diff --git a/support/include/xcommon.h b/support/include/xcommon.h +index 30b0403..efde83c 100644 +--- a/support/include/xcommon.h ++++ b/support/include/xcommon.h +@@ -7,7 +7,7 @@ + */ + + #ifndef _XMALLOC_H +-#define _MALLOC_H ++#define _XMALLOC_H + + #ifdef HAVE_CONFIG_H + #include +diff --git a/support/nfs/exports.c b/support/nfs/exports.c +index 97eb318..037febd 100644 +--- a/support/nfs/exports.c ++++ b/support/nfs/exports.c +@@ -838,6 +838,7 @@ struct export_features *get_export_features(void) + close(fd); + if (c == -1) + goto err; ++ buf[c] = 0; + c = sscanf(buf, "%x %x", &ef.flags, &ef.secinfo_flags); + if (c != 2) + goto err; +diff --git a/support/nfs/xlog.c b/support/nfs/xlog.c +index 687d862..86acd6a 100644 +--- a/support/nfs/xlog.c ++++ b/support/nfs/xlog.c +@@ -156,13 +156,29 @@ xlog_enabled(int fac) + void + xlog_backend(int kind, const char *fmt, va_list args) + { +- va_list args2; +- + if (!(kind & (L_ALL)) && !(logging && (kind & logmask))) + return; + +- if (log_stderr) ++ if (log_stderr) { ++ va_list args2; ++#ifdef VERBOSE_PRINTF ++ time_t now; ++ struct tm *tm; ++ ++ time(&now); ++ tm = localtime(&now); ++ fprintf(stderr, "%s[%d] %04d-%02d-%02d %02d:%02d:%02d ", ++ log_name, log_pid, ++ tm->tm_year+1900, tm->tm_mon + 1, tm->tm_mday, ++ tm->tm_hour, tm->tm_min, tm->tm_sec); ++#else ++ fprintf(stderr, "%s: ", log_name); ++#endif + va_copy(args2, args); ++ vfprintf(stderr, fmt, args2); ++ fprintf(stderr, "\n"); ++ va_end(args2); ++ } + + if (log_syslog) { + switch (kind) { +@@ -185,25 +201,6 @@ xlog_backend(int kind, const char *fmt, va_list args) + } + } + +- if (log_stderr) { +-#ifdef VERBOSE_PRINTF +- time_t now; +- struct tm *tm; +- +- time(&now); +- tm = localtime(&now); +- fprintf(stderr, "%s[%d] %04d-%02d-%02d %02d:%02d:%02d ", +- log_name, log_pid, +- tm->tm_year+1900, tm->tm_mon + 1, tm->tm_mday, +- tm->tm_hour, tm->tm_min, tm->tm_sec); +-#else +- fprintf(stderr, "%s: ", log_name); +-#endif +- vfprintf(stderr, fmt, args2); +- fprintf(stderr, "\n"); +- va_end(args2); +- } +- + if (kind == L_FATAL) + exit(1); + } +diff --git a/support/nfsidmap/libnfsidmap.c b/support/nfsidmap/libnfsidmap.c +index bce448c..0a912e5 100644 +--- a/support/nfsidmap/libnfsidmap.c ++++ b/support/nfsidmap/libnfsidmap.c +@@ -237,24 +237,40 @@ static int load_translation_plugin(char *method, struct mapping_plugin *plgn) + { + void *dl = NULL; + struct trans_func *trans = NULL; +- libnfsidmap_plugin_init_t init_func; ++ libnfsidmap_plugin_init_t init_func = NULL; + char plgname[128]; + int ret = 0; + +- snprintf(plgname, sizeof(plgname), "%s/%s.so", PATH_PLUGINS, method); ++ /* Look for library using search path first to allow overriding */ ++ snprintf(plgname, sizeof(plgname), "%s.so", method); + + dl = dlopen(plgname, RTLD_NOW | RTLD_LOCAL); +- if (dl == NULL) { +- IDMAP_LOG(1, ("libnfsidmap: Unable to load plugin: %s", +- dlerror())); +- return -1; ++ if (dl != NULL) { ++ /* Is it really one of our libraries */ ++ init_func = (libnfsidmap_plugin_init_t) dlsym(dl, PLUGIN_INIT_FUNC); ++ if (init_func == NULL) { ++ dlclose(dl); ++ dl = NULL; ++ } + } +- init_func = (libnfsidmap_plugin_init_t) dlsym(dl, PLUGIN_INIT_FUNC); +- if (init_func == NULL) { +- IDMAP_LOG(1, ("libnfsidmap: Unable to get init function: %s", +- dlerror())); +- dlclose(dl); +- return -1; ++ ++ if (dl == NULL) { ++ /* Fallback to hard-coded path */ ++ snprintf(plgname, sizeof(plgname), "%s/%s.so", PATH_PLUGINS, method); ++ ++ dl = dlopen(plgname, RTLD_NOW | RTLD_LOCAL); ++ if (dl == NULL) { ++ IDMAP_LOG(1, ("libnfsidmap: Unable to load plugin: %s: %s", ++ plgname, dlerror())); ++ return -1; ++ } ++ init_func = (libnfsidmap_plugin_init_t) dlsym(dl, PLUGIN_INIT_FUNC); ++ if (init_func == NULL) { ++ IDMAP_LOG(1, ("libnfsidmap: Unable to get init function: %s: %s", ++ plgname, dlerror())); ++ dlclose(dl); ++ return -1; ++ } + } + trans = init_func(); + if (trans == NULL) { +@@ -496,6 +512,19 @@ out: + return ret ? -ENOENT: 0; + } + ++void nfs4_term_name_mapping(void) ++{ ++ if (nfs4_plugins) ++ unload_plugins(nfs4_plugins); ++ if (gss_plugins) ++ unload_plugins(gss_plugins); ++ ++ nfs4_plugins = gss_plugins = NULL; ++ ++ free_local_realms(); ++ conf_cleanup(); ++} ++ + int + nfs4_get_default_domain(char *UNUSED(server), char *domain, size_t len) + { +diff --git a/support/nfsidmap/nfsidmap.h b/support/nfsidmap/nfsidmap.h +index 1063065..5a79568 100644 +--- a/support/nfsidmap/nfsidmap.h ++++ b/support/nfsidmap/nfsidmap.h +@@ -50,6 +50,7 @@ typedef struct _extra_mapping_params { + typedef void (*nfs4_idmap_log_function_t)(const char *, ...); + + int nfs4_init_name_mapping(char *conffile); ++void nfs4_term_name_mapping(void); + int nfs4_get_default_domain(char *server, char *domain, size_t len); + int nfs4_uid_to_name(uid_t uid, char *domain, char *name, size_t len); + int nfs4_gid_to_name(gid_t gid, char *domain, char *name, size_t len); +diff --git a/support/nfsidmap/nfsidmap_common.c b/support/nfsidmap/nfsidmap_common.c +index f89b82e..4d2cb14 100644 +--- a/support/nfsidmap/nfsidmap_common.c ++++ b/support/nfsidmap/nfsidmap_common.c +@@ -34,12 +34,21 @@ static char * toupper_str(char *s) + return s; + } + ++static struct conf_list *local_realms = NULL; ++ ++void free_local_realms(void) ++{ ++ if (local_realms) { ++ conf_free_list(local_realms); ++ local_realms = NULL; ++ } ++} ++ + /* Get list of "local equivalent" realms. Meaning the list of realms + * where john@REALM.A is considered the same user as john@REALM.B + * If not specified, default to upper-case of local domain name */ + struct conf_list *get_local_realms(void) + { +- static struct conf_list *local_realms = NULL; + if (local_realms) return local_realms; + + local_realms = conf_get_list("General", "Local-Realms"); +diff --git a/support/nfsidmap/nfsidmap_private.h b/support/nfsidmap/nfsidmap_private.h +index f1af55f..a5cb6dd 100644 +--- a/support/nfsidmap/nfsidmap_private.h ++++ b/support/nfsidmap/nfsidmap_private.h +@@ -37,6 +37,7 @@ + #include "conffile.h" + + struct conf_list *get_local_realms(void); ++void free_local_realms(void); + int get_nostrip(void); + int get_reformat_group(void); + +diff --git a/support/nfsidmap/nss.c b/support/nfsidmap/nss.c +index 9d46499..669760b 100644 +--- a/support/nfsidmap/nss.c ++++ b/support/nfsidmap/nss.c +@@ -467,6 +467,17 @@ static int nss_plugin_init(void) + return 0; + } + ++/* ++ * Called by dlclose(). See dlopen(3) man page ++ */ ++__attribute__((destructor)) ++static int nss_plugin_term(void) ++{ ++ free_local_realms(); ++ conf_cleanup(); ++ return 0; ++} ++ + + struct trans_func nss_trans = { + .name = "nsswitch", +diff --git a/systemd/nfs-server.service b/systemd/nfs-server.service +index 24118d6..06c1adb 100644 +--- a/systemd/nfs-server.service ++++ b/systemd/nfs-server.service +@@ -1,18 +1,18 @@ + [Unit] + Description=NFS server and services + DefaultDependencies=no +-Requires= network.target proc-fs-nfsd.mount +-Requires= nfs-mountd.service ++Requires=network.target proc-fs-nfsd.mount ++Requires=nfs-mountd.service + Wants=rpcbind.socket network-online.target + Wants=rpc-statd.service nfs-idmapd.service + Wants=rpc-statd-notify.service + Wants=nfsdcld.service + +-After= network-online.target local-fs.target +-After= proc-fs-nfsd.mount rpcbind.socket nfs-mountd.service +-After= nfs-idmapd.service rpc-statd.service +-After= nfsdcld.service +-Before= rpc-statd-notify.service ++After=network-online.target local-fs.target ++After=proc-fs-nfsd.mount rpcbind.socket nfs-mountd.service ++After=nfs-idmapd.service rpc-statd.service ++After=nfsdcld.service ++Before=rpc-statd-notify.service + + # GSS services dependencies and ordering + Wants=auth-rpcgss-module.service +diff --git a/tools/mountstats/mountstats.py b/tools/mountstats/mountstats.py +index 014f38a..1054f69 100755 +--- a/tools/mountstats/mountstats.py ++++ b/tools/mountstats/mountstats.py +@@ -560,7 +560,10 @@ class DeviceData: + # the reference to them. so we build new lists here + # for the result object. + for op in result.__rpc_data['ops']: +- result.__rpc_data[op] = list(map(difference, self.__rpc_data[op], old_stats.__rpc_data[op])) ++ try: ++ result.__rpc_data[op] = list(map(difference, self.__rpc_data[op], old_stats.__rpc_data[op])) ++ except KeyError: ++ continue + + # update the remaining keys + if protocol == 'udp': +diff --git a/tools/nfs-iostat/nfs-iostat.py b/tools/nfs-iostat/nfs-iostat.py +index b7e98a2..5556f69 100755 +--- a/tools/nfs-iostat/nfs-iostat.py ++++ b/tools/nfs-iostat/nfs-iostat.py +@@ -213,8 +213,11 @@ class DeviceData: + # the reference to them. so we build new lists here + # for the result object. + for op in result.__rpc_data['ops']: +- result.__rpc_data[op] = list(map( +- difference, self.__rpc_data[op], old_stats.__rpc_data[op])) ++ try: ++ result.__rpc_data[op] = list(map( ++ difference, self.__rpc_data[op], old_stats.__rpc_data[op])) ++ except KeyError: ++ continue + + # update the remaining keys we care about + result.__rpc_data['rpcsends'] -= old_stats.__rpc_data['rpcsends'] +diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c +index a04a789..cde5e51 100644 +--- a/utils/exportfs/exportfs.c ++++ b/utils/exportfs/exportfs.c +@@ -85,8 +85,11 @@ grab_lockfile() + static void + release_lockfile() + { +- if (_lockfd != -1) ++ if (_lockfd != -1) { + lockf(_lockfd, F_ULOCK, 0); ++ close(_lockfd); ++ _lockfd = -1; ++ } + } + + int +@@ -184,6 +187,7 @@ main(int argc, char **argv) + xtab_export_read(); + dump(f_verbose, f_export_format); + free_state_path_names(&etab); ++ export_freeall(); + return 0; + } + } +@@ -225,6 +229,7 @@ main(int argc, char **argv) + xtab_export_write(); + cache_flush(force_flush); + free_state_path_names(&etab); ++ export_freeall(); + + return export_errno; + } +diff --git a/utils/gssd/Makefile.am b/utils/gssd/Makefile.am +index 321046b..21d3bb8 100644 +--- a/utils/gssd/Makefile.am ++++ b/utils/gssd/Makefile.am +@@ -67,7 +67,6 @@ gssd_CFLAGS = \ + svcgssd_SOURCES = \ + $(COMMON_SRCS) \ + svcgssd.c \ +- svcgssd_main_loop.c \ + svcgssd_mech2file.c \ + svcgssd_proc.c \ + svcgssd_krb5.c \ +@@ -78,6 +77,7 @@ svcgssd_SOURCES = \ + svcgssd_LDADD = \ + ../../support/nfs/libnfs.la \ + ../../support/nfsidmap/libnfsidmap.la \ ++ $(LIBEVENT) \ + $(RPCSECGSS_LIBS) \ + $(KRBLIBS) $(GSSAPI_LIBS) $(LIBTIRPC) + +diff --git a/utils/gssd/gss_names.c b/utils/gssd/gss_names.c +index 2a7f3a1..982b96f 100644 +--- a/utils/gssd/gss_names.c ++++ b/utils/gssd/gss_names.c +@@ -110,10 +110,12 @@ get_hostbased_client_name(gss_name_t client_name, gss_OID mech, + /* For Kerberos, transform the NT_KRB5_PRINCIPAL name to + * an NT_HOSTBASED_SERVICE name */ + if (g_OID_equal(&krb5oid, mech)) { +- if (get_krb5_hostbased_name(&name, &cname) == 0) +- *hostbased_name = cname; ++ if (get_krb5_hostbased_name(&name, &cname) != 0) ++ goto out_rel_buf; ++ *hostbased_name = cname; + } else { + printerr(1, "WARNING: unknown/unsupport mech OID\n"); ++ goto out_rel_buf; + } + + res = 0; +diff --git a/utils/gssd/gss_util.c b/utils/gssd/gss_util.c +index 2e6d40f..a4b2777 100644 +--- a/utils/gssd/gss_util.c ++++ b/utils/gssd/gss_util.c +@@ -339,3 +339,9 @@ out: + return retval; + } + ++void ++gssd_cleanup(void) ++{ ++ u_int32_t min_stat; ++ gss_release_cred(&min_stat, &gssd_creds); ++} +diff --git a/utils/gssd/gss_util.h b/utils/gssd/gss_util.h +index aa9f778..4da64e3 100644 +--- a/utils/gssd/gss_util.h ++++ b/utils/gssd/gss_util.h +@@ -41,6 +41,7 @@ int gssd_acquire_cred(char *server_name, const gss_OID oid); + void pgsserr(char *msg, u_int32_t maj_stat, u_int32_t min_stat, + const gss_OID mech); + int gssd_check_mechs(void); ++void gssd_cleanup(void); + + #ifndef HAVE_LIBGSSGLUE + #include +diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c +index 588da0f..85bc4b0 100644 +--- a/utils/gssd/gssd.c ++++ b/utils/gssd/gssd.c +@@ -64,7 +64,7 @@ + #include + #include + #include +-#include ++#include + + #include "gssd.h" + #include "err_util.h" +@@ -77,7 +77,7 @@ static char *pipefs_path = GSSD_PIPEFS_DIR; + static DIR *pipefs_dir; + static int pipefs_fd; + static int inotify_fd; +-struct event inotify_ev; ++struct event *inotify_ev; + + char *keytabfile = GSSD_DEFAULT_KEYTAB_FILE; + char **ccachesearch; +@@ -90,9 +90,9 @@ char *ccachedir = NULL; + /* Avoid DNS reverse lookups on server names */ + static bool avoid_dns = true; + static bool use_gssproxy = false; +-int thread_started = false; +-pthread_mutex_t pmutex = PTHREAD_MUTEX_INITIALIZER; +-pthread_cond_t pcond = PTHREAD_COND_INITIALIZER; ++pthread_mutex_t clp_lock = PTHREAD_MUTEX_INITIALIZER; ++static bool signal_received = false; ++static struct event_base *evbase = NULL; + + TAILQ_HEAD(topdir_list_head, topdir) topdir_list; + +@@ -359,20 +359,28 @@ out: + free(port); + } + ++/* Actually frees clp and fields that might be used from other ++ * threads if was last reference. ++ */ + static void +-gssd_destroy_client(struct clnt_info *clp) ++gssd_free_client(struct clnt_info *clp) + { +- if (clp->krb5_fd >= 0) { ++ int refcnt; ++ ++ pthread_mutex_lock(&clp_lock); ++ refcnt = --clp->refcount; ++ pthread_mutex_unlock(&clp_lock); ++ if (refcnt > 0) ++ return; ++ ++ printerr(3, "freeing client %s\n", clp->relpath); ++ ++ if (clp->krb5_fd >= 0) + close(clp->krb5_fd); +- event_del(&clp->krb5_ev); +- } + +- if (clp->gssd_fd >= 0) { ++ if (clp->gssd_fd >= 0) + close(clp->gssd_fd); +- event_del(&clp->gssd_ev); +- } + +- inotify_rm_watch(inotify_fd, clp->wd); + free(clp->relpath); + free(clp->servicename); + free(clp->servername); +@@ -380,6 +388,30 @@ gssd_destroy_client(struct clnt_info *clp) + free(clp); + } + ++/* Called when removing from clnt_list to tear down event handling. ++ * Will then free clp if was last reference. ++ */ ++static void ++gssd_destroy_client(struct clnt_info *clp) ++{ ++ printerr(3, "destroying client %s\n", clp->relpath); ++ ++ if (clp->krb5_ev) { ++ event_del(clp->krb5_ev); ++ event_free(clp->krb5_ev); ++ clp->krb5_ev = NULL; ++ } ++ ++ if (clp->gssd_ev) { ++ event_del(clp->gssd_ev); ++ event_free(clp->gssd_ev); ++ clp->gssd_ev = NULL; ++ } ++ ++ inotify_rm_watch(inotify_fd, clp->wd); ++ gssd_free_client(clp); ++} ++ + static void gssd_scan(void); + + static int +@@ -416,11 +448,21 @@ static struct clnt_upcall_info *alloc_upcall_info(struct clnt_info *clp) + info = malloc(sizeof(struct clnt_upcall_info)); + if (info == NULL) + return NULL; ++ ++ pthread_mutex_lock(&clp_lock); ++ clp->refcount++; ++ pthread_mutex_unlock(&clp_lock); + info->clp = clp; + + return info; + } + ++void free_upcall_info(struct clnt_upcall_info *info) ++{ ++ gssd_free_client(info->clp); ++ free(info); ++} ++ + /* For each upcall read the upcall info into the buffer, then create a + * thread in a detached state so that resources are released back into + * the system without the need for a join. +@@ -438,13 +480,13 @@ gssd_clnt_gssd_cb(int UNUSED(fd), short UNUSED(which), void *data) + info->lbuflen = read(clp->gssd_fd, info->lbuf, sizeof(info->lbuf)); + if (info->lbuflen <= 0 || info->lbuf[info->lbuflen-1] != '\n') { + printerr(0, "WARNING: %s: failed reading request\n", __func__); +- free(info); ++ free_upcall_info(info); + return; + } + info->lbuf[info->lbuflen-1] = 0; + + if (start_upcall_thread(handle_gssd_upcall, info)) +- free(info); ++ free_upcall_info(info); + } + + static void +@@ -461,12 +503,12 @@ gssd_clnt_krb5_cb(int UNUSED(fd), short UNUSED(which), void *data) + sizeof(info->uid)) < (ssize_t)sizeof(info->uid)) { + printerr(0, "WARNING: %s: failed reading uid from krb5 " + "upcall pipe: %s\n", __func__, strerror(errno)); +- free(info); ++ free_upcall_info(info); + return; + } + + if (start_upcall_thread(handle_krb5_upcall, info)) +- free(info); ++ free_upcall_info(info); + } + + static struct clnt_info * +@@ -478,6 +520,8 @@ gssd_get_clnt(struct topdir *tdi, const char *name) + if (!strcmp(clp->name, name)) + return clp; + ++ printerr(3, "creating client %s/%s\n", tdi->name, name); ++ + clp = calloc(1, sizeof(struct clnt_info)); + if (!clp) { + printerr(0, "ERROR: can't malloc clnt_info: %s\n", +@@ -501,6 +545,7 @@ gssd_get_clnt(struct topdir *tdi, const char *name) + clp->name = clp->relpath + strlen(tdi->name) + 1; + clp->krb5_fd = -1; + clp->gssd_fd = -1; ++ clp->refcount = 1; + + TAILQ_INSERT_HEAD(&tdi->clnt_list, clp, list); + return clp; +@@ -515,11 +560,8 @@ static int + gssd_scan_clnt(struct clnt_info *clp) + { + int clntfd; +- bool gssd_was_closed; +- bool krb5_was_closed; + +- gssd_was_closed = clp->gssd_fd < 0 ? true : false; +- krb5_was_closed = clp->krb5_fd < 0 ? true : false; ++ printerr(3, "scanning client %s\n", clp->relpath); + + clntfd = openat(pipefs_fd, clp->relpath, O_RDONLY); + if (clntfd < 0) { +@@ -535,16 +577,30 @@ gssd_scan_clnt(struct clnt_info *clp) + if (clp->gssd_fd == -1 && clp->krb5_fd == -1) + clp->krb5_fd = openat(clntfd, "krb5", O_RDWR | O_NONBLOCK); + +- if (gssd_was_closed && clp->gssd_fd >= 0) { +- event_set(&clp->gssd_ev, clp->gssd_fd, EV_READ | EV_PERSIST, +- gssd_clnt_gssd_cb, clp); +- event_add(&clp->gssd_ev, NULL); ++ if (!clp->gssd_ev && clp->gssd_fd >= 0) { ++ clp->gssd_ev = event_new(evbase, clp->gssd_fd, EV_READ | EV_PERSIST, ++ gssd_clnt_gssd_cb, clp); ++ if (!clp->gssd_ev) { ++ printerr(0, "ERROR: %s: can't create gssd event for %s: %s\n", ++ __FUNCTION__, clp->relpath, strerror(errno)); ++ close(clp->gssd_fd); ++ clp->gssd_fd = -1; ++ } else { ++ event_add(clp->gssd_ev, NULL); ++ } + } + +- if (krb5_was_closed && clp->krb5_fd >= 0) { +- event_set(&clp->krb5_ev, clp->krb5_fd, EV_READ | EV_PERSIST, +- gssd_clnt_krb5_cb, clp); +- event_add(&clp->krb5_ev, NULL); ++ if (!clp->krb5_ev && clp->krb5_fd >= 0) { ++ clp->krb5_ev = event_new(evbase, clp->krb5_fd, EV_READ | EV_PERSIST, ++ gssd_clnt_krb5_cb, clp); ++ if (!clp->krb5_ev) { ++ printerr(0, "ERROR: %s: can't create krb5 event for %s: %s\n", ++ __FUNCTION__, clp->relpath, strerror(errno)); ++ close(clp->krb5_fd); ++ clp->krb5_fd = -1; ++ } else { ++ event_add(clp->krb5_ev, NULL); ++ } + } + + if (clp->krb5_fd == -1 && clp->gssd_fd == -1) +@@ -651,7 +707,7 @@ gssd_scan_topdir(const char *name) + if (clp->scanned) + continue; + +- printerr(3, "destroying client %s\n", clp->relpath); ++ printerr(3, "orphaned client %s\n", clp->relpath); + saveprev = clp->list.tqe_prev; + TAILQ_REMOVE(&tdi->clnt_list, clp, list); + gssd_destroy_client(clp); +@@ -748,12 +804,16 @@ gssd_inotify_clnt(struct topdir *tdi, struct clnt_info *clp, const struct inotif + } else if (ev->mask & IN_DELETE) { + if (!strcmp(ev->name, "gssd") && clp->gssd_fd >= 0) { + close(clp->gssd_fd); +- event_del(&clp->gssd_ev); ++ event_del(clp->gssd_ev); ++ event_free(clp->gssd_ev); ++ clp->gssd_ev = NULL; + clp->gssd_fd = -1; + + } else if (!strcmp(ev->name, "krb5") && clp->krb5_fd >= 0) { + close(clp->krb5_fd); +- event_del(&clp->krb5_ev); ++ event_del(clp->krb5_ev); ++ event_free(clp->krb5_ev); ++ clp->krb5_ev = NULL; + clp->krb5_fd = -1; + } + +@@ -826,10 +886,15 @@ found: + static void + sig_die(int signal) + { +- if (root_uses_machine_creds) +- gssd_destroy_krb5_machine_creds(); ++ if (signal_received) { ++ gssd_destroy_krb5_principals(root_uses_machine_creds); ++ printerr(1, "forced exiting on signal %d\n", signal); ++ exit(0); ++ } ++ ++ signal_received = true; + printerr(1, "exiting on signal %d\n", signal); +- exit(0); ++ event_base_loopexit(evbase, NULL); + } + + static void +@@ -886,9 +951,10 @@ main(int argc, char *argv[]) + int rpc_verbosity = 0; + int opt; + int i; ++ int rc; + extern char *optarg; + char *progname; +- struct event sighup_ev; ++ struct event *sighup_ev; + + read_gss_conf(); + +@@ -1027,7 +1093,11 @@ main(int argc, char *argv[]) + if (gssd_check_mechs() != 0) + errx(1, "Problem with gssapi library"); + +- event_init(); ++ evbase = event_base_new(); ++ if (!evbase) { ++ printerr(0, "ERROR: failed to create event base: %s\n", strerror(errno)); ++ exit(EXIT_FAILURE); ++ } + + pipefs_dir = opendir(pipefs_path); + if (!pipefs_dir) { +@@ -1049,18 +1119,51 @@ main(int argc, char *argv[]) + + signal(SIGINT, sig_die); + signal(SIGTERM, sig_die); +- signal_set(&sighup_ev, SIGHUP, gssd_scan_cb, NULL); +- signal_add(&sighup_ev, NULL); +- event_set(&inotify_ev, inotify_fd, EV_READ | EV_PERSIST, gssd_inotify_cb, NULL); +- event_add(&inotify_ev, NULL); ++ sighup_ev = evsignal_new(evbase, SIGHUP, gssd_scan_cb, NULL); ++ if (!sighup_ev) { ++ printerr(0, "ERROR: failed to create SIGHUP event: %s\n", strerror(errno)); ++ exit(EXIT_FAILURE); ++ } ++ evsignal_add(sighup_ev, NULL); ++ inotify_ev = event_new(evbase, inotify_fd, EV_READ | EV_PERSIST, ++ gssd_inotify_cb, NULL); ++ if (!inotify_ev) { ++ printerr(0, "ERROR: failed to create inotify event: %s\n", strerror(errno)); ++ exit(EXIT_FAILURE); ++ } ++ event_add(inotify_ev, NULL); + + TAILQ_INIT(&topdir_list); + gssd_scan(); + daemon_ready(); + +- event_dispatch(); ++ rc = event_base_dispatch(evbase); + +- printerr(0, "ERROR: event_dispatch() returned!\n"); +- return EXIT_FAILURE; +-} ++ printerr(0, "event_dispatch() returned %i!\n", rc); ++ ++ gssd_destroy_krb5_principals(root_uses_machine_creds); ++ ++ while (!TAILQ_EMPTY(&topdir_list)) { ++ struct topdir *tdi = TAILQ_FIRST(&topdir_list); ++ TAILQ_REMOVE(&topdir_list, tdi, list); ++ while (!TAILQ_EMPTY(&tdi->clnt_list)) { ++ struct clnt_info *clp = TAILQ_FIRST(&tdi->clnt_list); ++ TAILQ_REMOVE(&tdi->clnt_list, clp, list); ++ gssd_destroy_client(clp); ++ } ++ free(tdi); ++ } ++ ++ event_free(inotify_ev); ++ event_free(sighup_ev); ++ event_base_free(evbase); + ++ close(inotify_fd); ++ close(pipefs_fd); ++ closedir(pipefs_dir); ++ ++ free(preferred_realm); ++ free(ccachesearch); ++ ++ return rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS; ++} +diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h +index f4f5975..1e8c58d 100644 +--- a/utils/gssd/gssd.h ++++ b/utils/gssd/gssd.h +@@ -62,13 +62,10 @@ extern int root_uses_machine_creds; + extern unsigned int context_timeout; + extern unsigned int rpc_timeout; + extern char *preferred_realm; +-extern pthread_mutex_t ple_lock; +-extern pthread_cond_t pcond; +-extern pthread_mutex_t pmutex; +-extern int thread_started; + + struct clnt_info { + TAILQ_ENTRY(clnt_info) list; ++ int refcount; + int wd; + bool scanned; + char *name; +@@ -79,9 +76,9 @@ struct clnt_info { + int vers; + char *protocol; + int krb5_fd; +- struct event krb5_ev; ++ struct event *krb5_ev; + int gssd_fd; +- struct event gssd_ev; ++ struct event *gssd_ev; + struct sockaddr_storage addr; + }; + +@@ -94,6 +91,7 @@ struct clnt_upcall_info { + + void handle_krb5_upcall(struct clnt_upcall_info *clp); + void handle_gssd_upcall(struct clnt_upcall_info *clp); ++void free_upcall_info(struct clnt_upcall_info *info); + + + #endif /* _RPC_GSSD_H_ */ +diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c +index 8fe6605..e830f49 100644 +--- a/utils/gssd/gssd_proc.c ++++ b/utils/gssd/gssd_proc.c +@@ -149,9 +149,10 @@ do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd, + char *buf = NULL, *p = NULL, *end = NULL; + unsigned int timeout = context_timeout; + unsigned int buf_size = 0; ++ pthread_t tid = pthread_self(); + +- printerr(2, "doing downcall: lifetime_rec=%u acceptor=%.*s\n", +- lifetime_rec, acceptor->length, acceptor->value); ++ printerr(2, "do_downcall(0x%x): lifetime_rec=%u acceptor=%.*s\n", ++ tid, lifetime_rec, acceptor->length, acceptor->value); + buf_size = sizeof(uid) + sizeof(timeout) + sizeof(pd->pd_seq_win) + + sizeof(pd->pd_ctx_hndl.length) + pd->pd_ctx_hndl.length + + sizeof(context_token->length) + context_token->length + +@@ -177,7 +178,7 @@ do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd, + return; + out_err: + free(buf); +- printerr(1, "Failed to write downcall!\n"); ++ printerr(1, "do_downcall(0x%x): Failed to write downcall!\n", tid); + return; + } + +@@ -231,7 +232,7 @@ populate_port(struct sockaddr *sa, const socklen_t salen, + switch (sa->sa_family) { + case AF_INET: + if (s4->sin_port != 0) { +- printerr(2, "DEBUG: port already set to %d\n", ++ printerr(4, "DEBUG: port already set to %d\n", + ntohs(s4->sin_port)); + return 1; + } +@@ -239,7 +240,7 @@ populate_port(struct sockaddr *sa, const socklen_t salen, + #ifdef IPV6_SUPPORTED + case AF_INET6: + if (s6->sin6_port != 0) { +- printerr(2, "DEBUG: port already set to %d\n", ++ printerr(4, "DEBUG: port already set to %d\n", + ntohs(s6->sin6_port)); + return 1; + } +@@ -548,7 +549,7 @@ krb5_use_machine_creds(struct clnt_info *clp, uid_t uid, + uid, tgtname); + + do { +- gssd_refresh_krb5_machine_credential(clp->servername, NULL, ++ gssd_refresh_krb5_machine_credential(clp->servername, + service, srchost); + /* + * Get a list of credential cache names and try each +@@ -730,7 +731,7 @@ handle_krb5_upcall(struct clnt_upcall_info *info) + printerr(2, "\n%s: uid %d (%s)\n", __func__, info->uid, clp->relpath); + + process_krb5_upcall(clp, info->uid, clp->krb5_fd, NULL, NULL, NULL); +- free(info); ++ free_upcall_info(info); + } + + void +@@ -747,8 +748,10 @@ handle_gssd_upcall(struct clnt_upcall_info *info) + char *enctypes = NULL; + char *upcall_str; + char *pbuf = info->lbuf; ++ pthread_t tid = pthread_self(); + +- printerr(2, "\n%s: '%s' (%s)\n", __func__, info->lbuf, clp->relpath); ++ printerr(2, "\n%s(0x%x): '%s' (%s)\n", __func__, tid, ++ info->lbuf, clp->relpath); + + upcall_str = strdup(info->lbuf); + if (upcall_str == NULL) { +@@ -830,6 +833,6 @@ handle_gssd_upcall(struct clnt_upcall_info *info) + out: + free(upcall_str); + out_nomem: +- free(info); ++ free_upcall_info(info); + return; + } +diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c +index 8c73748..9bd74b0 100644 +--- a/utils/gssd/krb5_util.c ++++ b/utils/gssd/krb5_util.c +@@ -130,9 +130,28 @@ + #include "gss_util.h" + #include "krb5_util.h" + ++/* ++ * List of principals from our keytab that we ++ * will try to use to obtain credentials ++ * (known as a principal list entry (ple)) ++ */ ++struct gssd_k5_kt_princ { ++ struct gssd_k5_kt_princ *next; ++ // Only protect against deletion, not modification ++ int refcount; ++ // Only set during creation in new_ple() ++ krb5_principal princ; ++ char *realm; ++ // Modified during usage by gssd_get_single_krb5_cred() ++ char *ccname; ++ krb5_timestamp endtime; ++}; ++ ++ + /* Global list of principals/cache file names for machine credentials */ +-struct gssd_k5_kt_princ *gssd_k5_kt_princ_list = NULL; +-pthread_mutex_t ple_lock = PTHREAD_MUTEX_INITIALIZER; ++static struct gssd_k5_kt_princ *gssd_k5_kt_princ_list = NULL; ++/* This mutex protects list modification & ple->ccname */ ++static pthread_mutex_t ple_lock = PTHREAD_MUTEX_INITIALIZER; + + #ifdef HAVE_SET_ALLOWABLE_ENCTYPES + int limit_to_legacy_enctypes = 0; +@@ -146,10 +165,22 @@ static int select_krb5_ccache(const struct dirent *d); + static int gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, + const char **cctype, struct dirent **d); + static int gssd_get_single_krb5_cred(krb5_context context, +- krb5_keytab kt, struct gssd_k5_kt_princ *ple, int nocache); ++ krb5_keytab kt, struct gssd_k5_kt_princ *ple); + static int query_krb5_ccache(const char* cred_cache, char **ret_princname, + char **ret_realm); + ++static void release_ple(krb5_context context, struct gssd_k5_kt_princ *ple) ++{ ++ if (--ple->refcount) ++ return; ++ ++ printerr(3, "freeing cached principal (ccname=%s, realm=%s)\n", ple->ccname, ple->realm); ++ krb5_free_principal(context, ple->princ); ++ free(ple->ccname); ++ free(ple->realm); ++ free(ple); ++} ++ + /* + * Called from the scandir function to weed out potential krb5 + * credentials cache files +@@ -192,7 +223,8 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, + int found = 0; + struct dirent *best_match_dir = NULL; + struct stat best_match_stat, tmp_stat; +- char buf[PATH_MAX+4+2+256]; ++ /* dirname + cctype + d_name + NULL */ ++ char buf[PATH_MAX+5+256+1]; + char *princname = NULL; + char *realm = NULL; + int score, best_match_score = 0, err = -EACCES; +@@ -349,8 +381,7 @@ gssd_check_if_cc_exists(struct gssd_k5_kt_princ *ple) + static int + gssd_get_single_krb5_cred(krb5_context context, + krb5_keytab kt, +- struct gssd_k5_kt_princ *ple, +- int nocache) ++ struct gssd_k5_kt_princ *ple) + { + #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS + krb5_get_init_creds_opt *init_opts = NULL; +@@ -367,22 +398,26 @@ gssd_get_single_krb5_cred(krb5_context context, + char *cache_type; + char *pname = NULL; + char *k5err = NULL; ++ int nocache = 0; + + memset(&my_creds, 0, sizeof(my_creds)); + +- if (!nocache && !use_memcache) ++ if (!use_memcache) + nocache = gssd_check_if_cc_exists(ple); + /* + * Workaround for clock skew among NFS server, NFS client and KDC + * 300 because clock skew must be within 300sec for kerberos + */ + now += 300; ++ pthread_mutex_lock(&ple_lock); + if (ple->ccname && ple->endtime > now && !nocache) { + printerr(3, "INFO: Credentials in CC '%s' are good until %d\n", + ple->ccname, ple->endtime); + code = 0; ++ pthread_mutex_unlock(&ple_lock); + goto out; + } ++ pthread_mutex_unlock(&ple_lock); + + if ((code = krb5_kt_get_name(context, kt, kt_name, BUFSIZ))) { + printerr(0, "ERROR: Unable to get keytab name in " +@@ -435,6 +470,7 @@ gssd_get_single_krb5_cred(krb5_context context, + * Initialize cache file which we're going to be using + */ + ++ pthread_mutex_lock(&ple_lock); + if (use_memcache) + cache_type = "MEMORY"; + else +@@ -444,15 +480,18 @@ gssd_get_single_krb5_cred(krb5_context context, + ccachesearch[0], GSSD_DEFAULT_CRED_PREFIX, + GSSD_DEFAULT_MACHINE_CRED_SUFFIX, ple->realm); + ple->endtime = my_creds.times.endtime; +- if (ple->ccname != NULL) ++ if (ple->ccname == NULL || strcmp(ple->ccname, cc_name) != 0) { + free(ple->ccname); +- ple->ccname = strdup(cc_name); +- if (ple->ccname == NULL) { +- printerr(0, "ERROR: no storage to duplicate credentials " +- "cache name '%s'\n", cc_name); +- code = ENOMEM; +- goto out; ++ ple->ccname = strdup(cc_name); ++ if (ple->ccname == NULL) { ++ printerr(0, "ERROR: no storage to duplicate credentials " ++ "cache name '%s'\n", cc_name); ++ code = ENOMEM; ++ pthread_mutex_unlock(&ple_lock); ++ goto out; ++ } + } ++ pthread_mutex_unlock(&ple_lock); + if ((code = krb5_cc_resolve(context, cc_name, &ccache))) { + k5err = gssd_k5_err_msg(context, code); + printerr(0, "ERROR: %s while opening credential cache '%s'\n", +@@ -484,12 +523,13 @@ gssd_get_single_krb5_cred(krb5_context context, + if (ccache) + krb5_cc_close(context, ccache); + krb5_free_cred_contents(context, &my_creds); +- krb5_free_string(context, k5err); ++ free(k5err); + return (code); + } + + /* + * Given a principal, find a matching ple structure ++ * Called with mutex held + */ + static struct gssd_k5_kt_princ * + find_ple_by_princ(krb5_context context, krb5_principal princ) +@@ -506,6 +546,7 @@ find_ple_by_princ(krb5_context context, krb5_principal princ) + + /* + * Create, initialize, and add a new ple structure to the global list ++ * Called with mutex held + */ + static struct gssd_k5_kt_princ * + new_ple(krb5_context context, krb5_principal princ) +@@ -557,6 +598,7 @@ new_ple(krb5_context context, krb5_principal princ) + p->next = ple; + } + ++ ple->refcount = 1; + return ple; + outerr: + if (ple) { +@@ -575,13 +617,14 @@ get_ple_by_princ(krb5_context context, krb5_principal princ) + { + struct gssd_k5_kt_princ *ple; + +- /* Need to serialize list if we ever become multi-threaded! */ +- + pthread_mutex_lock(&ple_lock); + ple = find_ple_by_princ(context, princ); + if (ple == NULL) { + ple = new_ple(context, princ); + } ++ if (ple != NULL) { ++ ple->refcount++; ++ } + pthread_mutex_unlock(&ple_lock); + + return ple; +@@ -715,6 +758,7 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt, + goto out; + } + ++ printerr(4, "Scanning keytab for %s/*@%s\n", service, realm); + while ((code = krb5_kt_next_entry(context, kt, kte, &cursor)) == 0) { + if ((code = krb5_unparse_name(context, kte->principal, + &pname))) { +@@ -723,7 +767,7 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt, + "we failed to unparse principal name: %s\n", + k5err); + k5_free_kt_entry(context, kte); +- krb5_free_string(context, k5err); ++ free(k5err); + k5err = NULL; + continue; + } +@@ -746,6 +790,8 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt, + retval = ENOMEM; + k5_free_kt_entry(context, kte); + } else { ++ release_ple(context, ple); ++ ple = NULL; + retval = 0; + *found = 1; + } +@@ -770,7 +816,7 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt, + if (retval < 0) + retval = 0; + out: +- krb5_free_string(context, k5err); ++ free(k5err); + return retval; + } + +@@ -811,41 +857,42 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, + /* Get full local hostname */ + if (srchost) { + strcpy(myhostname, srchost); +- } else if (gethostname(myhostname, sizeof(myhostname)) == -1) { +- retval = errno; +- k5err = gssd_k5_err_msg(context, retval); +- printerr(1, "%s while getting local hostname\n", k5err); +- goto out; ++ strcpy(myhostad, myhostname); ++ } else { ++ /* Borrow myhostad for gethostname(), we need it later anyways */ ++ if (gethostname(myhostad, sizeof(myhostad)-1) == -1) { ++ retval = errno; ++ k5err = gssd_k5_err_msg(context, retval); ++ printerr(1, "%s while getting local hostname\n", k5err); ++ goto out; ++ } ++ retval = get_full_hostname(myhostad, myhostname, sizeof(myhostname)); ++ if (retval) { ++ /* Don't use myhostname */ ++ myhostname[0] = 0; ++ } + } + + /* Compute the active directory machine name HOST$ */ +- krb5_appdefault_string(context, "nfs", NULL, "ad_principal_name", ++ krb5_appdefault_string(context, "nfs", NULL, "ad_principal_name", + notsetstr, &adhostoverride); +- if (strcmp(adhostoverride, notsetstr) != 0) { +- printerr (1, +- "AD host string overridden with \"%s\" from appdefaults\n", +- adhostoverride); +- /* No overflow: Windows cannot handle strings longer than 19 chars */ +- strcpy(myhostad, adhostoverride); ++ if (adhostoverride && strcmp(adhostoverride, notsetstr) != 0) { ++ printerr(1, ++ "AD host string overridden with \"%s\" from appdefaults\n", ++ adhostoverride); ++ /* No overflow: Windows cannot handle strings longer than 19 chars */ ++ strcpy(myhostad, adhostoverride); + } else { +- strcpy(myhostad, myhostname); +- for (i = 0; myhostad[i] != 0; ++i) { +- if (myhostad[i] == '.') break; +- } +- myhostad[i] = '$'; +- myhostad[i+1] = 0; ++ /* In this case, it's been pre-filled above */ ++ for (i = 0; myhostad[i] != 0; ++i) { ++ if (myhostad[i] == '.') break; ++ } ++ myhostad[i] = '$'; ++ myhostad[i+1] = 0; + } + if (adhostoverride) + krb5_free_string(context, adhostoverride); + +- if (!srchost) { +- retval = get_full_hostname(myhostname, myhostname, sizeof(myhostname)); +- if (retval) { +- /* Don't use myhostname */ +- myhostname[0] = 0; +- } +- } +- + code = krb5_get_default_realm(context, &default_realm); + if (code) { + retval = code; +@@ -927,7 +974,7 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, + k5err = gssd_k5_err_msg(context, code); + printerr(1, "%s while building principal for '%s'\n", + k5err, spn); +- krb5_free_string(context, k5err); ++ free(k5err); + k5err = NULL; + continue; + } +@@ -937,7 +984,7 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, + k5err = gssd_k5_err_msg(context, code); + printerr(3, "%s while getting keytab entry for '%s'\n", + k5err, spn); +- krb5_free_string(context, k5err); ++ free(k5err); + k5err = NULL; + /* + * We tried the active directory machine account +@@ -986,7 +1033,7 @@ out: + k5_free_default_realm(context, default_realm); + if (realmnames) + krb5_free_host_realm(context, realmnames); +- krb5_free_string(context, k5err); ++ free(k5err); + return retval; + } + +@@ -1078,6 +1125,93 @@ err_cache: + return (*ret_princname && *ret_realm); + } + ++/* ++ * Obtain (or refresh if necessary) Kerberos machine credentials ++ * If a ple is passed in, it's reference will be released ++ */ ++static int ++gssd_refresh_krb5_machine_credential_internal(char *hostname, ++ struct gssd_k5_kt_princ *ple, ++ char *service, char *srchost) ++{ ++ krb5_error_code code = 0; ++ krb5_context context; ++ krb5_keytab kt = NULL;; ++ int retval = 0; ++ char *k5err = NULL; ++ const char *svcnames[] = { "$", "root", "nfs", "host", NULL }; ++ ++ printerr(2, "%s: hostname=%s ple=%p service=%s srchost=%s\n", ++ __func__, hostname, ple, service, srchost); ++ ++ /* ++ * If a specific service name was specified, use it. ++ * Otherwise, use the default list. ++ */ ++ if (service != NULL && strcmp(service, "*") != 0) { ++ svcnames[0] = service; ++ svcnames[1] = NULL; ++ } ++ if (hostname == NULL && ple == NULL) ++ return EINVAL; ++ ++ code = krb5_init_context(&context); ++ if (code) { ++ k5err = gssd_k5_err_msg(NULL, code); ++ printerr(0, "ERROR: %s: %s while initializing krb5 context\n", ++ __func__, k5err); ++ retval = code; ++ goto out; ++ } ++ ++ if ((code = krb5_kt_resolve(context, keytabfile, &kt))) { ++ k5err = gssd_k5_err_msg(context, code); ++ printerr(0, "ERROR: %s: %s while resolving keytab '%s'\n", ++ __func__, k5err, keytabfile); ++ goto out_free_context; ++ } ++ ++ if (ple == NULL) { ++ krb5_keytab_entry kte; ++ ++ code = find_keytab_entry(context, kt, srchost, hostname, ++ &kte, svcnames); ++ if (code) { ++ printerr(0, "ERROR: %s: no usable keytab entry found " ++ "in keytab %s for connection with host %s\n", ++ __FUNCTION__, keytabfile, hostname); ++ retval = code; ++ goto out_free_kt; ++ } ++ ++ ple = get_ple_by_princ(context, kte.principal); ++ k5_free_kt_entry(context, &kte); ++ if (ple == NULL) { ++ char *pname; ++ if ((krb5_unparse_name(context, kte.principal, &pname))) { ++ pname = NULL; ++ } ++ printerr(0, "ERROR: %s: Could not locate or create " ++ "ple struct for principal %s for connection " ++ "with host %s\n", ++ __FUNCTION__, pname ? pname : "", ++ hostname); ++ if (pname) k5_free_unparsed_name(context, pname); ++ goto out_free_kt; ++ } ++ } ++ retval = gssd_get_single_krb5_cred(context, kt, ple); ++out_free_kt: ++ krb5_kt_close(context, kt); ++out_free_context: ++ if (ple) ++ release_ple(context, ple); ++ krb5_free_context(context); ++out: ++ free(k5err); ++ return retval; ++} ++ + /*==========================*/ + /*=== External routines ===*/ + /*==========================*/ +@@ -1092,7 +1226,8 @@ err_cache: + int + gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, char *dirpattern) + { +- char buf[PATH_MAX+2+256], dirname[PATH_MAX]; ++ /* dirname + cctype + d_name + NULL */ ++ char buf[PATH_MAX+5+256+1], dirname[PATH_MAX]; + const char *cctype; + struct dirent *d; + int err, i, j; +@@ -1171,37 +1306,56 @@ gssd_get_krb5_machine_cred_list(char ***list) + goto out; + } + +- /* Need to serialize list if we ever become multi-threaded! */ +- ++ pthread_mutex_lock(&ple_lock); + for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) { +- if (ple->ccname) { +- /* Make sure cred is up-to-date before returning it */ +- retval = gssd_refresh_krb5_machine_credential(NULL, ple, +- NULL, NULL); +- if (retval) +- continue; +- if (i + 1 > listsize) { +- listsize += listinc; +- l = (char **) +- realloc(l, listsize * sizeof(char *)); +- if (l == NULL) { +- retval = ENOMEM; +- goto out; +- } +- } +- if ((l[i++] = strdup(ple->ccname)) == NULL) { ++ if (!ple->ccname) ++ continue; ++ ++ /* Take advantage of the fact we only remove the ple ++ * from the list during shutdown. If it's modified ++ * concurrently at worst we'll just miss a new entry ++ * before the current ple ++ * ++ * gssd_refresh_krb5_machine_credential_internal() will ++ * release the ple refcount ++ */ ++ ple->refcount++; ++ pthread_mutex_unlock(&ple_lock); ++ /* Make sure cred is up-to-date before returning it */ ++ retval = gssd_refresh_krb5_machine_credential_internal(NULL, ple, ++ NULL, NULL); ++ pthread_mutex_lock(&ple_lock); ++ if (gssd_k5_kt_princ_list == NULL) { ++ /* Looks like we did shutdown... abort */ ++ l[i] = NULL; ++ gssd_free_krb5_machine_cred_list(l); ++ retval = ENOMEM; ++ goto out_lock; ++ } ++ if (retval) ++ continue; ++ if (i + 1 > listsize) { ++ listsize += listinc; ++ l = (char **) ++ realloc(l, listsize * sizeof(char *)); ++ if (l == NULL) { + retval = ENOMEM; +- goto out; ++ goto out_lock; + } + } ++ if ((l[i++] = strdup(ple->ccname)) == NULL) { ++ retval = ENOMEM; ++ goto out_lock; ++ } + } + if (i > 0) { + l[i] = NULL; + *list = l; + retval = 0; +- goto out; + } else + free((void *)l); ++out_lock: ++ pthread_mutex_unlock(&ple_lock); + out: + return retval; + } +@@ -1226,7 +1380,7 @@ gssd_free_krb5_machine_cred_list(char **list) + * Called upon exit. Destroys machine credentials. + */ + void +-gssd_destroy_krb5_machine_creds(void) ++gssd_destroy_krb5_principals(int destroy_machine_creds) + { + krb5_context context; + krb5_error_code code = 0; +@@ -1238,33 +1392,38 @@ gssd_destroy_krb5_machine_creds(void) + if (code) { + k5err = gssd_k5_err_msg(NULL, code); + printerr(0, "ERROR: %s while initializing krb5\n", k5err); +- goto out; ++ free(k5err); ++ return; + } + +- for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) { +- if (!ple->ccname) +- continue; +- if ((code = krb5_cc_resolve(context, ple->ccname, &ccache))) { +- k5err = gssd_k5_err_msg(context, code); +- printerr(0, "WARNING: %s while resolving credential " +- "cache '%s' for destruction\n", k5err, +- ple->ccname); +- krb5_free_string(context, k5err); +- k5err = NULL; +- continue; +- } ++ pthread_mutex_lock(&ple_lock); ++ while (gssd_k5_kt_princ_list) { ++ ple = gssd_k5_kt_princ_list; ++ gssd_k5_kt_princ_list = ple->next; + +- if ((code = krb5_cc_destroy(context, ccache))) { +- k5err = gssd_k5_err_msg(context, code); +- printerr(0, "WARNING: %s while destroying credential " +- "cache '%s'\n", k5err, ple->ccname); +- krb5_free_string(context, k5err); +- k5err = NULL; ++ if (destroy_machine_creds && ple->ccname) { ++ if ((code = krb5_cc_resolve(context, ple->ccname, &ccache))) { ++ k5err = gssd_k5_err_msg(context, code); ++ printerr(0, "WARNING: %s while resolving credential " ++ "cache '%s' for destruction\n", k5err, ++ ple->ccname); ++ free(k5err); ++ k5err = NULL; ++ } ++ ++ if (!code && (code = krb5_cc_destroy(context, ccache))) { ++ k5err = gssd_k5_err_msg(context, code); ++ printerr(0, "WARNING: %s while destroying credential " ++ "cache '%s'\n", k5err, ple->ccname); ++ free(k5err); ++ k5err = NULL; ++ } + } ++ ++ release_ple(context, ple); + } ++ pthread_mutex_unlock(&ple_lock); + krb5_free_context(context); +- out: +- krb5_free_string(context, k5err); + } + + /* +@@ -1272,83 +1431,10 @@ gssd_destroy_krb5_machine_creds(void) + */ + int + gssd_refresh_krb5_machine_credential(char *hostname, +- struct gssd_k5_kt_princ *ple, + char *service, char *srchost) + { +- krb5_error_code code = 0; +- krb5_context context; +- krb5_keytab kt = NULL;; +- int retval = 0; +- char *k5err = NULL; +- const char *svcnames[] = { "$", "root", "nfs", "host", NULL }; +- +- printerr(2, "%s: hostname=%s ple=%p service=%s srchost=%s\n", +- __func__, hostname, ple, service, srchost); +- +- /* +- * If a specific service name was specified, use it. +- * Otherwise, use the default list. +- */ +- if (service != NULL && strcmp(service, "*") != 0) { +- svcnames[0] = service; +- svcnames[1] = NULL; +- } +- if (hostname == NULL && ple == NULL) +- return EINVAL; +- +- code = krb5_init_context(&context); +- if (code) { +- k5err = gssd_k5_err_msg(NULL, code); +- printerr(0, "ERROR: %s: %s while initializing krb5 context\n", +- __func__, k5err); +- retval = code; +- goto out; +- } +- +- if ((code = krb5_kt_resolve(context, keytabfile, &kt))) { +- k5err = gssd_k5_err_msg(context, code); +- printerr(0, "ERROR: %s: %s while resolving keytab '%s'\n", +- __func__, k5err, keytabfile); +- goto out_free_context; +- } +- +- if (ple == NULL) { +- krb5_keytab_entry kte; +- +- code = find_keytab_entry(context, kt, srchost, hostname, +- &kte, svcnames); +- if (code) { +- printerr(0, "ERROR: %s: no usable keytab entry found " +- "in keytab %s for connection with host %s\n", +- __FUNCTION__, keytabfile, hostname); +- retval = code; +- goto out_free_kt; +- } +- +- ple = get_ple_by_princ(context, kte.principal); +- k5_free_kt_entry(context, &kte); +- if (ple == NULL) { +- char *pname; +- if ((krb5_unparse_name(context, kte.principal, &pname))) { +- pname = NULL; +- } +- printerr(0, "ERROR: %s: Could not locate or create " +- "ple struct for principal %s for connection " +- "with host %s\n", +- __FUNCTION__, pname ? pname : "", +- hostname); +- if (pname) k5_free_unparsed_name(context, pname); +- goto out_free_kt; +- } +- } +- retval = gssd_get_single_krb5_cred(context, kt, ple, 0); +-out_free_kt: +- krb5_kt_close(context, kt); +-out_free_context: +- krb5_free_context(context); +-out: +- krb5_free_string(context, k5err); +- return retval; ++ return gssd_refresh_krb5_machine_credential_internal(hostname, NULL, ++ service, srchost); + } + + /* +diff --git a/utils/gssd/krb5_util.h b/utils/gssd/krb5_util.h +index b000b44..2415205 100644 +--- a/utils/gssd/krb5_util.h ++++ b/utils/gssd/krb5_util.h +@@ -9,27 +9,13 @@ + #include "gss_oids.h" + #endif + +-/* +- * List of principals from our keytab that we +- * will try to use to obtain credentials +- * (known as a principal list entry (ple)) +- */ +-struct gssd_k5_kt_princ { +- struct gssd_k5_kt_princ *next; +- krb5_principal princ; +- char *ccname; +- char *realm; +- krb5_timestamp endtime; +-}; +- + + int gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, + char *dirname); + int gssd_get_krb5_machine_cred_list(char ***list); + void gssd_free_krb5_machine_cred_list(char **list); +-void gssd_destroy_krb5_machine_creds(void); ++void gssd_destroy_krb5_principals(int destroy_machine_creds); + int gssd_refresh_krb5_machine_credential(char *hostname, +- struct gssd_k5_kt_princ *ple, + char *service, char *srchost); + char *gssd_k5_err_msg(krb5_context context, krb5_error_code code); + void gssd_k5_get_default_realm(char **def_realm); +diff --git a/utils/gssd/svcgssd.c b/utils/gssd/svcgssd.c +index ec49b61..3ab2100 100644 +--- a/utils/gssd/svcgssd.c ++++ b/utils/gssd/svcgssd.c +@@ -57,20 +57,36 @@ + #include + #include + #include ++#include ++ + #include "nfslib.h" + #include "svcgssd.h" + #include "gss_util.h" + #include "err_util.h" + #include "conffile.h" ++#include "misc.h" ++#include "svcgssd_krb5.h" ++ ++struct state_paths etab; /* from cacheio.c */ ++static bool signal_received = false; ++static struct event_base *evbase = NULL; ++static int nullrpc_fd = -1; ++static struct event *nullrpc_event = NULL; ++static struct event *wait_event = NULL; + +-struct state_paths etab; ++#define NULLRPC_FILE "/proc/net/rpc/auth.rpcsec.init/channel" + + static void + sig_die(int signal) + { +- /* destroy krb5 machine creds */ ++ if (signal_received) { ++ /* destroy krb5 machine creds */ ++ printerr(1, "forced exiting on signal %d\n", signal); ++ exit(0); ++ } ++ signal_received = true; + printerr(1, "exiting on signal %d\n", signal); +- exit(0); ++ event_base_loopexit(evbase, NULL); + } + + static void +@@ -89,6 +105,84 @@ usage(char *progname) + exit(1); + } + ++static void ++svcgssd_nullrpc_cb(int fd, short UNUSED(which), void *UNUSED(data)) ++{ ++ char lbuf[RPC_CHAN_BUF_SIZE]; ++ int lbuflen = 0; ++ ++ printerr(1, "reading null request\n"); ++ ++ lbuflen = read(fd, lbuf, sizeof(lbuf)); ++ if (lbuflen <= 0 || lbuf[lbuflen-1] != '\n') { ++ printerr(0, "WARNING: handle_nullreq: failed reading request\n"); ++ return; ++ } ++ lbuf[lbuflen-1] = 0; ++ ++ handle_nullreq(lbuf); ++} ++ ++static void ++svcgssd_nullrpc_close(void) ++{ ++ if (nullrpc_event) { ++ printerr(2, "closing nullrpc channel %s\n", NULLRPC_FILE); ++ event_free(nullrpc_event); ++ nullrpc_event = NULL; ++ } ++ if (nullrpc_fd != -1) { ++ close(nullrpc_fd); ++ nullrpc_fd = -1; ++ } ++} ++ ++static void ++svcgssd_nullrpc_open(void) ++{ ++ nullrpc_fd = open(NULLRPC_FILE, O_RDWR); ++ if (nullrpc_fd < 0) { ++ printerr(0, "failed to open %s: %s\n", ++ NULLRPC_FILE, strerror(errno)); ++ return; ++ } ++ nullrpc_event = event_new(evbase, nullrpc_fd, EV_READ | EV_PERSIST, ++ svcgssd_nullrpc_cb, NULL); ++ if (!nullrpc_event) { ++ printerr(0, "failed to create event for %s: %s\n", ++ NULLRPC_FILE, strerror(errno)); ++ close(nullrpc_fd); ++ nullrpc_fd = -1; ++ return; ++ } ++ event_add(nullrpc_event, NULL); ++ printerr(2, "opened nullrpc channel %s\n", NULLRPC_FILE); ++} ++ ++static void ++svcgssd_wait_cb(int UNUSED(fd), short UNUSED(which), void *UNUSED(data)) ++{ ++ static int times = 0; ++ int rc; ++ ++ rc = access(NULLRPC_FILE, R_OK | W_OK); ++ if (rc != 0) { ++ struct timeval t = {times < 10 ? 1 : 10, 0}; ++ times++; ++ if (times % 30 == 0) ++ printerr(2, "still waiting for nullrpc channel: %s\n", ++ NULLRPC_FILE); ++ evtimer_add(wait_event, &t); ++ return; ++ } ++ ++ svcgssd_nullrpc_open(); ++ event_free(wait_event); ++ wait_event = NULL; ++} ++ ++ ++ + int + main(int argc, char *argv[]) + { +@@ -102,6 +196,7 @@ main(int argc, char *argv[]) + char *progname; + char *principal = NULL; + char *s; ++ int rc; + + conf_init_file(NFS_CONFFILE); + +@@ -117,6 +212,9 @@ main(int argc, char *argv[]) + rpc_verbosity = conf_get_num("svcgssd", "RPC-Verbosity", rpc_verbosity); + idmap_verbosity = conf_get_num("svcgssd", "IDMAP-Verbosity", idmap_verbosity); + ++ /* We don't need the config anymore */ ++ conf_cleanup(); ++ + while ((opt = getopt(argc, argv, "fivrnp:")) != -1) { + switch (opt) { + case 'f': +@@ -182,6 +280,12 @@ main(int argc, char *argv[]) + + daemon_init(fg); + ++ evbase = event_base_new(); ++ if (!evbase) { ++ printerr(0, "ERROR: failed to create event base: %s\n", strerror(errno)); ++ exit(EXIT_FAILURE); ++ } ++ + signal(SIGINT, sig_die); + signal(SIGTERM, sig_die); + signal(SIGHUP, sig_hup); +@@ -209,10 +313,37 @@ main(int argc, char *argv[]) + } + } + ++ svcgssd_nullrpc_open(); ++ if (!nullrpc_event) { ++ struct timeval t = {1, 0}; ++ ++ printerr(2, "waiting for nullrpc channel to appear\n"); ++ wait_event = evtimer_new(evbase, svcgssd_wait_cb, NULL); ++ if (!wait_event) { ++ printerr(0, "ERROR: failed to create wait event: %s\n", ++ strerror(errno)); ++ exit(EXIT_FAILURE); ++ } ++ evtimer_add(wait_event, &t); ++ } ++ + daemon_ready(); + + nfs4_init_name_mapping(NULL); /* XXX: should only do this once */ +- gssd_run(); +- printerr(0, "gssd_run returned!\n"); +- abort(); ++ ++ rc = event_base_dispatch(evbase); ++ if (rc < 0) ++ printerr(0, "event_base_dispatch() returned %i!\n", rc); ++ ++ svcgssd_nullrpc_close(); ++ if (wait_event) ++ event_free(wait_event); ++ ++ event_base_free(evbase); ++ ++ nfs4_term_name_mapping(); ++ svcgssd_free_enctypes(); ++ gssd_cleanup(); ++ ++ return EXIT_SUCCESS; + } +diff --git a/utils/gssd/svcgssd.h b/utils/gssd/svcgssd.h +index 02b5c7a..e229b98 100644 +--- a/utils/gssd/svcgssd.h ++++ b/utils/gssd/svcgssd.h +@@ -35,8 +35,7 @@ + #include + #include + +-void handle_nullreq(int f); +-void gssd_run(void); ++void handle_nullreq(char *cp); + + #define GSSD_SERVICE_NAME "nfs" + +diff --git a/utils/gssd/svcgssd_krb5.c b/utils/gssd/svcgssd_krb5.c +index 1d44d34..305d475 100644 +--- a/utils/gssd/svcgssd_krb5.c ++++ b/utils/gssd/svcgssd_krb5.c +@@ -74,13 +74,7 @@ parse_enctypes(char *enctypes) + return 0; + + /* Free any existing cached_enctypes */ +- free(cached_enctypes); +- +- if (parsed_enctypes != NULL) { +- free(parsed_enctypes); +- parsed_enctypes = NULL; +- parsed_num_enctypes = 0; +- } ++ svcgssd_free_enctypes(); + + /* count the number of commas */ + for (curr = enctypes; curr && *curr != '\0'; curr = ++comma) { +@@ -162,6 +156,19 @@ out_clean_parsed: + /*=== External routines ===*/ + /*==========================*/ + ++void ++svcgssd_free_enctypes(void) ++{ ++ free(cached_enctypes); ++ cached_enctypes = NULL; ++ ++ if (parsed_enctypes != NULL) { ++ free(parsed_enctypes); ++ parsed_enctypes = NULL; ++ parsed_num_enctypes = 0; ++ } ++} ++ + /* + * Get encryption types supported by the kernel, and then + * call gss_krb5_set_allowable_enctypes() to limit the +diff --git a/utils/gssd/svcgssd_krb5.h b/utils/gssd/svcgssd_krb5.h +index 07d5eb9..78a90e9 100644 +--- a/utils/gssd/svcgssd_krb5.h ++++ b/utils/gssd/svcgssd_krb5.h +@@ -32,5 +32,6 @@ + #define SVCGSSD_KRB5_H + + int svcgssd_limit_krb5_enctypes(void); ++void svcgssd_free_enctypes(void); + + #endif /* SVCGSSD_KRB5_H */ +diff --git a/utils/gssd/svcgssd_main_loop.c b/utils/gssd/svcgssd_main_loop.c +deleted file mode 100644 +index 920520d..0000000 +--- a/utils/gssd/svcgssd_main_loop.c ++++ /dev/null +@@ -1,94 +0,0 @@ +-/* +- Copyright (c) 2004 The Regents of the University of Michigan. +- All rights reserved. +- +- 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 University 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 ``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 REGENTS 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. +-*/ +- +-#ifdef HAVE_CONFIG_H +-#include +-#endif /* HAVE_CONFIG_H */ +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "svcgssd.h" +-#include "err_util.h" +- +-void +-gssd_run() +-{ +- int ret; +- int f; +- struct pollfd pollfd; +- +-#define NULLRPC_FILE "/proc/net/rpc/auth.rpcsec.init/channel" +- +- f = open(NULLRPC_FILE, O_RDWR); +- if (f < 0) { +- printerr(0, "failed to open %s: %s\n", +- NULLRPC_FILE, strerror(errno)); +- exit(1); +- } +- pollfd.fd = f; +- pollfd.events = POLLIN; +- while (1) { +- int save_err; +- +- pollfd.revents = 0; +- printerr(1, "entering poll\n"); +- ret = poll(&pollfd, 1, -1); +- save_err = errno; +- printerr(1, "leaving poll\n"); +- if (ret < 0) { +- if (save_err != EINTR) +- printerr(0, "error return from poll: %s\n", +- strerror(save_err)); +- } else if (ret == 0) { +- /* timeout; shouldn't happen. */ +- } else { +- if (ret != 1) { +- printerr(0, "bug: unexpected poll return %d\n", +- ret); +- exit(1); +- } +- if (pollfd.revents & POLLIN) +- handle_nullreq(f); +- } +- } +-} +diff --git a/utils/gssd/svcgssd_proc.c b/utils/gssd/svcgssd_proc.c +index 72ec254..b403143 100644 +--- a/utils/gssd/svcgssd_proc.c ++++ b/utils/gssd/svcgssd_proc.c +@@ -318,7 +318,7 @@ print_hexl(const char *description, unsigned char *cp, int length) + #endif + + void +-handle_nullreq(int f) { ++handle_nullreq(char *cp) { + /* XXX initialize to a random integer to reduce chances of unnecessary + * invalidation of existing ctx's on restarting svcgssd. */ + static u_int32_t handle_seq = 0; +@@ -340,24 +340,11 @@ handle_nullreq(int f) { + u_int32_t maj_stat = GSS_S_FAILURE, min_stat = 0; + u_int32_t ignore_min_stat; + struct svc_cred cred; +- char lbuf[RPC_CHAN_BUF_SIZE]; +- int lbuflen = 0; +- char *cp; + int32_t ctx_endtime; + char *hostbased_name = NULL; + + printerr(1, "handling null request\n"); + +- lbuflen = read(f, lbuf, sizeof(lbuf)); +- if (lbuflen <= 0 || lbuf[lbuflen-1] != '\n') { +- printerr(0, "WARNING: handle_nullreq: " +- "failed reading request\n"); +- return; +- } +- lbuf[lbuflen-1] = 0; +- +- cp = lbuf; +- + in_handle.length = (size_t) qword_get(&cp, in_handle.value, + sizeof(in_handle_buf)); + #ifdef DEBUG +diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c +index 893159f..cb1478a 100644 +--- a/utils/idmapd/idmapd.c ++++ b/utils/idmapd/idmapd.c +@@ -49,7 +49,7 @@ + + #include + #include +-#include ++#include + #include + #include + #include +@@ -115,7 +115,7 @@ struct idmap_client { + int ic_fd; + int ic_dirfd; + int ic_scanned; +- struct event ic_event; ++ struct event *ic_event; + TAILQ_ENTRY(idmap_client) ic_next; + }; + static struct idmap_client nfsd_ic[2] = { +@@ -155,6 +155,7 @@ static void idtonameres(struct idmap_msg *); + static void nametoidres(struct idmap_msg *); + + static int nfsdopen(void); ++static void nfsdclose(void); + static int nfsdopenone(struct idmap_client *); + static void nfsdreopen_one(struct idmap_client *); + static void nfsdreopen(void); +@@ -166,6 +167,22 @@ static char pipefsdir[PATH_MAX]; + static char *nobodyuser, *nobodygroup; + static uid_t nobodyuid; + static gid_t nobodygid; ++static struct event_base *evbase = NULL; ++static bool signal_received = false; ++static int inotify_fd = -1; ++ ++static void ++sig_die(int signal) ++{ ++ if (signal_received) { ++ xlog_warn("forced exiting on signal %d\n", signal); ++ exit(0); ++ } ++ ++ signal_received = true; ++ xlog_warn("exiting on signal %d\n", signal); ++ event_base_loopexit(evbase, NULL); ++} + + static int + flush_nfsd_cache(char *path, time_t now) +@@ -209,14 +226,14 @@ main(int argc, char **argv) + { + int wd = -1, opt, fg = 0, nfsdret = -1; + struct idmap_clientq icq; +- struct event rootdirev, clntdirev, svrdirev, inotifyev; +- struct event initialize; ++ struct event *rootdirev = NULL, *clntdirev = NULL, ++ *svrdirev = NULL, *inotifyev = NULL; ++ struct event *initialize = NULL; + struct passwd *pw; + struct group *gr; + struct stat sb; + char *xpipefsdir = NULL; + int serverstart = 1, clientstart = 1; +- int inotify_fd; + int ret; + char *progname; + char *conf_path = NULL; +@@ -289,6 +306,9 @@ main(int argc, char **argv) + serverstart = 0; + } + ++ /* Not needed anymore */ ++ conf_cleanup(); ++ + while ((opt = getopt(argc, argv, GETOPTSTR)) != -1) + switch (opt) { + case 'v': +@@ -341,7 +361,9 @@ main(int argc, char **argv) + if (nfs4_init_name_mapping(conf_path)) + errx(1, "Unable to create name to user id mappings."); + +- event_init(); ++ evbase = event_base_new(); ++ if (evbase == NULL) ++ errx(1, "Failed to create event base."); + + if (verbose > 0) + xlog_warn("Expiration time is %d seconds.", +@@ -388,30 +410,44 @@ main(int argc, char **argv) + if (inotify_fd == -1) { + xlog_err("Unable to initialise inotify_init1: %s\n", strerror(errno)); + } else { +- wd = inotify_add_watch(inotify_fd, pipefsdir, IN_CREATE | IN_DELETE | IN_MODIFY); ++ wd = inotify_add_watch(inotify_fd, pipefsdir, IN_CREATE | IN_DELETE); + if (wd < 0) + xlog_err("Unable to inotify_add_watch(%s): %s\n", pipefsdir, strerror(errno)); + } + + TAILQ_INIT(&icq); + ++ signal(SIGINT, sig_die); ++ signal(SIGTERM, sig_die); ++ + /* These events are persistent */ +- signal_set(&rootdirev, SIGUSR1, dirscancb, &icq); +- signal_add(&rootdirev, NULL); +- signal_set(&clntdirev, SIGUSR2, clntscancb, &icq); +- signal_add(&clntdirev, NULL); +- signal_set(&svrdirev, SIGHUP, svrreopen, NULL); +- signal_add(&svrdirev, NULL); ++ rootdirev = evsignal_new(evbase, SIGUSR1, dirscancb, &icq); ++ if (rootdirev == NULL) ++ errx(1, "Failed to create SIGUSR1 event."); ++ evsignal_add(rootdirev, NULL); ++ clntdirev = evsignal_new(evbase, SIGUSR2, clntscancb, &icq); ++ if (clntdirev == NULL) ++ errx(1, "Failed to create SIGUSR2 event."); ++ evsignal_add(clntdirev, NULL); ++ svrdirev = evsignal_new(evbase, SIGHUP, svrreopen, NULL); ++ if (svrdirev == NULL) ++ errx(1, "Failed to create SIGHUP event."); ++ evsignal_add(svrdirev, NULL); + if ( wd >= 0) { +- event_set(&inotifyev, inotify_fd, EV_READ, dirscancb, &icq); +- event_add(&inotifyev, NULL); ++ inotifyev = event_new(evbase, inotify_fd, ++ EV_READ | EV_PERSIST, dirscancb, &icq); ++ if (inotifyev == NULL) ++ errx(1, "Failed to create inotify read event."); ++ event_add(inotifyev, NULL); + } + + /* Fetch current state */ + /* (Delay till start of event_dispatch to avoid possibly losing + * a SIGUSR1 between here and the call to event_dispatch().) */ +- evtimer_set(&initialize, dirscancb, &icq); +- evtimer_add(&initialize, &now); ++ initialize = evtimer_new(evbase, dirscancb, &icq); ++ if (initialize == NULL) ++ errx(1, "Failed to create initialize event."); ++ evtimer_add(initialize, &now); + } + + if (nfsdret != 0 && wd < 0) +@@ -419,15 +455,59 @@ main(int argc, char **argv) + + daemon_ready(); + +- if (event_dispatch() < 0) ++ if (event_base_dispatch(evbase) < 0) + xlog_err("main: event_dispatch returns errno %d (%s)", + errno, strerror(errno)); +- /* NOTREACHED */ ++ ++ nfs4_term_name_mapping(); ++ nfsdclose(); ++ ++ if (inotifyev) ++ event_free(inotifyev); ++ if (inotify_fd != -1) ++ close(inotify_fd); ++ ++ if (initialize) ++ event_free(initialize); ++ if (rootdirev) ++ event_free(rootdirev); ++ if (clntdirev) ++ event_free(clntdirev); ++ if (svrdirev) ++ event_free(svrdirev); ++ event_base_free(evbase); ++ + return 1; + } + + static void +-dirscancb(int UNUSED(fd), short UNUSED(which), void *data) ++flush_inotify(int fd) ++{ ++ while (true) { ++ char buf[4096] __attribute__ ((aligned(__alignof__(struct inotify_event)))); ++ const struct inotify_event *ev; ++ ssize_t len; ++ char *ptr; ++ ++ len = read(fd, buf, sizeof(buf)); ++ if (len == -1 && errno == EINTR) ++ continue; ++ ++ if (len <= 0) ++ break; ++ ++ for (ptr = buf; ptr < buf + len; ++ ptr += sizeof(struct inotify_event) + ev->len) { ++ ++ ev = (const struct inotify_event *)ptr; ++ xlog_warn("pipefs inotify: wd=%i, mask=0x%08x, len=%i, name=%s", ++ ev->wd, ev->mask, ev->len, ev->len ? ev->name : ""); ++ } ++ } ++} ++ ++static void ++dirscancb(int fd, short UNUSED(which), void *data) + { + int nent, i; + struct dirent **ents; +@@ -435,6 +515,13 @@ dirscancb(int UNUSED(fd), short UNUSED(which), void *data) + char path[PATH_MAX+256]; /* + sizeof(d_name) */ + struct idmap_clientq *icq = data; + ++ if (fd != -1) ++ flush_inotify(fd); ++ ++ TAILQ_FOREACH(ic, icq, ic_next) { ++ ic->ic_scanned = 0; ++ } ++ + nent = scandir(pipefsdir, &ents, NULL, alphasort); + if (nent == -1) { + xlog_warn("dirscancb: scandir(%s): %s", pipefsdir, strerror(errno)); +@@ -468,15 +555,15 @@ dirscancb(int UNUSED(fd), short UNUSED(which), void *data) + strlcat(path, "/idmap", sizeof(path)); + strlcpy(ic->ic_path, path, sizeof(ic->ic_path)); + +- if (verbose > 0) +- xlog_warn("New client: %s", ic->ic_clid); +- + if (nfsopen(ic) == -1) { + close(ic->ic_dirfd); + free(ic); + goto out; + } + ++ if (verbose > 0) ++ xlog_warn("New client: %s", ic->ic_clid); ++ + ic->ic_id = "Client"; + + TAILQ_INSERT_TAIL(icq, ic, ic_next); +@@ -490,17 +577,19 @@ dirscancb(int UNUSED(fd), short UNUSED(which), void *data) + while(ic != NULL) { + nextic=TAILQ_NEXT(ic, ic_next); + if (!ic->ic_scanned) { +- event_del(&ic->ic_event); +- close(ic->ic_fd); +- close(ic->ic_dirfd); ++ if (ic->ic_event) ++ event_free(ic->ic_event); ++ if (ic->ic_fd != -1) ++ close(ic->ic_fd); ++ if (ic->ic_dirfd != -1) ++ close(ic->ic_dirfd); + TAILQ_REMOVE(icq, ic, ic_next); + if (verbose > 0) { + xlog_warn("Stale client: %s", ic->ic_clid); + xlog_warn("\t-> closed %s", ic->ic_path); + } + free(ic); +- } else +- ic->ic_scanned = 0; ++ } + ic = nextic; + } + +@@ -546,7 +635,7 @@ nfsdcb(int UNUSED(fd), short which, void *data) + unsigned long tmp; + + if (which != EV_READ) +- goto out; ++ return; + + len = read(ic->ic_fd, buf, sizeof(buf)); + if (len == 0) +@@ -569,11 +658,11 @@ nfsdcb(int UNUSED(fd), short which, void *data) + /* Authentication name -- ignored for now*/ + if (getfield(&bp, authbuf, sizeof(authbuf)) == -1) { + xlog_warn("nfsdcb: bad authentication name in upcall\n"); +- goto out; ++ return; + } + if (getfield(&bp, typebuf, sizeof(typebuf)) == -1) { + xlog_warn("nfsdcb: bad type in upcall\n"); +- goto out; ++ return; + } + if (verbose > 0) + xlog_warn("nfsdcb: authbuf=%s authtype=%s", +@@ -587,26 +676,26 @@ nfsdcb(int UNUSED(fd), short which, void *data) + im.im_conv = IDMAP_CONV_NAMETOID; + if (getfield(&bp, im.im_name, sizeof(im.im_name)) == -1) { + xlog_warn("nfsdcb: bad name in upcall\n"); +- goto out; ++ return; + } + break; + case IC_IDNAME: + im.im_conv = IDMAP_CONV_IDTONAME; + if (getfield(&bp, buf1, sizeof(buf1)) == -1) { + xlog_warn("nfsdcb: bad id in upcall\n"); +- goto out; ++ return; + } + tmp = strtoul(buf1, (char **)NULL, 10); + im.im_id = (u_int32_t)tmp; + if ((tmp == ULONG_MAX && errno == ERANGE) + || (unsigned long)im.im_id != tmp) { + xlog_warn("nfsdcb: id '%s' too big!\n", buf1); +- goto out; ++ return; + } + break; + default: + xlog_warn("nfsdcb: Unknown which type %d", ic->ic_which); +- goto out; ++ return; + } + + imconv(ic, &im); +@@ -667,7 +756,7 @@ nfsdcb(int UNUSED(fd), short which, void *data) + break; + default: + xlog_warn("nfsdcb: Unknown which type %d", ic->ic_which); +- goto out; ++ return; + } + + bsiz = sizeof(buf) - bsiz; +@@ -675,9 +764,6 @@ nfsdcb(int UNUSED(fd), short which, void *data) + if (atomicio((void*)write, ic->ic_fd, buf, bsiz) != bsiz) + xlog_warn("nfsdcb: write(%s) failed: errno %d (%s)", + ic->ic_path, errno, strerror(errno)); +- +-out: +- event_add(&ic->ic_event, NULL); + } + + static void +@@ -721,14 +807,12 @@ nfscb(int UNUSED(fd), short which, void *data) + struct idmap_msg im; + + if (which != EV_READ) +- goto out; ++ return; + + if (atomicio(read, ic->ic_fd, &im, sizeof(im)) != sizeof(im)) { + if (verbose > 0) + xlog_warn("nfscb: read(%s): %s", ic->ic_path, strerror(errno)); +- if (errno == EPIPE) +- return; +- goto out; ++ return; + } + + imconv(ic, &im); +@@ -742,8 +826,19 @@ nfscb(int UNUSED(fd), short which, void *data) + + if (atomicio((void*)write, ic->ic_fd, &im, sizeof(im)) != sizeof(im)) + xlog_warn("nfscb: write(%s): %s", ic->ic_path, strerror(errno)); +-out: +- event_add(&ic->ic_event, NULL); ++} ++ ++static void ++nfsdclose_one(struct idmap_client *ic) ++{ ++ if (ic->ic_event) { ++ event_free(ic->ic_event); ++ ic->ic_event = NULL; ++ } ++ if (ic->ic_fd != -1) { ++ close(ic->ic_fd); ++ ic->ic_fd = -1; ++ } + } + + static void +@@ -755,14 +850,18 @@ nfsdreopen_one(struct idmap_client *ic) + xlog_warn("ReOpening %s", ic->ic_path); + + if ((fd = open(ic->ic_path, O_RDWR, 0)) != -1) { +- if ((event_initialized(&ic->ic_event))) +- event_del(&ic->ic_event); +- if (ic->ic_fd != -1) +- close(ic->ic_fd); ++ nfsdclose_one(ic); + +- ic->ic_event.ev_fd = ic->ic_fd = fd; +- event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfsdcb, ic); +- event_add(&ic->ic_event, NULL); ++ ic->ic_fd = fd; ++ ic->ic_event = event_new(evbase, ic->ic_fd, EV_READ | EV_PERSIST, nfsdcb, ic); ++ if (ic->ic_event == NULL) { ++ xlog_warn("nfsdreopen: Failed to create event for '%s'", ++ ic->ic_path); ++ close(ic->ic_fd); ++ ic->ic_fd = -1; ++ return; ++ } ++ event_add(ic->ic_event, NULL); + } else { + xlog_warn("nfsdreopen: Opening '%s' failed: errno %d (%s)", + ic->ic_path, errno, strerror(errno)); +@@ -784,6 +883,13 @@ nfsdopen(void) + nfsdopenone(&nfsd_ic[IC_IDNAME]) == 0) ? 0 : -1); + } + ++static void ++nfsdclose(void) ++{ ++ nfsdclose_one(&nfsd_ic[IC_NAMEID]); ++ nfsdclose_one(&nfsd_ic[IC_IDNAME]); ++} ++ + static int + nfsdopenone(struct idmap_client *ic) + { +@@ -795,8 +901,16 @@ nfsdopenone(struct idmap_client *ic) + return (-1); + } + +- event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfsdcb, ic); +- event_add(&ic->ic_event, NULL); ++ ic->ic_event = event_new(evbase, ic->ic_fd, EV_READ | EV_PERSIST, nfsdcb, ic); ++ if (ic->ic_event == NULL) { ++ if (verbose > 0) ++ xlog_warn("nfsdopenone: Create event for %s failed", ++ ic->ic_path); ++ close(ic->ic_fd); ++ ic->ic_fd = -1; ++ return (-1); ++ } ++ event_add(ic->ic_event, NULL); + + if (verbose > 0) + xlog_warn("Opened %s", ic->ic_path); +@@ -808,25 +922,34 @@ static int + nfsopen(struct idmap_client *ic) + { + if ((ic->ic_fd = open(ic->ic_path, O_RDWR, 0)) == -1) { +- switch (errno) { +- case ENOENT: +- fcntl(ic->ic_dirfd, F_SETSIG, SIGUSR2); +- fcntl(ic->ic_dirfd, F_NOTIFY, +- DN_CREATE | DN_DELETE | DN_MULTISHOT); +- break; +- default: +- xlog_warn("nfsopen: open(%s): %s", ic->ic_path, strerror(errno)); +- return (-1); ++ if (errno == ENOENT) { ++ char *slash; ++ ++ slash = strrchr(ic->ic_path, '/'); ++ if (!slash) ++ return -1; ++ *slash = 0; ++ inotify_add_watch(inotify_fd, ic->ic_path, IN_CREATE | IN_ONLYDIR | IN_ONESHOT); ++ *slash = '/'; ++ xlog_warn("Path %s not available. waiting...", ic->ic_path); ++ return -1; + } +- } else { +- event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfscb, ic); +- event_add(&ic->ic_event, NULL); +- fcntl(ic->ic_dirfd, F_NOTIFY, 0); +- fcntl(ic->ic_dirfd, F_SETSIG, 0); +- if (verbose > 0) +- xlog_warn("Opened %s", ic->ic_path); ++ ++ xlog_warn("nfsopen: open(%s): %s", ic->ic_path, strerror(errno)); ++ return (-1); + } + ++ ic->ic_event = event_new(evbase, ic->ic_fd, EV_READ | EV_PERSIST, nfscb, ic); ++ if (ic->ic_event == NULL) { ++ xlog_warn("nfsopen: Create event for %s failed", ic->ic_path); ++ close(ic->ic_fd); ++ ic->ic_fd = -1; ++ return -1; ++ } ++ event_add(ic->ic_event, NULL); ++ if (verbose > 0) ++ xlog_warn("Opened %s", ic->ic_path); ++ + return (0); + } + +diff --git a/utils/mount/network.c b/utils/mount/network.c +index 6ac913d..d9c0b51 100644 +--- a/utils/mount/network.c ++++ b/utils/mount/network.c +@@ -1268,14 +1268,14 @@ nfs_nfs_program(struct mount_options *options, unsigned long *program) + int + nfs_nfs_version(char *type, struct mount_options *options, struct nfs_version *version) + { +- char *version_key, *version_val, *cptr; ++ char *version_key, *version_val = NULL, *cptr; + int i, found = 0; + + version->v_mode = V_DEFAULT; + + for (i = 0; nfs_version_opttbl[i]; i++) { + if (po_contains_prefix(options, nfs_version_opttbl[i], +- &version_key) == PO_FOUND) { ++ &version_key) == PO_FOUND) { + found++; + break; + } +diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c +index 901f995..91a976b 100644 +--- a/utils/mount/stropts.c ++++ b/utils/mount/stropts.c +@@ -1094,9 +1094,7 @@ static int nfsmount_fg(struct nfsmount_info *mi) + if (nfs_try_mount(mi)) + return EX_SUCCESS; + +-#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers" + if (errno == EBUSY && is_mountpoint(mi->node)) { +-#pragma GCC diagnostic warning "-Wdiscarded-qualifiers" + /* + * EBUSY can happen when mounting a filesystem that + * is already mounted or when the context= are +diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c +index 6cba288..ea74067 100644 +--- a/utils/mountd/cache.c ++++ b/utils/mountd/cache.c +@@ -57,7 +57,7 @@ enum nfsd_fsid { + }; + + #undef is_mountpoint +-static int is_mountpoint(char *path) ++static int is_mountpoint(const char *path) + { + return check_is_mountpoint(path, nfsd_path_lstat); + } +diff --git a/utils/nfsdcld/cld-internal.h b/utils/nfsdcld/cld-internal.h +index cc283da..3576515 100644 +--- a/utils/nfsdcld/cld-internal.h ++++ b/utils/nfsdcld/cld-internal.h +@@ -26,7 +26,7 @@ + + struct cld_client { + int cl_fd; +- struct event cl_event; ++ struct event *cl_event; + union { + struct cld_msg cl_msg; + #if UPCALL_VERSION >= 2 +diff --git a/utils/nfsdcld/legacy.c b/utils/nfsdcld/legacy.c +index 3c6bea6..b89374c 100644 +--- a/utils/nfsdcld/legacy.c ++++ b/utils/nfsdcld/legacy.c +@@ -48,35 +48,28 @@ legacy_load_clients_from_recdir(int *num_records) + int fd; + DIR *v4recovery; + struct dirent *entry; +- char recdirname[PATH_MAX]; ++ char recdirname[PATH_MAX+1]; + char buf[NFS4_OPAQUE_LIMIT]; +- struct stat st; + char *nl; ++ ssize_t n; + + fd = open(NFSD_RECDIR_FILE, O_RDONLY); + if (fd < 0) { + xlog(D_GENERAL, "Unable to open %s: %m", NFSD_RECDIR_FILE); + return; + } +- if (read(fd, recdirname, PATH_MAX) < 0) { ++ n = read(fd, recdirname, PATH_MAX); ++ close(fd); ++ if (n < 0) { + xlog(D_GENERAL, "Unable to read from %s: %m", NFSD_RECDIR_FILE); + return; + } +- close(fd); + /* the output from the proc file isn't null-terminated */ ++ recdirname[PATH_MAX] = '\0'; + nl = strchr(recdirname, '\n'); + if (!nl) + return; + *nl = '\0'; +- if (stat(recdirname, &st) < 0) { +- xlog(D_GENERAL, "Unable to stat %s: %d", recdirname, errno); +- return; +- } +- if (!S_ISDIR(st.st_mode)) { +- xlog(D_GENERAL, "%s is not a directory: mode=0%o", recdirname +- , st.st_mode); +- return; +- } + v4recovery = opendir(recdirname); + if (!v4recovery) + return; +@@ -123,35 +116,28 @@ legacy_clear_recdir(void) + int fd; + DIR *v4recovery; + struct dirent *entry; +- char recdirname[PATH_MAX]; ++ char recdirname[PATH_MAX+1]; + char dirname[PATH_MAX]; +- struct stat st; + char *nl; ++ ssize_t n; + + fd = open(NFSD_RECDIR_FILE, O_RDONLY); + if (fd < 0) { + xlog(D_GENERAL, "Unable to open %s: %m", NFSD_RECDIR_FILE); + return; + } +- if (read(fd, recdirname, PATH_MAX) < 0) { ++ n = read(fd, recdirname, PATH_MAX); ++ close(fd); ++ if (n < 0) { + xlog(D_GENERAL, "Unable to read from %s: %m", NFSD_RECDIR_FILE); + return; + } +- close(fd); + /* the output from the proc file isn't null-terminated */ ++ recdirname[PATH_MAX] = '\0'; + nl = strchr(recdirname, '\n'); + if (!nl) + return; + *nl = '\0'; +- if (stat(recdirname, &st) < 0) { +- xlog(D_GENERAL, "Unable to stat %s: %d", recdirname, errno); +- return; +- } +- if (!S_ISDIR(st.st_mode)) { +- xlog(D_GENERAL, "%s is not a directory: mode=0%o", recdirname +- , st.st_mode); +- return; +- } + v4recovery = opendir(recdirname); + if (!v4recovery) + return; +diff --git a/utils/nfsdcld/nfsdcld.c b/utils/nfsdcld/nfsdcld.c +index be65562..636c398 100644 +--- a/utils/nfsdcld/nfsdcld.c ++++ b/utils/nfsdcld/nfsdcld.c +@@ -24,9 +24,10 @@ + #endif /* HAVE_CONFIG_H */ + + #include +-#include ++#include + #include + #include ++#include + #include + #include + #include +@@ -66,8 +67,10 @@ + static char pipefs_dir[PATH_MAX] = DEFAULT_PIPEFS_DIR; + static char pipepath[PATH_MAX]; + static int inotify_fd = -1; +-static struct event pipedir_event; ++static struct event *pipedir_event; ++static struct event_base *evbase; + static bool old_kernel = false; ++static bool signal_received = false; + + uint64_t current_epoch; + uint64_t recovery_epoch; +@@ -88,6 +91,19 @@ static struct option longopts[] = + /* forward declarations */ + static void cldcb(int UNUSED(fd), short which, void *data); + ++static void ++sig_die(int signal) ++{ ++ if (signal_received) { ++ xlog(D_GENERAL, "forced exiting on signal %d\n", signal); ++ exit(0); ++ } ++ ++ signal_received = true; ++ xlog(D_GENERAL, "exiting on signal %d\n", signal); ++ event_base_loopexit(evbase, NULL); ++} ++ + static void + usage(char *progname) + { +@@ -141,6 +157,7 @@ static int + cld_pipe_open(struct cld_client *clnt) + { + int fd; ++ struct event *ev; + + xlog(D_GENERAL, "%s: opening upcall pipe %s", __func__, pipepath); + fd = open(pipepath, O_RDWR, 0); +@@ -149,13 +166,22 @@ cld_pipe_open(struct cld_client *clnt) + return -errno; + } + +- if (event_initialized(&clnt->cl_event)) +- event_del(&clnt->cl_event); ++ ev = event_new(evbase, fd, EV_READ, cldcb, clnt); ++ if (ev == NULL) { ++ xlog(D_GENERAL, "%s: failed to create event for %s", __func__, pipepath); ++ close(fd); ++ return -ENOMEM; ++ } ++ ++ if (clnt->cl_event && event_initialized(clnt->cl_event)) { ++ event_del(clnt->cl_event); ++ event_free(clnt->cl_event); ++ } + if (clnt->cl_fd >= 0) + close(clnt->cl_fd); + + clnt->cl_fd = fd; +- event_set(&clnt->cl_event, clnt->cl_fd, EV_READ, cldcb, clnt); ++ clnt->cl_event = ev; + /* event_add is done by the caller */ + return 0; + } +@@ -208,7 +234,7 @@ cld_inotify_cb(int UNUSED(fd), short which, void *data) + switch (ret) { + case 0: + /* readd the event for the cl_event pipe */ +- event_add(&clnt->cl_event, NULL); ++ event_add(clnt->cl_event, NULL); + break; + case -ENOENT: + /* pipe must have disappeared, wait for it to come back */ +@@ -221,7 +247,7 @@ cld_inotify_cb(int UNUSED(fd), short which, void *data) + } + + out: +- event_add(&pipedir_event, NULL); ++ event_add(pipedir_event, NULL); + free(dirc); + } + +@@ -252,11 +278,12 @@ cld_inotify_setup(void) + xlog_err("%s: inotify_add_watch failed: %m", __func__); + ret = -errno; + goto out_err; +- } ++ } else ++ ret = 0; + + out_free: + free(dirc); +- return 0; ++ return ret; + out_err: + close(inotify_fd); + goto out_free; +@@ -286,7 +313,7 @@ cld_pipe_init(struct cld_client *clnt) + switch (ret) { + case 0: + /* add the event and we're good to go */ +- event_add(&clnt->cl_event, NULL); ++ event_add(clnt->cl_event, NULL); + break; + case -ENOENT: + /* ignore this error -- cld_inotify_cb will handle it */ +@@ -299,8 +326,12 @@ cld_pipe_init(struct cld_client *clnt) + } + + /* set event for inotify read */ +- event_set(&pipedir_event, inotify_fd, EV_READ, cld_inotify_cb, clnt); +- event_add(&pipedir_event, NULL); ++ pipedir_event = event_new(evbase, inotify_fd, EV_READ, cld_inotify_cb, clnt); ++ if (pipedir_event == NULL) { ++ close(inotify_fd); ++ return -ENOMEM; ++ } ++ event_add(pipedir_event, NULL); + out: + return ret; + } +@@ -331,6 +362,7 @@ cld_check_grace_period(void) + if (read(fd, &c, 1) < 0) { + xlog(L_WARNING, "Unable to read from %s: %m", + NFSD_END_GRACE_FILE); ++ close(fd); + return 1; + } + close(fd); +@@ -737,7 +769,7 @@ cldcb(int UNUSED(fd), short which, void *data) + cld_not_implemented(clnt); + } + out: +- event_add(&clnt->cl_event, NULL); ++ event_add(clnt->cl_event, NULL); + } + + int +@@ -762,7 +794,11 @@ main(int argc, char **argv) + return 1; + } + +- event_init(); ++ evbase = event_base_new(); ++ if (evbase == NULL) { ++ fprintf(stderr, "%s: unable to allocate event base.\n", argv[0]); ++ return 1; ++ } + xlog_syslog(0); + xlog_stderr(1); + +@@ -795,6 +831,7 @@ main(int argc, char **argv) + break; + default: + usage(progname); ++ free(progname); + return 0; + } + } +@@ -859,14 +896,27 @@ main(int argc, char **argv) + if (rc) + goto out; + ++ signal(SIGINT, sig_die); ++ signal(SIGTERM, sig_die); ++ + xlog(D_GENERAL, "%s: Starting event dispatch handler.", __func__); +- rc = event_dispatch(); ++ rc = event_base_dispatch(evbase); + if (rc < 0) + xlog(L_ERROR, "%s: event_dispatch failed: %m", __func__); + +- close(clnt.cl_fd); +- close(inotify_fd); + out: ++ if (clnt.cl_event) ++ event_free(clnt.cl_event); ++ if (clnt.cl_fd != -1) ++ close(clnt.cl_fd); ++ if (pipedir_event) ++ event_free(pipedir_event); ++ if (inotify_fd != -1) ++ close(inotify_fd); ++ ++ event_base_free(evbase); ++ sqlite_shutdown(); ++ + free(progname); + return rc; + } +diff --git a/utils/nfsdcld/sqlite.c b/utils/nfsdcld/sqlite.c +index 6666c86..03016fb 100644 +--- a/utils/nfsdcld/sqlite.c ++++ b/utils/nfsdcld/sqlite.c +@@ -48,7 +48,6 @@ + + #include + #include +-#include + #include + #include + #include +@@ -380,7 +379,7 @@ sqlite_maindb_init_v4(void) + &err); + if (ret != SQLITE_OK) { + xlog(L_ERROR, "Unable to begin transaction: %s", err); +- return ret; ++ goto out; + } + + /* +@@ -831,7 +830,6 @@ sqlite_prepare_dbh(const char *topdir) + switch (ret) { + case CLD_SQLITE_LATEST_SCHEMA_VERSION: + /* DB is already set up. Do nothing */ +- ret = 0; + break; + case 3: + /* Old DB -- update to new schema */ +@@ -868,6 +866,8 @@ sqlite_prepare_dbh(const char *topdir) + } + + ret = sqlite_startup_query_grace(); ++ if (ret) ++ goto out_close; + + ret = sqlite_query_first_time(&first_time); + if (ret) +@@ -1330,20 +1330,26 @@ sqlite_iterate_recovery(int (*cb)(struct cld_client *clnt), struct cld_client *c + } + + while ((ret = sqlite3_step(stmt)) == SQLITE_ROW) { ++ const void *id; ++ int id_len; ++ ++ id = sqlite3_column_blob(stmt, 0); ++ id_len = sqlite3_column_bytes(stmt, 0); ++ if (id_len > NFS4_OPAQUE_LIMIT) ++ id_len = NFS4_OPAQUE_LIMIT; ++ + memset(&cmsg->cm_u, 0, sizeof(cmsg->cm_u)); + #if UPCALL_VERSION >= 2 +- memcpy(&cmsg->cm_u.cm_clntinfo.cc_name.cn_id, +- sqlite3_column_blob(stmt, 0), NFS4_OPAQUE_LIMIT); +- cmsg->cm_u.cm_clntinfo.cc_name.cn_len = sqlite3_column_bytes(stmt, 0); ++ memcpy(&cmsg->cm_u.cm_clntinfo.cc_name.cn_id, id, id_len); ++ cmsg->cm_u.cm_clntinfo.cc_name.cn_len = id_len; + if (sqlite3_column_bytes(stmt, 1) > 0) { + memcpy(&cmsg->cm_u.cm_clntinfo.cc_princhash.cp_data, + sqlite3_column_blob(stmt, 1), SHA256_DIGEST_SIZE); + cmsg->cm_u.cm_clntinfo.cc_princhash.cp_len = sqlite3_column_bytes(stmt, 1); + } + #else +- memcpy(&cmsg->cm_u.cm_name.cn_id, sqlite3_column_blob(stmt, 0), +- NFS4_OPAQUE_LIMIT); +- cmsg->cm_u.cm_name.cn_len = sqlite3_column_bytes(stmt, 0); ++ memcpy(&cmsg->cm_u.cm_name.cn_id, id, id_len); ++ cmsg->cm_u.cm_name.cn_len = id_len; + #endif + cb(clnt); + } +@@ -1404,3 +1410,18 @@ sqlite_first_time_done(void) + sqlite3_free(err); + return ret; + } ++ ++/* ++ * Closes all sqlite3 resources and shuts down the library. ++ * ++ */ ++void ++sqlite_shutdown(void) ++{ ++ if (dbh != NULL) { ++ sqlite3_close(dbh); ++ dbh = NULL; ++ } ++ ++ sqlite3_shutdown(); ++} +diff --git a/utils/nfsdcld/sqlite.h b/utils/nfsdcld/sqlite.h +index 0a26ad6..044236c 100644 +--- a/utils/nfsdcld/sqlite.h ++++ b/utils/nfsdcld/sqlite.h +@@ -34,4 +34,5 @@ int sqlite_iterate_recovery(int (*cb)(struct cld_client *clnt), struct cld_clien + int sqlite_delete_cltrack_records(void); + int sqlite_first_time_done(void); + ++void sqlite_shutdown(void); + #endif /* _SQLITE_H */ +diff --git a/utils/nfsdcltrack/sqlite.c b/utils/nfsdcltrack/sqlite.c +index 2801201..f79aebb 100644 +--- a/utils/nfsdcltrack/sqlite.c ++++ b/utils/nfsdcltrack/sqlite.c +@@ -40,7 +40,7 @@ + + #include + #include +-#include ++#include + #include + #include + #include diff --git a/nfs-utils.spec b/nfs-utils.spec index aa82fe6..91a2995 100644 --- a/nfs-utils.spec +++ b/nfs-utils.spec @@ -2,7 +2,7 @@ Summary: NFS utilities and supporting clients and daemons for the kernel NFS ser Name: nfs-utils URL: http://linux-nfs.org/ Version: 2.5.1 -Release: 1%{?dist} +Release: 1.rc3%{?dist} Epoch: 1 # group all 32bit related archs @@ -16,6 +16,8 @@ Source4: nfsconvert.py Source5: nfsconvert.sh Source6: nfs-convert.service +Patch001: nfs-utils-2.5.2-rc3.patch + Patch100: nfs-utils-1.2.1-statdpath-man.patch Patch101: nfs-utils-1.2.1-exp-subtree-warn-off.patch Patch102: nfs-utils-1.2.5-idmap-errmsg.patch @@ -365,6 +367,9 @@ fi %{_pkgdir}/*/var-lib-nfs-rpc_pipefs.mount %changelog +* Tue Aug 04 2020 Steve Dickson 2.5.1-1.rc3 +- Updated to the latest RC release: nfs-utils-2-5-2-rc3 (bz 1856958) + * Tue Jul 28 2020 Fedora Release Engineering - 1:2.5.1-1 - Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild