From 9db2593faeb3a394545b27f44d3ed96fce51cd99 Mon Sep 17 00:00:00 2001 From: Sebastian Harl Date: Fri, 13 Dec 2013 14:31:22 +0100 Subject: [PATCH] sysdb: Check for and use libedit/libreadline for command input. --- configure.ac | 81 ++++++++++++++++++++++++++++++++++++++++++++++ src/Makefile.am | 10 ++++-- src/client/sysdb.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index dde7727..aedf6e8 100644 --- a/configure.ac +++ b/configure.ac @@ -184,6 +184,9 @@ if test "x$enable_strict_checks" = "xyes"; then fi AC_SUBST([STRICT_CFLAGS]) +m4_divert_once([HELP_ENABLE], [ +Build dependencies:]) + dnl Testing. PKG_CHECK_MODULES([CHECK], [check >= 0.9.4], [build_testing="yes"], [build_testing="no"]) @@ -219,6 +222,82 @@ if test "x$have_fopencookie" = "xyes"; then AC_DEFINE([HAVE_FOPENCOOKIE], 1) fi +dnl readline support +AC_ARG_WITH([readline], + [AS_HELP_STRING([--with-readline], + [readline support (libedit/libreadline) (default: auto, prefer libedit)])], + [readline_support="$withval"], + [readline_support="auto"]) + +if test "x$readline_support" = "xyes"; then + readline_support="auto" +fi + +have_libedit="no" +if test "x$readline_support" = "xauto" \ + || test "x$readline_support" = "xlibedit"; then + PKG_CHECK_MODULES([LIBEDIT], [libedit], + [have_libedit="yes"], [have_libedit="no"]) + if test "x$have_libedit" = "xyes"; then + AC_CHECK_HEADERS([editline/readline.h], [], + [AC_CHECK_HEADERS([readline.h], [], + [have_libedit="no (readline header not found"])]) + fi + if test "x$have_libedit" = "xyes"; then + AC_CHECK_HEADERS([editline/history.h], [], + [AC_CHECK_HEADERS([history.h], [], + [have_libedit="no (history header not found"])]) + fi +else + have_libedit="disabled on command-line" +fi + +have_libreadline="no" +if test "x$have_libedit" = "xno"; then + if test "x$readline_support" = "xauto" \ + || test "x$readline_support" = "xlibreadline"; then + AC_CHECK_LIB([readline], [readline], + [have_libreadline="yes"], + [have_libreadline="no (libreadline or symbol 'readline' not found)"]) + fi + if test "x$have_libreadline" = "xyes"; then + AC_CHECK_HEADERS([readline/readline.h], [], + [AC_CHECK_HEADERS([readline.h], [], + [have_libreadline="no (readline header not found"])]) + fi + if test "x$have_libreadline" = "xyes"; then + AC_CHECK_HEADERS([readline/history.h], [], + [AC_CHECK_HEADERS([history.h], [], + [have_libreadline="no (history header not found"])]) + fi +else + have_libreadline="unchecked (prefer libedit)" +fi + +if test "x$have_libedit" = "xyes"; then + READLINE_LIBS="$LIBEDIT_LIBS" + READLINE_CFLAGS="$LIBEDIT_CFLAGS" + readline_support="libedit" +else if test "x$have_libreadline" = "xyes"; then + READLINE_LIBS="-lreadline -lhistory" + READLINE_CFLAGS="" + readline_support="libreadline" +else + READLINE_LIBS="" + READLINE_CFLAGS="" + if test "x$readline_support" = "xno"; then + AC_MSG_WARN([*** readline support disabled; disabling SysDB client]) + else if test "x$readline_support" = "xauto"; then + AC_MSG_WARN([*** readline not found; disabling SysDB client]) + else + AC_MSG_ERROR([readline not found]) + fi; fi + readline_support="no" +fi; fi +AC_SUBST([READLINE_LIBS]) +AC_SUBST([READLINE_CFLAGS]) +AM_CONDITIONAL([BUILD_CLIENT], test "x$readline_support" != "no") + dnl Feature checks. build_documentation="yes" @@ -296,6 +375,8 @@ AC_MSG_RESULT([ stdio mocking: . . . . . $have_fopencookie]) AC_MSG_RESULT() AC_MSG_RESULT([ Libraries:]) AC_MSG_RESULT([ libdbi: . . . . . . . . . . $with_libdbi]) +AC_MSG_RESULT([ libedit: . . . . . . . . . $have_libedit]) +AC_MSG_RESULT([ libreadline: . . . . . . . $have_libreadline]) AC_MSG_RESULT() AC_MSG_RESULT([ Backends:]) AC_MSG_RESULT([ collectd: . . . . . . . . . $enable_collectd]) diff --git a/src/Makefile.am b/src/Makefile.am index e547fa4..3b1de0a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -79,11 +79,15 @@ libsysdb_la_SOURCES += \ libsysdb_la_LIBADD += -ldbi endif -bin_PROGRAMS = sysdb sysdbd +bin_PROGRAMS = sysdbd +if BUILD_CLIENT +bin_PROGRAMS += sysdb sysdb_SOURCES = client/sysdb.c include/client/sysdb.h -sysdb_CFLAGS = $(AM_CFLAGS) -DBUILD_DATE="\"$$( date --utc '+%F %T' ) (UTC)\"" -sysdb_LDADD = libsysdbclient.la +sysdb_CFLAGS = -DBUILD_DATE="\"$$( date --utc '+%F %T' ) (UTC)\"" \ + $(AM_CFLAGS) @READLINE_CFLAGS@ +sysdb_LDADD = libsysdbclient.la @READLINE_LIBS@ +endif sysdbd_SOURCES = daemon/sysdbd.c include/sysdb.h \ daemon/config.c include/daemon/config.h diff --git a/src/client/sysdb.c b/src/client/sysdb.c index c2b542d..7283a21 100644 --- a/src/client/sysdb.c +++ b/src/client/sysdb.c @@ -54,6 +54,23 @@ #include +#if HAVE_EDITLINE_READLINE_H +# include +# if HAVE_EDITLINE_HISTORY_H +# include +# endif +#elif HAVE_READLINE_READLINE_H +# include +# if HAVE_READLINE_HISTORY_H +# include +# endif +#elif HAVE_READLINE_H +# include +# if HAVE_HISTORY_H +# include +# endif +#endif /* READLINEs */ + #ifndef DEFAULT_SOCKET # define DEFAULT_SOCKET "unix:"LOCALSTATEDIR"/run/sysdbd.sock" #endif @@ -117,6 +134,31 @@ get_current_user(void) return result->pw_name; } /* get_current_user */ +static const char * +get_homedir(const char *username) +{ + struct passwd pw_entry; + struct passwd *result = NULL; + + /* needs to be static because we return a pointer into this buffer + * to the caller */ + static char buf[1024]; + + int status; + + memset(&pw_entry, 0, sizeof(pw_entry)); + status = getpwnam_r(username, &pw_entry, buf, sizeof(buf), &result); + + if (status || (! result)) { + char errbuf[1024]; + sdb_log(SDB_LOG_WARNING, "Failed to determine home directory " + "for user %s: %s", username, + sdb_strerror(errno, errbuf, sizeof(errbuf))); + return NULL; + } + return result->pw_dir; +} /* get_homedir */ + int main(int argc, char **argv) { @@ -125,6 +167,9 @@ main(int argc, char **argv) const char *host = NULL; const char *user = NULL; + const char *homedir; + char hist_file[1024] = ""; + while (42) { int opt = getopt(argc, argv, "H:U:hV"); @@ -175,6 +220,42 @@ main(int argc, char **argv) sdb_log(SDB_LOG_INFO, "SysDB client "SDB_CLIENT_VERSION_STRING SDB_CLIENT_VERSION_EXTRA"\n"); + using_history(); + + if ((homedir = get_homedir(user))) { + snprintf(hist_file, sizeof(hist_file) - 1, + "%s/.sysdb_history", homedir); + hist_file[sizeof(hist_file) - 1] = '\0'; + + errno = 0; + if (read_history(hist_file)) { + char errbuf[1024]; + sdb_log(SDB_LOG_WARNING, "Failed to load history (%s): %s", + hist_file, sdb_strerror(errno, errbuf, sizeof(errbuf))); + } + } + + while (42) { + char *line = readline("sysdb> "); + + if (! line) + break; + if (*line == '\0') + continue; + + if (*line != ' ') + add_history(line); + } + + if (hist_file[0] != '\0') { + errno = 0; + if (write_history(hist_file)) { + char errbuf[1024]; + sdb_log(SDB_LOG_WARNING, "Failed to store history (%s): %s", + hist_file, sdb_strerror(errno, errbuf, sizeof(errbuf))); + } + } + sdb_client_destroy(client); return 0; } /* main */ -- 2.30.2