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..00adc96 100755 --- a/tools/mountstats/mountstats.py +++ b/tools/mountstats/mountstats.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # -*- python-mode -*- """Parse /proc/self/mountstats and display it in human readable form """ @@ -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..4f5e8a6 100755 --- a/tools/nfs-iostat/nfs-iostat.py +++ b/tools/nfs-iostat/nfs-iostat.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # -*- python-mode -*- """Emulate iostat for NFS mount points using /proc/self/mountstats """ @@ -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'] @@ -380,6 +383,8 @@ class DeviceData: sends = float(self.__rpc_data['rpcsends']) if sample_time == 0: sample_time = float(self.__nfs_data['age']) + if sample_time == 0: + sample_time = 1; return (sends / sample_time) def display_iostats(self, sample_time, which): 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..f3d2314 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; } + /* Config memory is no longer needed */ + conf_cleanup(); + while ((opt = getopt(argc, argv, GETOPTSTR)) != -1) switch (opt) { case 'v': @@ -341,9 +361,11 @@ 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) + if (verbose > 1) xlog_warn("Expiration time is %d seconds.", cache_entry_expiration); if (serverstart) { @@ -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,60 @@ 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; + if (verbose > 2) + 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 +516,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 +556,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 > 2) + xlog_warn("New client: %s", ic->ic_clid); + ic->ic_id = "Client"; TAILQ_INSERT_TAIL(icq, ic, ic_next); @@ -490,17 +578,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) { + if (verbose > 2) { 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 +636,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,13 +659,13 @@ 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) + if (verbose > 2) xlog_warn("nfsdcb: authbuf=%s authtype=%s", authbuf, typebuf); @@ -587,26 +677,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 +757,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 +765,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 +808,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 +827,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 @@ -751,18 +847,22 @@ nfsdreopen_one(struct idmap_client *ic) { int fd; - if (verbose > 0) + if (verbose > 2) 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 +884,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,10 +902,18 @@ 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) + if (verbose > 2) xlog_warn("Opened %s", ic->ic_path); return (0); @@ -808,25 +923,35 @@ 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 = '/'; + if (verbose > 2) + 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 > 2) + 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