From 574a9c0456c182831b3d01a4d7ee0c737b91b107 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Tue, 9 Jun 2009 14:39:39 -0700 Subject: Remove Subversion Id strings --- configure.ac | 1 - 1 file changed, 1 deletion(-) (limited to 'configure.ac') diff --git a/configure.ac b/configure.ac index eb772a6..bada657 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,4 @@ dnl Process this file with Autoconf to produce a configure script. -dnl $Id$ dnl dnl Written by Russ Allbery dnl Copyright 2006, 2007, 2008 -- cgit v1.2.3 From b093893870d56cd460b16645496ec6c30c62a02f Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Mon, 8 Feb 2010 19:41:27 -0800 Subject: Initial port to Heimdal Just get the client code to compile with Heimdal. This will need more work later to use my regular Kerberos portability layer. --- client/keytab.c | 4 ++++ client/srvtab.c | 13 +++++++++++++ configure.ac | 4 ++++ 3 files changed, 21 insertions(+) (limited to 'configure.ac') diff --git a/client/keytab.c b/client/keytab.c index 2d31a27..bdd0134 100644 --- a/client/keytab.c +++ b/client/keytab.c @@ -47,7 +47,11 @@ merge_keytab(krb5_context ctx, const char *newfile, const char *file) status = krb5_kt_add_entry(ctx, old, &entry); if (status != 0) die_krb5(ctx, status, "cannot write to keytab %s", file); +#ifdef HAVE_KRB5_KT_FREE_ENTRY + krb5_kt_free_entry(ctx, &entry); +#else krb5_free_keytab_entry_contents(ctx, &entry); +#endif } if (status != KRB5_KT_END) die_krb5(ctx, status, "error reading temporary keytab %s", newfile); diff --git a/client/srvtab.c b/client/srvtab.c index 0cca70d..a01026e 100644 --- a/client/srvtab.c +++ b/client/srvtab.c @@ -58,8 +58,13 @@ write_srvtab(krb5_context ctx, const char *srvtab, const char *principal, ret = krb5_kt_get_entry(ctx, kt, princ, 0, ENCTYPE_DES_CBC_CRC, &entry); if (ret != 0) die_krb5(ctx, ret, "error reading DES key from keytab %s", keytab); +#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK + if (entry.keyblock.keyvalue.length != 8) + die("invalid DES key length in keytab"); +#else if (entry.key.length != 8) die("invalid DES key length in keytab"); +#endif krb5_kt_close(ctx, kt); /* Convert the principal to a Kerberos v4 principal. */ @@ -80,9 +85,17 @@ write_srvtab(krb5_context ctx, const char *srvtab, const char *principal, length += strlen(realm); data[length++] = '\0'; data[length++] = '\0'; +#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK + memcpy(data + length, entry.keyblock.keyvalue.data, 8); +#else memcpy(data + length, entry.key.contents, 8); +#endif length += 8; +#ifdef HAVE_KRB5_KT_FREE_ENTRY + krb5_kt_free_entry(ctx, &entry); +#else krb5_free_keytab_entry_contents(ctx, &entry); +#endif /* Write out the srvtab file. */ write_file(srvtab, data, length); diff --git a/configure.ac b/configure.ac index bada657..bc55ad0 100644 --- a/configure.ac +++ b/configure.ac @@ -45,6 +45,10 @@ AC_ARG_WITH([wallet-port], RRA_LIB_REMCTL RRA_LIB_KRB5 +RRA_LIB_KRB5_SWITCH +AC_CHECK_FUNCS([krb5_kt_free_entry]) +AC_CHECK_MEMBERS([krb5_keytab_entry.keyblock], , , [#include ]) +RRA_LIB_KRB5_RESTORE RRA_LIB_AFS AS_IF([test x"$rra_afs" = xtrue], [RRA_LIB_KRB4 -- cgit v1.2.3 From 03889c8b1b3145e5e79a7f05763a55c788ef8672 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Tue, 9 Feb 2010 13:18:18 -0800 Subject: Remove all the configure code for AFS kaserver support --- configure.ac | 7 --- m4/kaserver.m4 | 94 ----------------------------------- m4/krb4.m4 | 152 --------------------------------------------------------- 3 files changed, 253 deletions(-) delete mode 100644 m4/kaserver.m4 delete mode 100644 m4/krb4.m4 (limited to 'configure.ac') diff --git a/configure.ac b/configure.ac index bc55ad0..de998c0 100644 --- a/configure.ac +++ b/configure.ac @@ -49,13 +49,6 @@ RRA_LIB_KRB5_SWITCH AC_CHECK_FUNCS([krb5_kt_free_entry]) AC_CHECK_MEMBERS([krb5_keytab_entry.keyblock], , , [#include ]) RRA_LIB_KRB5_RESTORE -RRA_LIB_AFS -AS_IF([test x"$rra_afs" = xtrue], - [RRA_LIB_KRB4 - RRA_LIB_AFS_SWITCH - AC_CHECK_DECLS([ubik_Call], , , [#include ]) - RRA_LIB_AFS_RESTORE]) -AM_CONDITIONAL([AFS], [test x"$rra_afs" = xtrue]) AC_ARG_VAR([REMCTLD], [Path to the remctld binary]) AC_PATH_PROG([REMCTLD], [remctld], , [$PATH:/usr/sbin:/usr/local/sbin]) diff --git a/m4/kaserver.m4 b/m4/kaserver.m4 deleted file mode 100644 index 707a113..0000000 --- a/m4/kaserver.m4 +++ /dev/null @@ -1,94 +0,0 @@ -dnl kaserver.m4 -- Find the compiler and linker flags for OpenAFS kaserver. -dnl -dnl If --with-kaserver is given, finds the compiler and linker flags for -dnl building with OpenAFS libraries; sets AFS_CPPFLAGS, AFS_LDFLAGS, and -dnl AFS_LIBS as appropriate; and defines HAVE_AFS. Provides the macro -dnl RRA_LIB_AFS, which takes no arguments. -dnl -dnl This function also sets rra_kaserver to true if AFS was requested, which -dnl can be checked to determine if further checks should be done. -dnl -dnl Also provides RRA_LIB_AFS_SET to set CPPFLAGS, LDFLAGS, and LIBS to -dnl include the AFS libraries; RRA_LIB_AFS_SWITCH to do the same but save the -dnl current values first; and RRA_LIB_AFS_RESTORE to restore those settings to -dnl before the last RRA_LIB_AFS_SWITCH. -dnl -dnl Written by Russ Allbery -dnl Based on code developed by Derrick Brashear and Ken Hornstein of Sine -dnl Nomine Associates, on behalf of Stanford University. -dnl Copyright 2006, 2007, 2008 -dnl Board of Trustees, Leland Stanford Jr. University -dnl -dnl See LICENSE for licensing terms. - -dnl Set CPPFLAGS, LDFLAGS, and LIBS to values including the AFS settings. -AC_DEFUN([RRA_LIB_AFS_SET], -[CPPFLAGS="$AFS_CPPFLAGS $CPPFLAGS" - LDFLAGS="$AFS_LDFLAGS $LDFLAGS" - LIBS="$AFS_LIBS $LIBS"]) - -dnl Save the current CPPFLAGS, LDFLAGS, and LIBS settings and switch to -dnl versions that include the AFS flags. Used as a wrapper, with -dnl RRA_LIB_AFS_RESTORE, around tests. -AC_DEFUN([RRA_LIB_AFS_SWITCH], -[rra_afs_save_CPPFLAGS="$CPPFLAGS" - rra_afs_save_LDFLAGS="$LDFLAGS" - rra_afs_save_LIBS="$LIBS" - RRA_LIB_AFS_SET]) - -dnl Restore CPPFLAGS, LDFLAGS, and LIBS to their previous values (before -dnl RRA_LIB_AFS_SWITCH was called). -AC_DEFUN([RRA_LIB_AFS_RESTORE], -[CPPFLAGS="$rra_afs_save_CPPFLAGS" - LDFLAGS="$rra_afs_save_LDFLAGS" - LIBS="$rra_afs_save_LIBS"]) - -dnl The function that does the work checking for the AFS libraries. -AC_DEFUN([_RRA_LIB_AFS_CHECK], -[RRA_LIB_AFS_SET - LIBS= - AC_SEARCH_LIBS([pthread_getspecific], [pthread]) - AC_SEARCH_LIBS([res_search], [resolv], , - [AC_SEARCH_LIBS([__res_search], [resolv])]) - AC_SEARCH_LIBS([gethostbyname], [nsl]) - AC_SEARCH_LIBS([socket], [socket], , - [AC_CHECK_LIB([nsl], [socket], [LIBS="-lnsl -lsocket $LIBS"], , - [-lsocket])]) - AFS_LIBS="$AFS_LIBS $LIBS" - LIBS="$AFS_LIBS" - AC_CACHE_CHECK([whether linking with AFS libraries work], [rra_cv_lib_afs], - [AC_TRY_LINK( -[#include -#include ], -[char cell[256] = "EXAMPLE.COM"; -char realm[256]; -int local; - -ka_CellToRealm(cell, realm, &local);], - [rra_cv_lib_afs=yes], - [rra_cv_lib_afs=no])]) - AS_IF([test "$rra_cv_lib_afs" = no], - [AC_MSG_ERROR([unable to link test AFS program])]) - RRA_LIB_AFS_RESTORE]) - -dnl The public entry point. Sets up the --with option and only does the -dnl library check if AFS linkage was requested. -AC_DEFUN([RRA_LIB_AFS], -[rra_afs=false - AFS_CPPFLAGS= - AFS_LDFLAGS= - AFS_LIBS="-lafsauthent -lafsrpc" - AC_SUBST([AFS_CPPFLAGS]) - AC_SUBST([AFS_LDFLAGS]) - AC_SUBST([AFS_LIBS]) - AC_ARG_WITH([kaserver], - [AC_HELP_STRING([--with-kaserver@<:@=DIR@:>@], - [Compile with AFS kaserver sync support])], - [AS_IF([test x"$withval" != xno], - [rra_afs=true - AS_IF([test x"$withval" != xyes], - [AFS_CPPFLAGS="-I${withval}/include" - AFS_LDFLAGS="-L${withval}/lib"]) - _RRA_LIB_AFS_CHECK - AC_DEFINE([HAVE_AFS], 1, - [Define to enable AFS kaserver support.])])])]) diff --git a/m4/krb4.m4 b/m4/krb4.m4 deleted file mode 100644 index 75ca505..0000000 --- a/m4/krb4.m4 +++ /dev/null @@ -1,152 +0,0 @@ -dnl krb4.m4 -- Find the compiler and linker flags for Kerberos v4. -dnl -dnl Finds the compiler and linker flags for linking with Kerberos v4 libraries -dnl and sets the substitution variables KRB4_CPPFLAGS, KRB4_LDFLAGS, and -dnl KRB4_LIBS. Provides the --with-krb4 configure option to specify a -dnl non-standard path to the Kerberos libraries. Uses krb5-config where -dnl available unless reduced dependencies is requested. -dnl -dnl Provides the macro RRA_LIB_KRB4 and sets the substitution variables -dnl KRB4_CPPFLAGS, KRB4_LDFLAGS, and KRB4_LIBS. Also provides -dnl RRA_LIB_KRB4_SET to set CPPFLAGS, LDFLAGS, and LIBS to include the -dnl Kerberos libraries; RRA_LIB_KRB4_SWITCH to do the same but save the -dnl current values first; and RRA_LIB_KRB4_RESTORE to restore those settings -dnl to before the last RRA_LIB_KRB4_SWITCH. -dnl -dnl Written by Russ Allbery -dnl Copyright 2005, 2006, 2007, 2008 -dnl Board of Trustees, Leland Stanford Jr. University -dnl -dnl See LICENSE for licensing terms. - -dnl Set CPPFLAGS, LDFLAGS, and LIBS to values including the Kerberos v4 -dnl settings. -AC_DEFUN([RRA_LIB_KRB4_SET], -[CPPFLAGS="$KRB4_CPPFLAGS $CPPFLAGS" - LDFLAGS="$KRB4_LDFLAGS $LDFLAGS" - LIBS="$KRB4_LIBS $LIBS"]) - -dnl Save the current CPPFLAGS, LDFLAGS, and LIBS settings and switch to -dnl versions that include the Kerberos v4 flags. Used as a wrapper, with -dnl RRA_LIB_KRB4_RESTORE, around tests. -AC_DEFUN([RRA_LIB_KRB4_SWITCH], -[rra_krb4_save_CPPFLAGS="$CPPFLAGS" - rra_krb4_save_LDFLAGS="$LDFLAGS" - rra_krb4_save_LIBS="$LIBS" - RRA_LIB_KRB4_SET]) - -dnl Restore CPPFLAGS, LDFLAGS, and LIBS to their previous values (before -dnl RRA_LIB_KRB4_SWITCH was called). -AC_DEFUN([RRA_LIB_KRB4_RESTORE], -[CPPFLAGS="$rra_krb4_save_CPPFLAGS" - LDFLAGS="$rra_krb4_save_LDFLAGS" - LIBS="$rra_krb4_save_LIBS"]) - -dnl Set KRB4_CPPFLAGS and KRB4_LDFLAGS based on rra_krb4_root. -AC_DEFUN([_RRA_LIB_KRB4_PATHS], -[AS_IF([test x"$rra_krb4_root" != x], - [AS_IF([test x"$rra_krb4_root" != x/usr], - [KRB4_CPPFLAGS="-I${rra_krb4_root}/include"]) - KRB4_LDFLAGS="-L${rra_krb4_root}/lib"])]) - -dnl Does the appropriate library checks for reduced-dependency Kerberos v4 -dnl linkage. -AC_DEFUN([_RRA_LIB_KRB4_REDUCED], -[RRA_LIB_KRB4_SWITCH - AC_CHECK_LIB([krb4], [krb_get_svc_in_tkt], [KRB4_LIBS="-lkrb4"], - [AC_CHECK_LIB([krb], [krb_get_svc_in_tkt], [KRB4_LIBS="-lkrb"], - [AC_MSG_ERROR([cannot find usable Kerberos v4 library])])]) - RRA_LIB_KRB4_RESTORE]) - -dnl Does the appropriate library checks for Kerberos v4 linkage when we don't -dnl have krb5-config or reduced dependencies. -AC_DEFUN([_RRA_LIB_KRB4_MANUAL], -[RRA_LIB_KRB4_SWITCH - rra_krb4_extra= - LIBS= - AC_SEARCH_LIBS([res_search], [resolv], , - [AC_SEARCH_LIBS([__res_search], [resolv])]) - AC_SEARCH_LIBS([gethostbyname], [nsl]) - AC_SEARCH_LIBS([socket], [socket], , - [AC_CHECK_LIB([nsl], [socket], [LIBS="-lnsl -lsocket $LIBS"], , - [-lsocket])]) - AC_SEARCH_LIBS([crypt], [crypt]) - rra_krb4_extra="$LIBS" - LIBS="$rra_krb4_save_LIBS" - AC_CHECK_LIB([crypto], [des_set_key], - [rra_krb4_extra="-lcrypto $rra_krb4_extra"], - [AC_CHECK_LIB([des], [des_set_key], - [rra_krb4_extra="-ldes $rra_krb4_extra"])]) - AC_CHECK_LIB([krb], [krb_get_svc_in_tkt], - [KRB4_LIBS="-lkrb $rra_krb4_extra"], - [rra_krb4_extra="-ldes425 -lkrb5 -lk5crypto -lcom_err $rra_krb4_extra" - AC_CHECK_LIB([krb5support], [krb5int_getspecific], - [rra_krb4_extra="$rra_krb4_extra -lkrb5support"], - [AC_CHECK_LIB([pthreads], [pthread_setspecific], - [rra_krb4_pthread="-lpthreads"], - [AC_CHECK_LIB([pthread], [pthread_setspecific], - [rra_krb4_pthread="-lpthread"])]) - AC_CHECK_LIB([krb5support], [krb5int_setspecific], - [rra_krb4_extra="-lkrb5support $rra_krb4_pthread"], , - [$rra_krb4_pthread])]) - AC_CHECK_LIB([krb4], [krb_get_svc_in_tkt], - [KRB4_LIBS="-lkrb4 $rra_krb4_extra"], - [AC_MSG_ERROR([cannot find usable Kerberos v4 library])], - [$rra_krb4_extra])], - [$rra_krb4_extra]) - RRA_LIB_KRB4_RESTORE]) - -dnl Additional checks for portability that apply to either way that we find -dnl the right libraries. -AC_DEFUN([_RRA_LIB_KRB4_EXTRA], -[RRA_LIB_KRB4_SWITCH - AC_CHECK_HEADERS([kerberosIV/krb.h]) - RRA_LIB_KRB4_RESTORE]) - -dnl Sanity-check the results of krb5-config and be sure we can really link a -dnl Kerberos program. -AC_DEFUN([_RRA_LIB_KRB4_CHECK], -[RRA_LIB_KRB4_SWITCH - AC_CHECK_FUNC([krb_get_svc_in_tkt], , - [AC_MSG_FAILURE([krb5-config results fail for Kerberos v4])]) - RRA_LIB_KRB4_RESTORE]) - -dnl The main macro. -AC_DEFUN([RRA_LIB_KRB4], -[AC_REQUIRE([RRA_ENABLE_REDUCED_DEPENDS]) -rra_krb4_root= -KRB4_CPPFLAGS= -KRB4_LDFLAGS= -KRB4_LIBS= -AC_SUBST([KRB4_CPPFLAGS]) -AC_SUBST([KRB4_LDFLAGS]) -AC_SUBST([KRB4_LIBS]) -AC_ARG_WITH([krb4], - [AC_HELP_STRING([--with-krb4=DIR], - [Location of Kerberos v4 headers and libraries])], - [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], - [rra_krb4_root="$withval"])]) -AS_IF([test x"$rra_reduced_depends" = xtrue], - [_RRA_LIB_KRB4_PATHS - _RRA_LIB_KRB4_REDUCED], - [AC_ARG_VAR([KRB5_CONFIG], [Path to krb5-config]) - AS_IF([test x"$rra_krb4_root" != x && test -z "$KRB5_CONFIG"], - [AS_IF([test -x "${rra_krb4_root}/bin/krb5-config"], - [KRB5_CONFIG="${rra_krb4_root}/bin/krb5-config"])], - [AC_PATH_PROG([KRB5_CONFIG], [krb5-config])]) - AS_IF([test x"$KRB5_CONFIG" != x && test -x "$KRB5_CONFIG"], - [AC_CACHE_CHECK([for krb4 support in krb5-config], - [rra_cv_lib_krb4_config], - [AS_IF(["$KRB5_CONFIG" | grep krb4 > /dev/null 2>&1], - [rra_cv_lib_krb4_config=yes], - [rra_cv_lib_krb4_config=no])]) - AS_IF([test "$rra_cv_lib_krb4_config" = yes], - [KRB4_CPPFLAGS=`"$KRB5_CONFIG" --cflags krb4` - KRB4_LIBS=`"$KRB5_CONFIG" --libs krb4`], - [_RRA_LIB_KRB4_PATHS - _RRA_LIB_KRB4_MANUAL]) - KRB4_CPPFLAGS=`echo "$KRB5_CPPFLAGS" | sed 's%-I/usr/include ?%%'` - _RRA_LIB_KRB4_CHECK], - [_RRA_LIB_KRB4_PATHS - _RRA_LIB_KRB4_MANUAL])]) - _RRA_LIB_KRB4_EXTRA]) -- cgit v1.2.3 From fecab76139894250a57fa3761b3b90944c8cfa9d Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Tue, 9 Feb 2010 13:43:18 -0800 Subject: Remove the test suite for kasetkey --- configure.ac | 1 - tests/TESTS | 1 - tests/kasetkey/basic-t.in | 127 ---------------------------------------------- 3 files changed, 129 deletions(-) delete mode 100644 tests/kasetkey/basic-t.in (limited to 'configure.ac') diff --git a/configure.ac b/configure.ac index de998c0..8d00229 100644 --- a/configure.ac +++ b/configure.ac @@ -63,7 +63,6 @@ AC_CONFIG_FILES([tests/client/full-t], [chmod +x tests/client/full-t]) AC_CONFIG_FILES([tests/client/pod-t], [chmod +x tests/client/pod-t]) AC_CONFIG_FILES([tests/client/prompt-t], [chmod +x tests/client/prompt-t]) AC_CONFIG_FILES([tests/data/cmd-wrapper], [chmod +x tests/data/cmd-wrapper]) -AC_CONFIG_FILES([tests/kasetkey/basic-t], [chmod +x tests/kasetkey/basic-t]) AC_CONFIG_FILES([tests/server/admin-t], [chmod +x tests/server/admin-t]) AC_CONFIG_FILES([tests/server/backend-t], [chmod +x tests/server/backend-t]) AC_CONFIG_FILES([tests/server/keytab-t], [chmod +x tests/server/keytab-t]) diff --git a/tests/TESTS b/tests/TESTS index c94cce0..a446643 100644 --- a/tests/TESTS +++ b/tests/TESTS @@ -2,7 +2,6 @@ client/basic client/full client/pod client/prompt -kasetkey/basic portable/asprintf portable/snprintf portable/strlcat diff --git a/tests/kasetkey/basic-t.in b/tests/kasetkey/basic-t.in deleted file mode 100644 index bb086d6..0000000 --- a/tests/kasetkey/basic-t.in +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/perl -w -# -# Tests for basic kasetkey functionality. -# -# We only test creation (with a random key), deletion, enable, disable, and -# examine. That's enough to verify that kasetkey is basically working, and -# since AFS kaservers are becoming scarce, it's probably not worth the effort -# to do anything more comprehensive. -# -# We do test creation of a principal with a known key given a srvtab from -# inside the wallet server test suite already. -# -# Written by Russ Allbery -# Copyright 2008 Board of Trustees, Leland Stanford Jr. University -# -# See LICENSE for licensing terms. - -BEGIN { our $total = 27 } -use Test::More tests => $total; - -use lib '@abs_top_builddir@/perl/blib/lib'; -use lib '@abs_top_srcdir@/perl/t/lib'; -use Util; - -# Global variables used for the kasetkey configuration. -our $ADMIN; -our $SRVTAB; - -# Make a call to the kasetkey client and returns the standard output, the -# standard error, and the exit status as a list. -sub kasetkey { - my @command = @_; - my $pid = fork; - if (not defined $pid) { - die "cannot fork: $!\n"; - } elsif ($pid == 0) { - open (STDOUT, '>', 'kasetkey.out') - or die "cannot create kasetkey.out: $!\n"; - open (STDERR, '>', 'kasetkey.err') - or die "cannot create kasetkey.err: $!\n"; - exec ('@abs_top_builddir@/kasetkey/kasetkey', '-a', $ADMIN, - '-k', $SRVTAB, @command) - or die "cannot run @abs_top_builddir@/kasetkey/kasetky: $!\n"; - } else { - waitpid ($pid, 0); - } - my $status = ($? >> 8); - local $/; - open (OUT, '<', 'kasetkey.out') or die "cannot open kasetkey.out: $!\n"; - my $output = ; - close OUT; - open (ERR, '<', 'kasetkey.err') or die "cannot open kasetkey.err: $!\n"; - my $error = ; - close ERR; - unlink ('kasetkey.out', 'kasetkey.err'); - return ($output, $error, $status); -} - -SKIP: { - skip 'no AFS kaserver configuration', $total - unless -f '@abs_top_builddir@/tests/data/test.srvtab'; - skip 'no AFS kaserver support', $total, - unless -x '@abs_top_builddir@/kasetkey/kasetkey'; - - # Set up the configuration. - $ADMIN = contents ('@abs_top_builddir@/tests/data/test.admin'); - $SRVTAB = '@abs_top_builddir@/tests/data/test.srvtab'; - my $realm = $ADMIN; - $realm =~ s/^[^\@]+\@//; - my $principal = "wallet.one\@$realm"; - - # Now we can start manipulating principals. Test examine and create. - my ($out, $err, $status) = kasetkey ('-e', $principal); - is ($status, 1, 'Examining a non-existent principal fails'); - is ($out, '', ' with no output'); - is ($err, "no such entry in the database\n", ' and the right error'); - ($out, $err, $status) = kasetkey ('-s', $principal, '-r'); - is ($status, 0, 'Creating a principal succeeds'); - is ($out, '', ' with no output'); - is ($err, '', ' and no error'); - ($out, $err, $status) = kasetkey ('-e', $principal); - is ($status, 0, 'Examining a principal succeeds'); - $out =~ s/: (Sun|Mon|Tue|Wed|Thu|Fri|Sat).*/: DATE/g; - my $shortadmin = $ADMIN; - $shortadmin =~ s/\@.*//; - my $enabled = <<"EOE"; -status: enabled -account expiration: never -password last changed: DATE -modification time: DATE -modified by: $shortadmin -EOE - is ($out, $enabled, ' with the right output'); - is ($err, '', ' and no error'); - - # Test enable and disable. - ($out, $err, $status) = kasetkey ('-s', $principal, '-n'); - is ($status, 0, 'Disabling a principal succeeds'); - is ($out, '', ' with no output'); - is ($err, '', ' and no error'); - ($out, $err, $status) = kasetkey ('-e', $principal); - is ($status, 0, ' and examining it still succeeds'); - $out =~ s/: (Sun|Mon|Tue|Wed|Thu|Fri|Sat).*/: DATE/g; - my $disabled = $enabled; - $disabled =~ s/enabled/disabled/; - is ($out, $disabled, ' with the right output'); - is ($err, '', ' and no error'); - ($out, $err, $status) = kasetkey ('-s', $principal, '-t'); - is ($status, 0, 'Enabling a principal succeeds'); - is ($out, '', ' with no output'); - is ($err, '', ' and no error'); - ($out, $err, $status) = kasetkey ('-e', $principal); - is ($status, 0, ' and examining it still succeeds'); - $out =~ s/: (Sun|Mon|Tue|Wed|Thu|Fri|Sat).*/: DATE/g; - is ($out, $enabled, ' with the right output'); - is ($err, '', ' and no error'); - - # Test deletion. - ($out, $err, $status) = kasetkey ('-D', $principal); - is ($status, 0, 'Deleting the principal succeeds'); - is ($out, '', ' with no output'); - is ($err, '', ' and no error'); - ($out, $err, $status) = kasetkey ('-e', $principal); - is ($status, 1, ' and now examining it fails'); - is ($out, '', ' with no output'); - is ($err, "no such entry in the database\n", ' and the right error'); -} -- cgit v1.2.3 From 838a73223d19e64a6556047791006f068b779307 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Tue, 9 Feb 2010 15:21:12 -0800 Subject: Update the Autoconf code to rra-c-util 3.0 * Sanity-check the results of krb5-config before proceeding. * Fall back on manual probing if krb5-config results don't work. * Add --with-krb5-include and --with-krb5-lib configure options. * Add --with-remctl-include and --with-remctl-lib configure options. * Add --with-gssapi-include and --with-gssapi-lib configure options. * Don't break if the user clobbers CPPFLAGS at build time. * Suppress error output from krb5-config probes. * Prefer KRB5_CONFIG over a path constructed from --with-*. * Update GSS-API probes for Solaris 10's native implementation. * Change AC_TRY_* to AC_*_IFELSE as recommended by Autoconf. Also strip out more outdated AFS kaserver instructions from README. --- NEWS | 13 +++++ README | 41 ++++++------- configure.ac | 31 +++++----- m4/gssapi.m4 | 101 ++++++++++++++++++++------------ m4/krb5.m4 | 164 +++++++++++++++++++++++++++++++--------------------- m4/lib-depends.m4 | 4 +- m4/lib-pathname.m4 | 55 ++++++++++++++++++ m4/remctl.m4 | 79 +++++++++++++++++-------- m4/snprintf.m4 | 17 +++--- m4/vamacros.m4 | 55 ++++++++++++------ portable/snprintf.c | 7 +-- 11 files changed, 368 insertions(+), 199 deletions(-) create mode 100644 m4/lib-pathname.m4 (limited to 'configure.ac') diff --git a/NEWS b/NEWS index 5b821f2..661771a 100644 --- a/NEWS +++ b/NEWS @@ -50,6 +50,19 @@ wallet 0.10 (unreleased) implementation than the wallet client. This primarily helps with testing. + Update to rra-c-util 3.0: + + * Sanity-check the results of krb5-config before proceeding. + * Fall back on manual probing if krb5-config results don't work. + * Add --with-krb5-include and --with-krb5-lib configure options. + * Add --with-remctl-include and --with-remctl-lib configure options. + * Add --with-gssapi-include and --with-gssapi-lib configure options. + * Don't break if the user clobbers CPPFLAGS at build time. + * Suppress error output from krb5-config probes. + * Prefer KRB5_CONFIG over a path constructed from --with-*. + * Update GSS-API probes for Solaris 10's native implementation. + * Change AC_TRY_* to AC_*_IFELSE as recommended by Autoconf. + wallet 0.9 (2008-04-24) The wallet command-line client now reads the data for store from a diff --git a/README b/README index 6e165ec..6c00234 100644 --- a/README +++ b/README @@ -3,9 +3,10 @@ Written by Russ Allbery - Copyright 2006, 2007, 2008 Board of Trustees, Leland Stanford Jr. - University. This software is distributed under a BSD-style license. - Please see the file LICENSE in the distribution for more information. + Copyright 2006, 2007, 2008, 2009, 2010 Board of Trustees, Leland + Stanford Jr. University. This software is distributed under a BSD-style + license. Please see the file LICENSE in the distribution for more + information. This software is beta-quality and should be treated with caution. It is currently being tested for production deployment at Stanford. @@ -159,9 +160,9 @@ BUILD AND INSTALLATION If remctl was installed in a path not normally searched by your compiler, you must specify its installation prefix to configure with the - --with-remctl=DIR option. If the GSS-API libraries used by remctl - aren't in a path normally searched by your compiler, you must generally - also specify its installation prefix with the --with-gssapi=DIR option. + --with-remctl=DIR option, or alternately set the path to the include + files and libraries separately with --with-remctl-include=DIR and + --with-remctl-lib=DIR. Normally, configure will use krb5-config to determine the flags to use to compile with your Kerberos libraries. If krb5-config isn't found, it @@ -170,9 +171,16 @@ BUILD AND INSTALLATION path is not the one corresponding to the Kerberos libraries you want to use or if your Kerberos libraries and includes aren't in a location searched by default by your compiler, you need to specify - --with-krb5=PATH: + --with-krb5=PATH and --with-gssapi=PATH: - ./configure --with-krb5=/usr/pubsw + ./configure --with-krb5=/usr/pubsw --with-gssapi=/usr/pubsw + + You can also individually set the paths to the include directory and the + library directory with --with-krb5-include, --with-krb5-lib, + --with-gssapi-include, and --with-gssapi-lib. You may need to do this + if Autoconf can't figure out whether to use lib, lib32, or lib64 on your + platform. Note that these settings aren't used if a krb5-config script + is found. To specify a particular krb5-config script to use, either set the KRB5_CONFIG environment variable or pass it to configure like: @@ -184,18 +192,6 @@ BUILD AND INSTALLATION ./configure KRB5_CONFIG=/nonexistent - To build with AFS kaserver synchronization support, pass --with-kaserver - to configure. You may need to include the path to the AFS include files - and libraries, such as: - - ./configure --with-kaserver=/usr/afsws - - The AFS kaserver support also requires Kerberos v4 libraries and tries - to use krb5-config to find such libraries. If your Kerberos v4 - libraries aren't somewhere found by your compiler and the krb5-config - script doesn't produce correct results, you need to specify - --with-krb4=PATH giving the root path of the Kerberos v4 installation. - You can build wallet in a different directory from the source if you wish. To do this, create a new empty directory, cd to that directory, and then give the path to configure when running configure. Everything @@ -228,10 +224,7 @@ TESTING perl/t/data/README and follow the instructions in those files to enable the full test - suite. Note that testing the AFS kaserver requires creating a srvtab - with ADMIN access to a running AFS kaserver; if you don't care about AFS - kaserver synchronization, you may want to skip that part of the test - suite configuration. + suite. The test suite also requires some additional software be installed that isn't otherwise used by the wallet. See REQUIREMENTS above for the full diff --git a/configure.ac b/configure.ac index 8d00229..78fecea 100644 --- a/configure.ac +++ b/configure.ac @@ -1,32 +1,40 @@ -dnl Process this file with Autoconf to produce a configure script. +dnl Autoconf configuration for wallet. dnl dnl Written by Russ Allbery -dnl Copyright 2006, 2007, 2008 +dnl Copyright 2006, 2007, 2008, 2010 dnl Board of Trustees, Leland Stanford Jr. University dnl dnl See LICENSE for licensing terms. -AC_REVISION([$Revision$]) -AC_PREREQ([2.61]) +dnl We cannot use -Wall -Werror with AM_INIT_AUTOMAKE since we override +dnl distuninstallcheck (not supported by Perl). +AC_PREREQ([2.64]) AC_INIT([wallet], [0.9], [rra@stanford.edu]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_LIBOBJ_DIR([portable]) -AM_INIT_AUTOMAKE([1.10]) +AC_CONFIG_MACRO_DIR([m4]) +AM_INIT_AUTOMAKE([1.11 check-news]) AM_MAINTAINER_MODE AC_PROG_CC +AC_USE_SYSTEM_EXTENSIONS AM_PROG_CC_C_O AC_PROG_INSTALL AC_PROG_RANLIB -AC_AIX -AC_GNU_SOURCE + +RRA_LIB_REMCTL +RRA_LIB_KRB5 +RRA_LIB_KRB5_SWITCH +AC_CHECK_FUNCS([krb5_kt_free_entry]) +AC_CHECK_MEMBERS([krb5_keytab_entry.keyblock], , , [#include ]) +RRA_LIB_KRB5_RESTORE AC_HEADER_STDBOOL AC_CHECK_HEADERS([sys/bitypes.h syslog.h]) AC_CHECK_DECLS([snprintf, vsnprintf]) RRA_C_C99_VAMACROS RRA_C_GNU_VAMACROS -AC_CHECK_TYPES([long long]) +AC_TYPE_LONG_LONG_INT RRA_FUNC_SNPRINTF AC_CHECK_FUNCS([setrlimit]) AC_REPLACE_FUNCS([asprintf strlcat strlcpy]) @@ -43,13 +51,6 @@ AC_ARG_WITH([wallet-port], [AC_DEFINE_UNQUOTED([WALLET_PORT], [$withval], [Define to the default server port.])])]) -RRA_LIB_REMCTL -RRA_LIB_KRB5 -RRA_LIB_KRB5_SWITCH -AC_CHECK_FUNCS([krb5_kt_free_entry]) -AC_CHECK_MEMBERS([krb5_keytab_entry.keyblock], , , [#include ]) -RRA_LIB_KRB5_RESTORE - AC_ARG_VAR([REMCTLD], [Path to the remctld binary]) AC_PATH_PROG([REMCTLD], [remctld], , [$PATH:/usr/sbin:/usr/local/sbin]) AS_IF([test x"$REMCTLD" != x], diff --git a/m4/gssapi.m4 b/m4/gssapi.m4 index a352e38..4b08569 100644 --- a/m4/gssapi.m4 +++ b/m4/gssapi.m4 @@ -1,30 +1,24 @@ -dnl gssapi.m4 -- Find the compiler and linker flags for GSS-API. +dnl Find the compiler and linker flags for GSS-API. dnl -dnl Finds the compiler and linker flags for linking with GSS-API libraries -dnl and sets the substitution variables GSSAPI_CPPFLAGS, GSSAPI_LDFLAGS, and -dnl GSSAPI_LIBS. Provides the --with-gssapi configure option to specify a -dnl non-standard path to the GSS-API libraries. Uses krb5-config where -dnl available unless reduced dependencies is requested. +dnl Finds the compiler and linker flags for linking with GSS-API libraries. +dnl Provides the --with-gssapi, --with-gssapi-include, and --with-gssapi-lib +dnl configure option to specify a non-standard path to the GSS-API libraries. +dnl Uses krb5-config where available unless reduced dependencies is requested. dnl dnl Provides the macro RRA_LIB_GSSAPI and sets the substitution variables dnl GSSAPI_CPPFLAGS, GSSAPI_LDFLAGS, and GSSAPI_LIBS. Also provides -dnl RRA_LIB_GSSAPI_SET to set CPPFLAGS, LDFLAGS, and LIBS to include the -dnl GSS-API libraries; RRA_LIB_GSSAPI_SWITCH to do the same but save the -dnl current values first; and RRA_LIB_GSSAPI_RESTORE to restore those settings -dnl to before the last RRA_LIB_GSSAPI_SWITCH. +dnl RRA_LIB_GSSAPI_SWITCH to set CPPFLAGS, LDFLAGS, and LIBS to include the +dnl GSS-API libraries, saving the ecurrent values, and RRA_LIB_GSSAPI_RESTORE +dnl to restore those settings to before the last RRA_LIB_GSSAPI_SWITCH. +dnl +dnl Depends on RRA_ENABLE_REDUCED_DEPENDS and RRA_SET_LDFLAGS. dnl dnl Written by Russ Allbery -dnl Copyright 2005, 2006, 2007, 2008 +dnl Copyright 2005, 2006, 2007, 2008, 2009 dnl Board of Trustees, Leland Stanford Jr. University dnl dnl See LICENSE for licensing terms. -dnl Set CPPFLAGS, LDFLAGS, and LIBS to values including the GSS-API settings. -AC_DEFUN([RRA_LIB_GSSAPI_SET], -[CPPFLAGS="$GSSAPI_CPPFLAGS $CPPFLAGS" - LDFLAGS="$GSSAPI_LDFLAGS $LDFLAGS" - LIBS="$GSSAPI_LIBS $LIBS"]) - dnl Save the current CPPFLAGS, LDFLAGS, and LIBS settings and switch to dnl versions that include the GSS-API flags. Used as a wrapper, with dnl RRA_LIB_GSSAPI_RESTORE, around tests. @@ -32,7 +26,9 @@ AC_DEFUN([RRA_LIB_GSSAPI_SWITCH], [rra_gssapi_save_CPPFLAGS="$CPPFLAGS" rra_gssapi_save_LDFLAGS="$LDFLAGS" rra_gssapi_save_LIBS="$LIBS" - RRA_LIB_GSSAPI_SET]) + CPPFLAGS="$GSSAPI_CPPFLAGS $CPPFLAGS" + LDFLAGS="$GSSAPI_LDFLAGS $LDFLAGS" + LIBS="$GSSAPI_LIBS $LIBS"]) dnl Restore CPPFLAGS, LDFLAGS, and LIBS to their previous values (before dnl RRA_LIB_GSSAPI_SWITCH was called). @@ -41,12 +37,18 @@ AC_DEFUN([RRA_LIB_GSSAPI_RESTORE], LDFLAGS="$rra_gssapi_save_LDFLAGS" LIBS="$rra_gssapi_save_LIBS"]) -dnl Set GSSAPI_CPPFLAGS and GSSAPI_LDFLAGS based on rra_gssapi_root. +dnl Set GSSAPI_CPPFLAGS and GSSAPI_LDFLAGS based on rra_gssapi_root, +dnl rra_gssapi_libdir, and rra_gssapi_includedir. AC_DEFUN([_RRA_LIB_GSSAPI_PATHS], -[AS_IF([test x"$rra_gssapi_root" != x], - [AS_IF([test x"$rra_gssapi_root" != x/usr], - [GSSAPI_CPPFLAGS="-I${rra_gssapi_root}/include"]) - GSSAPI_LDFLAGS="-L${rra_gssapi_root}/lib"])]) +[AS_IF([test x"$rra_gssapi_libdir" != x], + [GSSAPI_LDFLAGS="-L$rra_gssapi_libdir"], + [AS_IF([test x"$rra_gssapi_root" != x], + [RRA_SET_LDFLAGS([GSSAPI_LDFLAGS], [$rra_gssapi_root])])]) + AS_IF([test x"$rra_gssapi_includedir" != x], + [GSSAPI_CPPFLAGS="-I$rra_gssapi_includedir"], + [AS_IF([test x"$rra_gssapi_root" != x], + [AS_IF([test x"$rra_gssapi_root" != x/usr], + [GSSAPI_CPPFLAGS="-I${rra_gssapi_root}/include"])])])]) dnl Does the appropriate library checks for reduced-dependency GSS-API dnl linkage. @@ -54,10 +56,13 @@ AC_DEFUN([_RRA_LIB_GSSAPI_REDUCED], [RRA_LIB_GSSAPI_SWITCH AC_CHECK_LIB([gssapi_krb5], [gss_import_name], [GSSAPI_LIBS="-lgssapi_krb5"], [AC_CHECK_LIB([gssapi], [gss_import_name], [GSSAPI_LIBS="-lgssapi"], - [AC_MSG_ERROR([cannot find usable GSS-API library])])])]) + [AC_CHECK_LIB([gss], [gss_import_name], [GSSAPI_LIBS="-lgss"], + [AC_MSG_ERROR([cannot find usable GSS-API library])])])])]) dnl Does the appropriate library checks for GSS-API linkage when we don't -dnl have krb5-config or reduced dependencies. +dnl have krb5-config or reduced dependencies. libgss is used as a last +dnl resort, since it may be a non-functional mech-independent wrapper, but +dnl it's the right choice on Solaris 10. AC_DEFUN([_RRA_LIB_GSSAPI_MANUAL], [RRA_LIB_GSSAPI_SWITCH rra_gssapi_extra= @@ -91,53 +96,73 @@ AC_DEFUN([_RRA_LIB_GSSAPI_MANUAL], rra_gssapi_extra="-lkrb5 $rra_gssapi_extra" AC_CHECK_LIB([gssapi_krb5], [gss_import_name], [GSSAPI_LIBS="-lgssapi_krb5 $rra_gssapi_extra"], - [AC_MSG_ERROR([cannot find usable GSS-API library])], + [AC_CHECK_LIB([gss], [gss_import_name], + [GSSAPI_LIBS="-lgss"], + [AC_MSG_ERROR([cannot find usable GSS-API library])])], [$rra_gssapi_extra])], [-lkrb5 -lasn1 -lroken -lcrypto -lcom_err $rra_gssapi_extra]) RRA_LIB_GSSAPI_RESTORE]) dnl Sanity-check the results of krb5-config and be sure we can really link a -dnl GSS-API program. +dnl GSS-API program. If not, fall back on the manual check. AC_DEFUN([_RRA_LIB_GSSAPI_CHECK], [RRA_LIB_GSSAPI_SWITCH - AC_CHECK_FUNC([gss_import_name], , - [AC_MSG_FAILURE([krb5-config results fail for GSS-API])]) - RRA_LIB_GSSAPI_RESTORE]) + AC_CHECK_FUNC([gss_import_name], + [RRA_LIB_GSSAPI_RESTORE], + [RRA_LIB_GSSAPI_RESTORE + GSSAPI_CPPFLAGS= + GSSAPI_LIBS= + _RRA_LIB_GSSAPI_PATHS + _RRA_LIB_GSSAPI_MANUAL])]) dnl The main macro. AC_DEFUN([RRA_LIB_GSSAPI], [AC_REQUIRE([RRA_ENABLE_REDUCED_DEPENDS]) rra_gssapi_root= + rra_gssapi_libdir= + rra_gssapi_includedir= GSSAPI_CPPFLAGS= GSSAPI_LDFLAGS= GSSAPI_LIBS= AC_SUBST([GSSAPI_CPPFLAGS]) AC_SUBST([GSSAPI_LDFLAGS]) AC_SUBST([GSSAPI_LIBS]) + AC_ARG_WITH([gssapi], - [AC_HELP_STRING([--with-gssapi=DIR], + [AS_HELP_STRING([--with-gssapi=DIR], [Location of GSS-API headers and libraries])], [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], [rra_gssapi_root="$withval"])]) + AC_ARG_WITH([gssapi-include], + [AS_HELP_STRING([--with-gssapi-include=DIR], + [Location of GSS-API headers])], + [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], + [rra_gssapi_includedir="$withval"])]) + AC_ARG_WITH([gssapi-lib], + [AS_HELP_STRING([--with-gssapi-lib=DIR], + [Location of GSS-API libraries])], + [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], + [rra_gssapi_libdir="$withval"])]) + AS_IF([test x"$rra_reduced_depends" = xtrue], [_RRA_LIB_GSSAPI_PATHS _RRA_LIB_GSSAPI_REDUCED], [AC_ARG_VAR([KRB5_CONFIG], [Path to krb5-config]) - AS_IF([test x"$rra_gssapi_root" != x], + AS_IF([test x"$rra_gssapi_root" != x && test -z "$KRB5_CONFIG"], [AS_IF([test -x "${rra_gssapi_root}/bin/krb5-config"], [KRB5_CONFIG="${rra_gssapi_root}/bin/krb5-config"])], [AC_PATH_PROG([KRB5_CONFIG], [krb5-config])]) - AS_IF([test x"$KRB5_CONFIG" != x], + AS_IF([test x"$KRB5_CONFIG" != x && test -x "$KRB5_CONFIG"], [AC_CACHE_CHECK([for gssapi support in krb5-config], [rra_cv_lib_gssapi_config], - [AS_IF(["$KRB5_CONFIG" | grep gssapi > /dev/null 2>&1], + [AS_IF(["$KRB5_CONFIG" 2>&1 | grep gssapi >/dev/null 2>&1], [rra_cv_lib_gssapi_config=yes], [rra_cv_lib_gssapi_config=no])]) AS_IF([test "$rra_cv_lib_gssapi_config" = yes], - [GSSAPI_CPPFLAGS=`"$KRB5_CONFIG" --cflags gssapi` - GSSAPI_LIBS=`"$KRB5_CONFIG" --libs gssapi`], - [GSSAPI_CPPFLAGS=`"$KRB5_CONFIG" --cflags` - GSSAPI_LIBS=`"$KRB5_CONFIG" --libs`]) + [GSSAPI_CPPFLAGS=`"$KRB5_CONFIG" --cflags gssapi 2>/dev/null` + GSSAPI_LIBS=`"$KRB5_CONFIG" --libs gssapi 2>/dev/null`], + [GSSAPI_CPPFLAGS=`"$KRB5_CONFIG" --cflags 2>/dev/null` + GSSAPI_LIBS=`"$KRB5_CONFIG" --libs 2>/dev/null`]) GSSAPI_CPPFLAGS=`echo "$GSSAPI_CPPFLAGS" \ | sed 's%-I/usr/include ?%%'` _RRA_LIB_GSSAPI_CHECK], diff --git a/m4/krb5.m4 b/m4/krb5.m4 index 12d97f8..bba9694 100644 --- a/m4/krb5.m4 +++ b/m4/krb5.m4 @@ -1,41 +1,36 @@ -dnl krb5.m4 -- Find the compiler and linker flags for Kerberos v5. +dnl Find the compiler and linker flags for Kerberos v5. dnl -dnl Finds the compiler and linker flags for linking with Kerberos v5 libraries -dnl and sets the substitution variables KRB5_CPPFLAGS, KRB5_LDFLAGS, and -dnl KRB5_LIBS. Provides the --with-krb5 configure option to specify a -dnl non-standard path to the Kerberos libraries. Uses krb5-config where -dnl available unless reduced dependencies is requested. -dnl -dnl Sets an Automake conditional saying whether we use com_err, since if we're -dnl also linking with AFS libraries, we may have to change library ordering in -dnl that case. +dnl Finds the compiler and linker flags for linking with Kerberos v5 +dnl libraries. Provides the --with-krb5, --with-krb5-include, and +dnl --with-krb5-lib configure options to specify non-standards paths to the +dnl Kerberos libraries. Uses krb5-config where available unless reduced +dnl dependencies is requested. dnl dnl Provides the macro RRA_LIB_KRB5 and sets the substitution variables dnl KRB5_CPPFLAGS, KRB5_LDFLAGS, and KRB5_LIBS. Also provides -dnl RRA_LIB_KRB5_SET to set CPPFLAGS, LDFLAGS, and LIBS to include the -dnl Kerberos libraries; RRA_LIB_KRB5_SWITCH to do the same but save the -dnl current values first; and RRA_LIB_KRB5_RESTORE to restore those settings -dnl to before the last RRA_LIB_KRB5_SWITCH. +dnl RRA_LIB_KRB5_SWITCH to set CPPFLAGS, LDFLAGS, and LIBS to include the +dnl Kerberos libraries, saving the current values first, and +dnl RRA_LIB_KRB5_RESTORE to restore those settings to before the last +dnl RRA_LIB_KRB5_SWITCH. dnl -dnl Also provides the RRA_LIB_KRB5_OPTIONAL macro, which should be used if -dnl Kerberos support is optional. This macro will still always set the -dnl substitution variables, but they'll be empty unless --with-krb5 is used. -dnl Also, HAVE_KERBEROS will be defined if --with-krb5 is given and +dnl Provides the RRA_LIB_KRB5_OPTIONAL macro, which should be used if Kerberos +dnl support is optional. This macro will still always set the substitution +dnl variables, but they'll be empty unless --with-krb5 is given. Also, +dnl HAVE_KERBEROS will be defined if --with-krb5 is given and dnl $rra_use_kerberos will be set to "true". dnl +dnl Sets the Automake conditional KRB5_USES_COM_ERR saying whether we use +dnl com_err, since if we're also linking with AFS libraries, we may have to +dnl change library ordering in that case. +dnl +dnl Depends on RRA_ENABLE_REDUCED_DEPENDS and RRA_SET_LDFLAGS. +dnl dnl Written by Russ Allbery -dnl Copyright 2005, 2006, 2007, 2008 +dnl Copyright 2005, 2006, 2007, 2008, 2009 dnl Board of Trustees, Leland Stanford Jr. University dnl dnl See LICENSE for licensing terms. -dnl Set CPPFLAGS, LDFLAGS, and LIBS to values including the Kerberos v5 -dnl settings. -AC_DEFUN([RRA_LIB_KRB5_SET], -[CPPFLAGS="$KRB5_CPPFLAGS $CPPFLAGS" - LDFLAGS="$KRB5_LDFLAGS $LDFLAGS" - LIBS="$KRB5_LIBS $LIBS"]) - dnl Save the current CPPFLAGS, LDFLAGS, and LIBS settings and switch to dnl versions that include the Kerberos v5 flags. Used as a wrapper, with dnl RRA_LIB_KRB5_RESTORE, around tests. @@ -43,7 +38,9 @@ AC_DEFUN([RRA_LIB_KRB5_SWITCH], [rra_krb5_save_CPPFLAGS="$CPPFLAGS" rra_krb5_save_LDFLAGS="$LDFLAGS" rra_krb5_save_LIBS="$LIBS" - RRA_LIB_KRB5_SET]) + CPPFLAGS="$KRB5_CPPFLAGS $CPPFLAGS" + LDFLAGS="$KRB5_LDFLAGS $LDFLAGS" + LIBS="$KRB5_LIBS $LIBS"]) dnl Restore CPPFLAGS, LDFLAGS, and LIBS to their previous values (before dnl RRA_LIB_KRB5_SWITCH was called). @@ -52,12 +49,18 @@ AC_DEFUN([RRA_LIB_KRB5_RESTORE], LDFLAGS="$rra_krb5_save_LDFLAGS" LIBS="$rra_krb5_save_LIBS"]) -dnl Set KRB5_CPPFLAGS and KRB5_LDFLAGS based on rra_krb5_root. +dnl Set KRB5_CPPFLAGS and KRB5_LDFLAGS based on rra_krb5_root, +dnl rra_krb5_libdir, and rra_krb5_includedir. AC_DEFUN([_RRA_LIB_KRB5_PATHS], -[AS_IF([test x"$rra_krb5_root" != x], - [AS_IF([test x"$rra_krb5_root" != x/usr], - [KRB5_CPPFLAGS="-I${rra_krb5_root}/include"]) - KRB5_LDFLAGS="-L${rra_krb5_root}/lib"])]) +[AS_IF([test x"$rra_krb5_libdir" != x], + [KRB5_LDFLAGS="-L$rra_krb5_libdir"], + [AS_IF([test x"$rra_krb5_root" != x], + [RRA_SET_LDFLAGS([KRB5_LDFLAGS], [$rra_krb5_root])])]) + AS_IF([test x"$rra_krb5_includedir" != x], + [KRB5_CPPFLAGS="-I$rra_krb5_includedir"], + [AS_IF([test x"$rra_krb5_root" != x], + [AS_IF([test x"$rra_krb5_root" != x/usr], + [KRB5_CPPFLAGS="-I${rra_krb5_root}/include"])])])]) dnl Does the appropriate library checks for reduced-dependency Kerberos v5 dnl linkage. The single argument, if true, says to fail if Kerberos could not @@ -70,15 +73,16 @@ AC_DEFUN([_RRA_LIB_KRB5_REDUCED], LIBS="$KRB5_LIBS $LIBS" AC_CHECK_FUNCS([krb5_get_error_message], [AC_CHECK_FUNCS([krb5_free_error_message])], - [AC_CHECK_FUNCS([krb5_get_err_txt], , - [AC_CHECK_LIB([ksvc], [krb5_svc_get_msg], - [KRB5_LIBS="$KRB5_LIBS -lksvc" - AC_DEFINE([HAVE_KRB5_SVC_GET_MSG], [1]) - AC_CHECK_HEADERS([ibm_svc/krb5_svc.h])], - [AC_CHECK_LIB([com_err], [com_err], - [KRB5_LIBS="$KRB5_LIBS -lcom_err"], - [AC_MSG_ERROR([cannot find usable com_err library])]) - AC_CHECK_HEADERS([et/com_err.h])])])]) + [AC_CHECK_FUNCS([krb5_get_error_string], , + [AC_CHECK_FUNCS([krb5_get_err_txt], , + [AC_CHECK_LIB([ksvc], [krb5_svc_get_msg], + [KRB5_LIBS="$KRB5_LIBS -lksvc" + AC_DEFINE([HAVE_KRB5_SVC_GET_MSG], [1]) + AC_CHECK_HEADERS([ibm_svc/krb5_svc.h])], + [AC_CHECK_LIB([com_err], [com_err], + [KRB5_LIBS="$KRB5_LIBS -lcom_err"], + [AC_MSG_ERROR([cannot find usable com_err library])]) + AC_CHECK_HEADERS([et/com_err.h])])])])]) RRA_LIB_KRB5_RESTORE]) dnl Does the appropriate library checks for Kerberos v5 linkage when we don't @@ -125,24 +129,26 @@ AC_DEFUN([_RRA_LIB_KRB5_MANUAL], LIBS="$KRB5_LIBS $LIBS" AC_CHECK_FUNCS([krb5_get_error_message], [AC_CHECK_FUNCS([krb5_free_error_message])], - [AC_CHECK_FUNCS([krb5_get_err_txt], , - [AC_CHECK_FUNCS([krb5_svc_get_msg], - [AC_CHECK_HEADERS([ibm_svc/krb5_svc.h])], - [AC_CHECK_HEADERS([et/com_err.h])])])]) + [AC_CHECK_FUNCS([krb5_get_error_string], , + [AC_CHECK_FUNCS([krb5_get_err_txt], , + [AC_CHECK_FUNCS([krb5_svc_get_msg], + [AC_CHECK_HEADERS([ibm_svc/krb5_svc.h])], + [AC_CHECK_HEADERS([et/com_err.h])])])])]) RRA_LIB_KRB5_RESTORE]) dnl Sanity-check the results of krb5-config and be sure we can really link a -dnl Kerberos program. The first option says whether to fail if Kerberos was -dnl not found. If we shouldn't fail, clear KRB5_CPPFLAGS and KRB5_LIBS so -dnl that we know we don't have usable flags. +dnl Kerberos program. If that fails, clear KRB5_CPPFLAGS and KRB5_LIBS so +dnl that we know we don't have usable flags and fall back on the manual +dnl check. AC_DEFUN([_RRA_LIB_KRB5_CHECK], [RRA_LIB_KRB5_SWITCH - AC_CHECK_FUNC([krb5_init_context], , - [AS_IF([test x"$1" = xtrue], - [AC_MSG_FAILURE([krb5-config results fail for Kerberos v5])]) + AC_CHECK_FUNC([krb5_init_context], + [RRA_LIB_KRB5_RESTORE], + [RRA_LIB_KRB5_RESTORE KRB5_CPPFLAGS= - KRB5_LIBS=]) - RRA_LIB_KRB5_RESTORE]) + KRB5_LIBS= + _RRA_LIB_KRB5_PATHS + _RRA_LIB_KRB5_MANUAL([$1])])]) dnl The core of the library checking, shared between RRA_LIB_KRB5 and dnl RRA_LIB_KRB5_OPTIONAL. The single argument, if "true", says to fail if @@ -160,23 +166,24 @@ AC_DEFUN([_RRA_LIB_KRB5_INTERNAL], AS_IF([test x"$KRB5_CONFIG" != x && test -x "$KRB5_CONFIG"], [AC_CACHE_CHECK([for krb5 support in krb5-config], [rra_cv_lib_krb5_config], - [AS_IF(["$KRB5_CONFIG" | grep krb5 > /dev/null 2>&1], + [AS_IF(["$KRB5_CONFIG" 2>&1 | grep krb5 >/dev/null 2>&1], [rra_cv_lib_krb5_config=yes], [rra_cv_lib_krb5_config=no])]) - AS_IF([test "$rra_cv_lib_krb5_config" = yes], - [KRB5_CPPFLAGS=`"$KRB5_CONFIG" --cflags krb5` - KRB5_LIBS=`"$KRB5_CONFIG" --libs krb5`], - [KRB5_CPPFLAGS=`"$KRB5_CONFIG" --cflags` - KRB5_LIBS=`"$KRB5_CONFIG" --libs`]) + AS_IF([test x"$rra_cv_lib_krb5_config" = xyes], + [KRB5_CPPFLAGS=`"$KRB5_CONFIG" --cflags krb5 2>/dev/null` + KRB5_LIBS=`"$KRB5_CONFIG" --libs krb5 2>/dev/null`], + [KRB5_CPPFLAGS=`"$KRB5_CONFIG" --cflags 2>/dev/null` + KRB5_LIBS=`"$KRB5_CONFIG" --libs 2>/dev/null`]) KRB5_CPPFLAGS=`echo "$KRB5_CPPFLAGS" | sed 's%-I/usr/include ?%%'` _RRA_LIB_KRB5_CHECK([$1]) RRA_LIB_KRB5_SWITCH AC_CHECK_FUNCS([krb5_get_error_message], [AC_CHECK_FUNCS([krb5_free_error_message])], - [AC_CHECK_FUNCS([krb5_get_err_txt], , - [AC_CHECK_FUNCS([krb5_svc_get_msg], - [AC_CHECK_HEADERS([ibm_svc/krb5_svc.h])], - [AC_CHECK_HEADERS([et/com_err.h])])])]) + [AC_CHECK_FUNCS([krb5_get_error_string], , + [AC_CHECK_FUNCS([krb5_get_err_txt], , + [AC_CHECK_FUNCS([krb5_svc_get_msg], + [AC_CHECK_HEADERS([ibm_svc/krb5_svc.h])], + [AC_CHECK_HEADERS([et/com_err.h])])])])]) RRA_LIB_KRB5_RESTORE], [_RRA_LIB_KRB5_PATHS _RRA_LIB_KRB5_MANUAL([$1])])]) @@ -191,22 +198,37 @@ AC_DEFUN([_RRA_LIB_KRB5_INTERNAL], dnl The main macro for packages with mandatory Kerberos support. AC_DEFUN([RRA_LIB_KRB5], [rra_krb5_root= + rra_krb5_libdir= + rra_krb5_includedir= KRB5_CPPFLAGS= KRB5_LDFLAGS= KRB5_LIBS= AC_SUBST([KRB5_CPPFLAGS]) AC_SUBST([KRB5_LDFLAGS]) AC_SUBST([KRB5_LIBS]) + AC_ARG_WITH([krb5], - [AC_HELP_STRING([--with-krb5=DIR], + [AS_HELP_STRING([--with-krb5=DIR], [Location of Kerberos v5 headers and libraries])], [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], [rra_krb5_root="$withval"])]) + AC_ARG_WITH([krb5-include], + [AS_HELP_STRING([--with-krb5-include=DIR], + [Location of Kerberos v5 headers])], + [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], + [rra_krb5_includedir="$withval"])]) + AC_ARG_WITH([krb5-lib], + [AS_HELP_STRING([--with-krb5-lib=DIR], + [Location of Kerberos v5 libraries])], + [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], + [rra_krb5_libdir="$withval"])]) _RRA_LIB_KRB5_INTERNAL([true])]) dnl The main macro for packages with optional Kerberos support. AC_DEFUN([RRA_LIB_KRB5_OPTIONAL], [rra_krb5_root= + rra_krb5_libdir= + rra_krb5_includedir= rra_use_kerberos= KRB5_CPPFLAGS= KRB5_LDFLAGS= @@ -214,13 +236,25 @@ AC_DEFUN([RRA_LIB_KRB5_OPTIONAL], AC_SUBST([KRB5_CPPFLAGS]) AC_SUBST([KRB5_LDFLAGS]) AC_SUBST([KRB5_LIBS]) + AC_ARG_WITH([krb5], - [AC_HELP_STRING([--with-krb5@<:@=DIR@:>@], + [AS_HELP_STRING([--with-krb5@<:@=DIR@:>@], [Location of Kerberos v5 headers and libraries])], [AS_IF([test x"$withval" = xno], [rra_use_kerberos=false], [AS_IF([test x"$withval" != xyes], [rra_krb5_root="$withval"]) rra_use_kerberos=true])]) + AC_ARG_WITH([krb5-include], + [AS_HELP_STRING([--with-krb5-include=DIR], + [Location of Kerberos v5 headers])], + [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], + [rra_krb5_includedir="$withval"])]) + AC_ARG_WITH([krb5-lib], + [AS_HELP_STRING([--with-krb5-lib=DIR], + [Location of Kerberos v5 libraries])], + [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], + [rra_krb5_libdir="$withval"])]) + AS_IF([test x"$rra_use_kerberos" != xfalse], [AS_IF([test x"$rra_use_kerberos" = xtrue], [_RRA_LIB_KRB5_INTERNAL([true])], diff --git a/m4/lib-depends.m4 b/m4/lib-depends.m4 index 1d7e769..039e245 100644 --- a/m4/lib-depends.m4 +++ b/m4/lib-depends.m4 @@ -1,4 +1,4 @@ -dnl lib-depends.m4 -- Provides option to change library probes. +dnl Provides option to change library probes. dnl dnl This file provides RRA_ENABLE_REDUCED_DEPENDS, which adds the configure dnl option --enable-reduced-depends to request that library probes assume @@ -18,6 +18,6 @@ dnl See LICENSE for licensing terms. AC_DEFUN([RRA_ENABLE_REDUCED_DEPENDS], [rra_reduced_depends=false AC_ARG_ENABLE([reduced-depends], - [AC_HELP_STRING([--enable-reduced-depends], + [AS_HELP_STRING([--enable-reduced-depends], [Try to minimize shared library dependencies])], [AS_IF([test x"$enableval" = xyes], [rra_reduced_depends=true])])]) diff --git a/m4/lib-pathname.m4 b/m4/lib-pathname.m4 new file mode 100644 index 0000000..fc326a0 --- /dev/null +++ b/m4/lib-pathname.m4 @@ -0,0 +1,55 @@ +dnl Determine the library path name. +dnl +dnl Red Hat systems and some other Linux systems use lib64 and lib32 rather +dnl than just lib in some circumstances. This file provides an Autoconf +dnl macro, RRA_SET_LDFLAGS, which given a variable, a prefix, and an optional +dnl suffix, adds -Lprefix/lib, -Lprefix/lib32, or -Lprefix/lib64 to the +dnl variable depending on which directories exist and the size of a long in +dnl the compilation environment. If a suffix is given, a slash and that +dnl suffix will be appended, to allow for adding a subdirectory of the library +dnl directory. +dnl +dnl This file also provides the Autoconf macro RRA_SET_LIBDIR, which sets the +dnl libdir variable to PREFIX/lib{,32,64} as appropriate. +dnl +dnl Written by Russ Allbery +dnl Copyright 2008, 2009 Board of Trustees, Leland Stanford Jr. University +dnl +dnl See LICENSE for licensing terms. + +dnl Probe for the alternate library name that we should attempt on this +dnl architecture, given the size of an int, and set rra_lib_arch_name to that +dnl name. Separated out so that it can be AC_REQUIRE'd and not run multiple +dnl times. +dnl +dnl There is an unfortunate abstraction violation here where we assume we know +dnl the cache variable name used by Autoconf. Unfortunately, Autoconf doesn't +dnl provide any other way of getting at that information in shell that I can +dnl see. +AC_DEFUN([_RRA_LIB_ARCH_NAME], +[rra_lib_arch_name=lib + AC_CHECK_SIZEOF([long]) + AS_IF([test "$ac_cv_sizeof_long" -eq 4 && test -d /usr/lib32], + [rra_lib_arch_name=lib32], + [AS_IF([test "$ac_cv_sizeof_long" -eq 8 && test -d /usr/lib64], + [rra_lib_arch_name=lib64])])]) + +dnl Set VARIABLE to -LPREFIX/lib{,32,64} or -LPREFIX/lib{,32,64}/SUFFIX as +dnl appropriate. +AC_DEFUN([RRA_SET_LDFLAGS], +[AC_REQUIRE([_RRA_LIB_ARCH_NAME]) + AS_IF([test -d "$2/$rra_lib_arch_name"], + [AS_IF([test x"$3" = x], + [$1="[$]$1 -L$2/${rra_lib_arch_name}"], + [$1="[$]$1 -L$2/${rra_lib_arch_name}/$3"])], + [AS_IF([test x"$3" = x], + [$1="[$]$1 -L$2/lib"], + [$1="[$]$1 -L$2/lib/$3"])]) + $1=`echo "[$]$1" | sed -e 's/^ *//'`]) + +dnl Set libdir to PREFIX/lib{,32,64} as appropriate. +AC_DEFUN([RRA_SET_LIBDIR], +[AC_REQUIRE([_RRA_LIB_ARCH_NAME]) + AS_IF([test -d "$1/$rra_lib_arch_name"], + [libdir="$1/${rra_lib_arch_name}"], + [libdir="$1/lib"])]) diff --git a/m4/remctl.m4 b/m4/remctl.m4 index 5705a26..8ee3c16 100644 --- a/m4/remctl.m4 +++ b/m4/remctl.m4 @@ -1,27 +1,25 @@ -dnl remctl.m4 -- Find the compiler and linker flags for remctl. +dnl Find the compiler and linker flags for remctl. dnl -dnl This file provides RRA_LIB_REMCTL, which finds the compiler and linker -dnl flags for linking with remctl libraries and sets the substitution -dnl variables REMCTL_CPPFLAGS, REMCTL_LDFLAGS, and REMCTL_LIBS. Also provides -dnl RRA_LIB_REMCTL_SET to set CPPFLAGS, LDFLAGS, and LIBS to include the -dnl remctl libraries; RRA_LIB_REMCTL_SWITCH to do the same but save the -dnl current values first; and RRA_LIB_REMCTL_RESTORE to restore those settings -dnl to before the last RRA_LIB_REMCTL_SWITCH. +dnl Finds the compiler and linker flags for linking with remctl libraries. +dnl Provides the --with-remctl, --with-remctl-include, and --with-remctl-lib +dnl configure options to specify non-standard paths to the remctl headers and +dnl libraries. dnl -dnl This macro depends on RRA_ENABLE_REDUCED_DEPENDS and RRA_LIB_GSSAPI. +dnl Provides the macro RRA_LIB_REMCTL and sets the substitution variables +dnl REMCTL_CPPFLAGS, REMCTL_LDFLAGS, and REMCTL_LIBS. Also provides +dnl RRA_LIB_REMCTL_SWITCH to set CPPFLAGS, LDFLAGS, and LIBS to include the +dnl remctl libraries, saving the current values first, and +dnl RRA_LIB_REMCTL_RESTORE to restore those settings to before the last +dnl RRA_LIB_REMCTL_SWITCH. +dnl +dnl Depends on RRA_ENABLE_REDUCED_DEPENDS, RRA_SET_LDFLAGS, and +dnl RRA_LIB_GSSAPI. dnl dnl Written by Russ Allbery -dnl Copyright 2008 Board of Trustees, Leland Stanford Jr. University +dnl Copyright 2008, 2009 Board of Trustees, Leland Stanford Jr. University dnl dnl See LICENSE for licensing terms. -dnl Set CPPFLAGS, LDFLAGS, and LIBS to values including the Kerberos v5 -dnl settings. -AC_DEFUN([RRA_LIB_REMCTL_SET], -[CPPFLAGS="$REMCTL_CPPFLAGS $CPPFLAGS" - LDFLAGS="$REMCTL_LDFLAGS $LDFLAGS" - LIBS="$REMCTL_LIBS $LIBS"]) - dnl Save the current CPPFLAGS, LDFLAGS, and LIBS settings and switch to dnl versions that include the Kerberos v5 flags. Used as a wrapper, with dnl RRA_LIB_REMCTL_RESTORE, around tests. @@ -29,7 +27,9 @@ AC_DEFUN([RRA_LIB_REMCTL_SWITCH], [rra_remctl_save_CPPFLAGS="$CPPFLAGS" rra_remctl_save_LDFLAGS="$LDFLAGS" rra_remctl_save_LIBS="$LIBS" - RRA_LIB_REMCTL_SET]) + CPPFLAGS="$REMCTL_CPPFLAGS $CPPFLAGS" + LDFLAGS="$REMCTL_LDFLAGS $LDFLAGS" + LIBS="$REMCTL_LIBS $LIBS"]) dnl Restore CPPFLAGS, LDFLAGS, and LIBS to their previous values (before dnl RRA_LIB_REMCTL_SWITCH was called). @@ -38,32 +38,61 @@ AC_DEFUN([RRA_LIB_REMCTL_RESTORE], LDFLAGS="$rra_remctl_save_LDFLAGS" LIBS="$rra_remctl_save_LIBS"]) -dnl Set REMCTL_CPPFLAGS and REMCTL_LDFLAGS based on rra_remctl_root. +dnl Set REMCTL_CPPFLAGS and REMCTL_LDFLAGS based on rra_remctl_root, +dnl rra_remctl_libdir, and rra_remctl_includedir. AC_DEFUN([_RRA_LIB_REMCTL_PATHS], -[AS_IF([test x"$rra_remctl_root" != x], - [AS_IF([test x"$rra_remctl_root" != x/usr], - [REMCTL_CPPFLAGS="-I${rra_remctl_root}/include"]) - REMCTL_LDFLAGS="-L${rra_remctl_root}/lib"])]) +[AS_IF([test x"$rra_remctl_libdir" != x], + [REMCTL_LDFLAGS="-L$rra_remctl_libdir"], + [AS_IF([test x"$rra_remctl_root" != x], + [RRA_SET_LDFLAGS([REMCTL_LDFLAGS], [$rra_remctl_root])])]) + AS_IF([test x"$rra_remctl_includedir" != x], + [REMCTL_CPPFLAGS="-I$rra_remctl_includedir"], + [AS_IF([test x"$rra_remctl_root" != x], + [AS_IF([test x"$rra_remctl_root" != x/usr], + [REMCTL_CPPFLAGS="-I${rra_remctl_root}/include"])])])]) + +dnl Sanity-check the results of the remctl library search to be sure we can +dnl really link a remctl program. +AC_DEFUN([_RRA_LIB_REMCTL_CHECK], +[RRA_LIB_REMCTL_SWITCH + AC_CHECK_FUNC([remctl_open], , + [AC_MSG_FAILURE([unable to link with remctl library])]) + RRA_LIB_REMCTL_RESTORE]) dnl The main macro. AC_DEFUN([RRA_LIB_REMCTL], [AC_REQUIRE([RRA_ENABLE_REDUCED_DEPENDS]) rra_remctl_root= + rra_remctl_libdir= + rra_remctl_includedir= REMCTL_CPPFLAGS= REMCTL_LDFLAGS= REMCTL_LIBS= AC_SUBST([REMCTL_CPPFLAGS]) AC_SUBST([REMCTL_LDFLAGS]) AC_SUBST([REMCTL_LIBS]) + AC_ARG_WITH([remctl], - [AC_HELP_STRING([--with-remctl=DIR], + [AS_HELP_STRING([--with-remctl=DIR], [Location of remctl headers and libraries])], [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], [rra_remctl_root="$withval"])]) + AC_ARG_WITH([remctl-include], + [AS_HELP_STRING([--with-remctl-include=DIR], + [Location of remctl headers])], + [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], + [rra_remctl_includedir="$withval"])]) + AC_ARG_WITH([remctl-lib], + [AS_HELP_STRING([--with-remctl-lib=DIR], + [Location of remctl libraries])], + [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], + [rra_remctl_libdir="$withval"])]) + _RRA_LIB_REMCTL_PATHS AS_IF([test x"$rra_reduced_depends" = xtrue], [REMCTL_LIBS="-lremctl"], [RRA_LIB_GSSAPI REMCTL_CPPFLAGS="$REMCTL_CPPFLAGS $GSSAPI_CPPFLAGS" REMCTL_LDFLAGS="$REMCTL_LDFLAGS $GSSAPI_LDFLAGS" - REMCTL_LIBS="-lremctl $GSSAPI_LIBS"])]) + REMCTL_LIBS="-lremctl $GSSAPI_LIBS"]) + _RRA_LIB_REMCTL_CHECK]) diff --git a/m4/snprintf.m4 b/m4/snprintf.m4 index 79c0089..d933f55 100644 --- a/m4/snprintf.m4 +++ b/m4/snprintf.m4 @@ -1,4 +1,4 @@ -dnl snprintf.m4 -- Test for a working C99 snprintf. +dnl Test for a working C99 snprintf. dnl dnl Check for a working snprintf. Some systems have an snprintf that doesn't dnl nul-terminate if the buffer isn't large enough. Others return -1 if the @@ -10,12 +10,14 @@ dnl Provides RRA_FUNC_SNPRINTF, which adds snprintf.o to LIBOBJS unless a dnl fully working snprintf is found. dnl dnl Written by Russ Allbery -dnl Copyright 2006, 2008 Board of Trustees, Leland Stanford Jr. University +dnl Copyright 2006, 2008, 2009 +dnl Board of Trustees, Leland Stanford Jr. University +dnl dnl See LICENSE for licensing terms. dnl Source used by RRA_FUNC_SNPRINTF. -define([_RRA_FUNC_SNPRINTF_SOURCE], -[[#include +AC_DEFUN([_RRA_FUNC_SNPRINTF_SOURCE], [[ +#include #include char buf[2]; @@ -37,16 +39,17 @@ main() { return ((test("%s", "abcd") == 4 && buf[0] == 'a' && buf[1] == '\0' && snprintf(NULL, 0, "%s", "abcd") == 4) ? 0 : 1); -}]]) +} +]]) dnl The user-callable test. AC_DEFUN([RRA_FUNC_SNPRINTF], [AC_CACHE_CHECK([for working snprintf], [rra_cv_func_snprintf_works], - [AC_TRY_RUN(_RRA_FUNC_SNPRINTF_SOURCE(), + [AC_RUN_IFELSE([AC_LANG_SOURCE([_RRA_FUNC_SNPRINTF_SOURCE])], [rra_cv_func_snprintf_works=yes], [rra_cv_func_snprintf_works=no], [rra_cv_func_snprintf_works=no])]) -AS_IF([test "$rra_cv_func_snprintf_works" = yes], + AS_IF([test x"$rra_cv_func_snprintf_works" = xyes], [AC_DEFINE([HAVE_SNPRINTF], 1, [Define if your system has a working snprintf function.])], [AC_LIBOBJ([snprintf])])]) diff --git a/m4/vamacros.m4 b/m4/vamacros.m4 index 6740d77..855bb40 100644 --- a/m4/vamacros.m4 +++ b/m4/vamacros.m4 @@ -1,4 +1,4 @@ -dnl vamacros.m4 -- Check for support for variadic macros. +dnl Check for support for variadic macros. dnl dnl This file defines two macros for probing for compiler support for variadic dnl macros. Provided are RRA_C_C99_VAMACROS, which checks for support for the @@ -14,30 +14,49 @@ dnl dnl They set HAVE_C99_VAMACROS or HAVE_GNU_VAMACROS as appropriate. dnl dnl Written by Russ Allbery -dnl Copyright 2006, 2008 Board of Trustees, Leland Stanford Jr. University +dnl Copyright 2006, 2008, 2009 +dnl Board of Trustees, Leland Stanford Jr. University dnl dnl See LICENSE for licensing terms. +AC_DEFUN([_RRA_C_C99_VAMACROS_SOURCE], [[ +#include +#define error(...) fprintf(stderr, __VA_ARGS__) + +int +main(void) { + error("foo"); + error("foo %d", 0); + return 0; +} +]]) + AC_DEFUN([RRA_C_C99_VAMACROS], [AC_CACHE_CHECK([for C99 variadic macros], [rra_cv_c_c99_vamacros], -[AC_TRY_COMPILE( -[#include -#define error(...) fprintf(stderr, __VA_ARGS__)], - [error("foo"); error("foo %d", 0); return 0;], - [rra_cv_c_c99_vamacros=yes], - [rra_cv_c_c99_vamacros=no])]) -AS_IF([test $rra_cv_c_c99_vamacros = yes], + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_RRA_C_C99_VAMACROS_SOURCE])], + [rra_cv_c_c99_vamacros=yes], + [rra_cv_c_c99_vamacros=no])]) + AS_IF([test x"$rra_cv_c_c99_vamacros" = xyes], [AC_DEFINE([HAVE_C99_VAMACROS], 1, - [Define if the compiler supports C99 variadic macros.])])]) + [Define if the compiler supports C99 variadic macros.])])]) + +AC_DEFUN([_RRA_C_GNU_VAMACROS_SOURCE], [[ +#include +#define error(args...) fprintf(stderr, args) + +int +main(void) { + error("foo"); + error("foo %d", 0); + return 0; +} +]]) AC_DEFUN([RRA_C_GNU_VAMACROS], [AC_CACHE_CHECK([for GNU-style variadic macros], [rra_cv_c_gnu_vamacros], -[AC_TRY_COMPILE( -[#include -#define error(args...) fprintf(stderr, args)], - [error("foo"); error("foo %d", 0); return 0;], - [rra_cv_c_gnu_vamacros=yes], - [rra_cv_c_gnu_vamacros=no])]) -AS_IF([test $rra_cv_c_gnu_vamacros = yes], + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_RRA_C_GNU_VAMACROS_SOURCE])], + [rra_cv_c_gnu_vamacros=yes], + [rra_cv_c_gnu_vamacros=no])]) + AS_IF([test x"$rra_cv_c_gnu_vamacros" = xyes], [AC_DEFINE([HAVE_GNU_VAMACROS], 1, - [Define if the compiler supports GNU-style variadic macros.])])]) + [Define if the compiler supports GNU-style variadic macros.])])]) diff --git a/portable/snprintf.c b/portable/snprintf.c index 3775b8a..ab3121c 100644 --- a/portable/snprintf.c +++ b/portable/snprintf.c @@ -109,13 +109,10 @@ #define VA_SHIFT(v,t) ; /* no-op for ANSI */ #define VA_END va_end(ap) -#ifdef HAVE_LONG_DOUBLE +/* Assume all compilers support long double, per Autoconf documentation. */ #define LDOUBLE long double -#else -#define LDOUBLE double -#endif -#ifdef HAVE_LONG_LONG +#ifdef HAVE_LONG_LONG_INT # define LLONG long long #else # define LLONG long -- cgit v1.2.3 From ccf1cd7efa90bdcbe834e0d4ca144289cca97fd7 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Tue, 9 Feb 2010 15:32:54 -0800 Subject: Update portability code to rra-c-util 3.0 Add replacements for mkstemp and setenv, since we now use them when obtaining credentials in the client. Fix the bool type with Sun Studio 12 on Solaris 10. --- Makefile.am | 5 +-- NEWS | 2 ++ configure.ac | 2 +- portable/asprintf.c | 3 +- portable/mkstemp.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++ portable/setenv.c | 61 +++++++++++++++++++++++++++++++++++++ portable/stdbool.h | 4 ++- portable/system.h | 38 ++++++++++++++++------- 8 files changed, 186 insertions(+), 16 deletions(-) create mode 100644 portable/mkstemp.c create mode 100644 portable/setenv.c (limited to 'configure.ac') diff --git a/Makefile.am b/Makefile.am index 439b4c1..57fb6eb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,7 +1,8 @@ -# Makefile.am -- Automake makefile for wallet. +# Automake makefile for wallet. # # Written by Russ Allbery -# Copyright 2006, 2007, 2008 Board of Trustees, Leland Stanford Jr. University +# Copyright 2006, 2007, 2008, 2010 +# Board of Trustees, Leland Stanford Jr. University # # See LICENSE for licensing terms. diff --git a/NEWS b/NEWS index 661771a..e7931dd 100644 --- a/NEWS +++ b/NEWS @@ -62,6 +62,8 @@ wallet 0.10 (unreleased) * Prefer KRB5_CONFIG over a path constructed from --with-*. * Update GSS-API probes for Solaris 10's native implementation. * Change AC_TRY_* to AC_*_IFELSE as recommended by Autoconf. + * Use AC_TYPE_LONG_LONG_INT instead of AC_CHECK_TYPES([long long]). + * Provide a proper bool type with Sun Studio 12 on Solaris 10. wallet 0.9 (2008-04-24) diff --git a/configure.ac b/configure.ac index 78fecea..1b91ff0 100644 --- a/configure.ac +++ b/configure.ac @@ -37,7 +37,7 @@ RRA_C_GNU_VAMACROS AC_TYPE_LONG_LONG_INT RRA_FUNC_SNPRINTF AC_CHECK_FUNCS([setrlimit]) -AC_REPLACE_FUNCS([asprintf strlcat strlcpy]) +AC_REPLACE_FUNCS([asprintf mkstemp setenv strlcat strlcpy]) AC_ARG_WITH([wallet-server], [AC_HELP_STRING([--with-wallet-server=HOST], [Default wallet server])], diff --git a/portable/asprintf.c b/portable/asprintf.c index 9451795..4219a19 100644 --- a/portable/asprintf.c +++ b/portable/asprintf.c @@ -18,7 +18,8 @@ #if TESTING # define asprintf test_asprintf # define vasprintf test_vasprintf -int test_asprintf(char **, const char *, ...); +int test_asprintf(char **, const char *, ...) + __attribute__((__format__(printf, 2, 3))); int test_vasprintf(char **, const char *, va_list); #endif diff --git a/portable/mkstemp.c b/portable/mkstemp.c new file mode 100644 index 0000000..dd2a485 --- /dev/null +++ b/portable/mkstemp.c @@ -0,0 +1,87 @@ +/* + * Replacement for a missing mkstemp. + * + * Provides the same functionality as the library function mkstemp for those + * systems that don't have it. + * + * Written by Russ Allbery + * This work is hereby placed in the public domain by its author. + */ + +#include +#include + +#include +#include +#include + +/* + * If we're running the test suite, rename mkstemp to avoid conflicts with the + * system version. #undef it first because some systems may define it to + * another name. + */ +#if TESTING +# undef mkstemp +# define mkstemp test_mkstemp +int test_mkstemp(char *); +#endif + +/* Pick the longest available integer type. */ +#if HAVE_LONG_LONG +typedef unsigned long long long_int_type; +#else +typedef unsigned long long_int_type; +#endif + +int +mkstemp(char *template) +{ + static const char letters[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + size_t length; + char *XXXXXX; + struct timeval tv; + long_int_type randnum, working; + int i, tries, fd; + + /* + * Make sure we have a valid template and initialize p to point at the + * beginning of the template portion of the string. + */ + length = strlen(template); + if (length < 6) { + errno = EINVAL; + return -1; + } + XXXXXX = template + length - 6; + if (strcmp(XXXXXX, "XXXXXX") != 0) { + errno = EINVAL; + return -1; + } + + /* Get some more-or-less random information. */ + gettimeofday(&tv, NULL); + randnum = ((long_int_type) tv.tv_usec << 16) ^ tv.tv_sec ^ getpid(); + + /* + * Now, try to find a working file name. We try no more than TMP_MAX file + * names. + */ + for (tries = 0; tries < TMP_MAX; tries++) { + for (working = randnum, i = 0; i < 6; i++) { + XXXXXX[i] = letters[working % 62]; + working /= 62; + } + fd = open(template, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd >= 0 || (errno != EEXIST && errno != EISDIR)) + return fd; + + /* + * This is a relatively random increment. Cut off the tail end of + * tv_usec since it's often predictable. + */ + randnum += (tv.tv_usec >> 10) & 0xfff; + } + errno = EEXIST; + return -1; +} diff --git a/portable/setenv.c b/portable/setenv.c new file mode 100644 index 0000000..d66ddcd --- /dev/null +++ b/portable/setenv.c @@ -0,0 +1,61 @@ +/* + * Replacement for a missing setenv. + * + * Provides the same functionality as the standard library routine setenv for + * those platforms that don't have it. + * + * Written by Russ Allbery + * This work is hereby placed in the public domain by its author. + */ + +#include +#include + +/* + * If we're running the test suite, rename setenv to avoid conflicts with + * the system version. + */ +#if TESTING +# define setenv test_setenv +int test_setenv(const char *, const char *, int); +#endif + +int +setenv(const char *name, const char *value, int overwrite) +{ + char *envstring; + size_t size; + + if (!overwrite && getenv(name) != NULL) + return 0; + + /* + * Allocate memory for the environment string. We intentionally don't use + * concat here, or the xmalloc family of allocation routines, since the + * intention is to provide a replacement for the standard library function + * which sets errno and returns in the event of a memory allocation + * failure. + */ + size = strlen(name) + 1 + strlen(value) + 1; + envstring = malloc(size); + if (envstring == NULL) + return -1; + + /* + * Build the environment string and add it to the environment using + * putenv. Systems without putenv lose, but XPG4 requires it. + */ + strlcpy(envstring, name, size); + strlcat(envstring, "=", size); + strlcat(envstring, value, size); + return putenv(envstring); + + /* + * Note that the memory allocated is not freed. This is intentional; many + * implementations of putenv assume that the string passed to putenv will + * never be freed and don't make a copy of it. Repeated use of this + * function will therefore leak memory, since most implementations of + * putenv also don't free strings removed from the environment (due to + * being overwritten). + */ +} diff --git a/portable/stdbool.h b/portable/stdbool.h index 01a2ff2..bfbf4c4 100644 --- a/portable/stdbool.h +++ b/portable/stdbool.h @@ -15,7 +15,9 @@ #if HAVE_STDBOOL_H # include #else -# if !HAVE__BOOL +# if HAVE__BOOL +# define bool _Bool +# else # ifdef __cplusplus typedef bool _Bool; # elif _WIN32 diff --git a/portable/system.h b/portable/system.h index b899d08..461601b 100644 --- a/portable/system.h +++ b/portable/system.h @@ -1,6 +1,9 @@ /* + * Standard system includes and portability adjustments. + * * Declarations of routines and variables in the C library. Including this - * file is the equivalent of including all of the following headers, portably: + * file is the equivalent of including all of the following headers, + * portably: * * #include * #include @@ -12,8 +15,8 @@ * #include * #include * - * Missing functions are provided via #define or prototyped if available. - * Also provides some standard #defines. + * Missing functions are provided via #define or prototyped if available from + * the portable helper library. Also provides some standard #defines. * * Written by Russ Allbery * This work is hereby placed in the public domain by its author. @@ -55,13 +58,17 @@ BEGIN_DECLS +/* Default to a hidden visibility for all portability functions. */ +#pragma GCC visibility push(hidden) + /* * Provide prototypes for functions not declared in system headers. Use the - * HAVE_DECL macros for those functions that may be prototyped but - * implemented incorrectly or implemented without a prototype. + * HAVE_DECL macros for those functions that may be prototyped but implemented + * incorrectly or implemented without a prototype. */ #if !HAVE_ASPRINTF -extern int asprintf(char **, const char *, ...); +extern int asprintf(char **, const char *, ...) + __attribute__((__format__(printf, 2, 3))); extern int vasprintf(char **, const char *, va_list); #endif #if !HAVE_DECL_SNPRINTF @@ -71,6 +78,12 @@ extern int snprintf(char *, size_t, const char *, ...) #if !HAVE_DECL_VSNPRINTF extern int vsnprintf(char *, size_t, const char *, va_list); #endif +#if !HAVE_MKSTEMP +extern int mkstemp(char *); +#endif +#if !HAVE_SETENV +extern int setenv(const char *, const char *, int); +#endif #if !HAVE_STRLCAT extern size_t strlcat(char *, const char *, size_t); #endif @@ -78,6 +91,9 @@ extern size_t strlcat(char *, const char *, size_t); extern size_t strlcpy(char *, const char *, size_t); #endif +/* Undo default visibility change. */ +#pragma GCC visibility pop + END_DECLS /* Windows provides snprintf under a different name. */ @@ -90,9 +106,9 @@ END_DECLS * been defined, all the rest almost certainly have. */ #ifndef STDIN_FILENO -# define STDIN_FILENO 0 -# define STDOUT_FILENO 1 -# define STDERR_FILENO 2 +# define STDIN_FILENO 0 +# define STDOUT_FILENO 1 +# define STDERR_FILENO 2 #endif /* @@ -101,9 +117,9 @@ END_DECLS */ #ifndef va_copy # ifdef __va_copy -# define va_copy(d, s) __va_copy((d), (s)) +# define va_copy(d, s) __va_copy((d), (s)) # else -# define va_copy(d, s) memcpy(&(d), &(s), sizeof(va_list)) +# define va_copy(d, s) memcpy(&(d), &(s), sizeof(va_list)) # endif #endif -- cgit v1.2.3 From d05f66dbff10b525d37f60ee01d5b9f94bf5192e Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Tue, 9 Feb 2010 16:00:04 -0800 Subject: Update util code and import Kerberos portability glue Use the Kerberos portability layer from rra-c-util 3.0 and avoid Kerberos API calls deprecated on Heimdal. Break util/util.h into separate header files and update all source files accordingly. The test suite is not yet updated. That will come in subsequent commits. --- Makefile.am | 11 ++-- NEWS | 3 + client/file.c | 6 +- client/internal.h | 4 +- client/keytab.c | 9 ++- client/krb5.c | 15 +++-- client/remctl.c | 5 +- client/srvtab.c | 10 +-- client/wallet.c | 8 ++- configure.ac | 4 +- portable/krb5-extra.c | 108 +++++++++++++++++++++++++++++++ portable/krb5.h | 74 ++++++++++++++++++++++ util/concat.c | 3 +- util/concat.h | 36 +++++++++++ util/macros.h | 17 +++++ util/messages-krb5.c | 74 +++------------------- util/messages-krb5.h | 39 ++++++++++++ util/messages.c | 29 +++------ util/messages.h | 96 ++++++++++++++++++++++++++++ util/util.h | 171 -------------------------------------------------- util/xmalloc.c | 24 ++----- util/xmalloc.h | 100 +++++++++++++++++++++++++++++ 22 files changed, 540 insertions(+), 306 deletions(-) create mode 100644 portable/krb5-extra.c create mode 100644 portable/krb5.h create mode 100644 util/concat.h create mode 100644 util/macros.h create mode 100644 util/messages-krb5.h create mode 100644 util/messages.h delete mode 100644 util/util.h create mode 100644 util/xmalloc.h (limited to 'configure.ac') diff --git a/Makefile.am b/Makefile.am index 57fb6eb..27a6e39 100644 --- a/Makefile.am +++ b/Makefile.am @@ -39,11 +39,14 @@ EXTRA_DIST = LICENSE autogen client/wallet.pod config/allow-extract \ docs/setup examples/stanford.conf $(PERL_FILES) $(TEST_FILES) noinst_LIBRARIES = portable/libportable.a util/libutil.a -portable_libportable_a_SOURCES = portable/dummy.c portable/macros.h \ - portable/stdbool.h portable/system.h +portable_libportable_a_SOURCES = portable/dummy.c portable/krb5-extra.c \ + portable/krb5.h portable/macros.h portable/stdbool.h \ + portable/system.h +portable_libportable_a_CPPFLAGS = $(KRB5_CPPFLAGS) portable_libportable_a_LIBADD = $(LIBOBJS) -util_libutil_a_SOURCES = util/concat.c util/messages.c util/messages-krb5.c \ - util/util.h util/xmalloc.c +util_libutil_a_SOURCES = util/concat.c util/concat.h util/macros.h \ + util/messages-krb5.c util/messages-krb5.h util/messages.c \ + util/messages.h util/xmalloc.c util/xmalloc.h util_libutil_a_CPPFLAGS = $(KRB5_CPPFLAGS) bin_PROGRAMS = client/wallet diff --git a/NEWS b/NEWS index e7931dd..1d3a5e3 100644 --- a/NEWS +++ b/NEWS @@ -52,6 +52,8 @@ wallet 0.10 (unreleased) Update to rra-c-util 3.0: + * Use Kerberos portability layer to support Heimdal. + * Avoid Kerberos API calls deprecated on Heimdal. * Sanity-check the results of krb5-config before proceeding. * Fall back on manual probing if krb5-config results don't work. * Add --with-krb5-include and --with-krb5-lib configure options. @@ -64,6 +66,7 @@ wallet 0.10 (unreleased) * Change AC_TRY_* to AC_*_IFELSE as recommended by Autoconf. * Use AC_TYPE_LONG_LONG_INT instead of AC_CHECK_TYPES([long long]). * Provide a proper bool type with Sun Studio 12 on Solaris 10. + * Break util/util.h into separate header files per module. wallet 0.9 (2008-04-24) diff --git a/client/file.c b/client/file.c index 670a30d..c9edf3a 100644 --- a/client/file.c +++ b/client/file.c @@ -2,7 +2,7 @@ * File handling for the wallet client. * * Written by Russ Allbery - * Copyright 2007, 2008 Board of Trustees, Leland Stanford Jr. University + * Copyright 2007, 2008, 2010 Board of Trustees, Leland Stanford Jr. University * * See LICENSE for licensing terms. */ @@ -15,7 +15,9 @@ #include #include -#include +#include +#include +#include /* * Given a filename, some data, and a length, write that data to the given diff --git a/client/internal.h b/client/internal.h index e48616a..7fe962b 100644 --- a/client/internal.h +++ b/client/internal.h @@ -2,7 +2,7 @@ * Internal support functions for the wallet client. * * Written by Russ Allbery - * Copyright 2007, 2008 Board of Trustees, Leland Stanford Jr. University + * Copyright 2007, 2008, 2010 Board of Trustees, Leland Stanford Jr. University * * See LICENSE for licensing terms. */ @@ -11,8 +11,8 @@ #define CLIENT_INTERNAL_H 1 #include +#include -#include #include /* Forward declarations to avoid unnecessary includes. */ diff --git a/client/keytab.c b/client/keytab.c index 393ce3c..5f2076f 100644 --- a/client/keytab.c +++ b/client/keytab.c @@ -8,12 +8,15 @@ */ #include +#include #include #include #include -#include +#include +#include +#include /* @@ -47,11 +50,7 @@ merge_keytab(krb5_context ctx, const char *newfile, const char *file) status = krb5_kt_add_entry(ctx, old, &entry); if (status != 0) die_krb5(ctx, status, "cannot write to keytab %s", file); -#ifdef HAVE_KRB5_KT_FREE_ENTRY krb5_kt_free_entry(ctx, &entry); -#else - krb5_free_keytab_entry_contents(ctx, &entry); -#endif } if (status != KRB5_KT_END) die_krb5(ctx, status, "error reading temporary keytab %s", newfile); diff --git a/client/krb5.c b/client/krb5.c index 3698dd3..38172ae 100644 --- a/client/krb5.c +++ b/client/krb5.c @@ -15,7 +15,8 @@ #include #include -#include +#include +#include /* @@ -29,7 +30,7 @@ kinit(krb5_context ctx, const char *principal) krb5_principal princ; krb5_ccache ccache; krb5_creds creds; - krb5_get_init_creds_opt opts; + krb5_get_init_creds_opt *opts; krb5_error_code status; char cache_name[] = "/tmp/krb5cc_wallet_XXXXXX"; int fd; @@ -38,17 +39,21 @@ kinit(krb5_context ctx, const char *principal) status = krb5_parse_name(ctx, principal, &princ); if (status != 0) die_krb5(ctx, status, "invalid Kerberos principal %s", principal); - krb5_get_init_creds_opt_init(&opts); + status = krb5_get_init_creds_opt_alloc(ctx, &opts); + if (status != 0) + die_krb5(ctx, status, "cannot allocate credential options"); + krb5_get_init_creds_opt_set_default_flags(ctx, "wallet", princ->realm, + opts); memset(&creds, 0, sizeof(creds)); status = krb5_get_init_creds_password(ctx, &creds, princ, NULL, - krb5_prompter_posix, NULL, 0, NULL, &opts); + krb5_prompter_posix, NULL, 0, NULL, opts); if (status != 0) die_krb5(ctx, status, "authentication failed"); /* Put the new credentials into a ticket cache. */ fd = mkstemp(cache_name); if (fd < 0) - sysdie("cannot create temporary ticket cache", cache_name); + sysdie("cannot create temporary ticket cache %s", cache_name); status = krb5_cc_resolve(ctx, cache_name, &ccache); if (status != 0) die_krb5(ctx, status, "cannot create cache %s", cache_name); diff --git a/client/remctl.c b/client/remctl.c index 8dfeb0a..a4ff097 100644 --- a/client/remctl.c +++ b/client/remctl.c @@ -2,7 +2,7 @@ * remctl interface for the wallet client. * * Written by Russ Allbery - * Copyright 2007 Board of Trustees, Leland Stanford Jr. University + * Copyright 2007, 2010 Board of Trustees, Leland Stanford Jr. University * * See LICENSE for licensing terms. */ @@ -13,7 +13,8 @@ #include #include -#include +#include +#include /* diff --git a/client/srvtab.c b/client/srvtab.c index 5b52955..b26e6fc 100644 --- a/client/srvtab.c +++ b/client/srvtab.c @@ -8,12 +8,12 @@ */ #include +#include #include -#include - #include -#include +#include +#include #ifndef KRB5_KRB4_COMPAT # define ANAME_SZ 40 @@ -87,11 +87,7 @@ write_srvtab(krb5_context ctx, const char *srvtab, const char *principal, memcpy(data + length, entry.key.contents, 8); #endif length += 8; -#ifdef HAVE_KRB5_KT_FREE_ENTRY krb5_kt_free_entry(ctx, &entry); -#else - krb5_free_keytab_entry_contents(ctx, &entry); -#endif /* Write out the srvtab file. */ write_file(srvtab, data, length); diff --git a/client/wallet.c b/client/wallet.c index 4225d45..ce0f4e7 100644 --- a/client/wallet.c +++ b/client/wallet.c @@ -2,21 +2,23 @@ * The client program for the wallet system. * * Written by Russ Allbery - * Copyright 2006, 2007, 2008 + * Copyright 2006, 2007, 2008, 2010 * Board of Trustees, Leland Stanford Jr. University * * See LICENSE for licensing terms. */ #include +#include #include #include -#include #include #include -#include +#include +#include +#include /* * Basic wallet behavior options set either on the command line or via diff --git a/configure.ac b/configure.ac index 1b91ff0..f66a682 100644 --- a/configure.ac +++ b/configure.ac @@ -25,7 +25,9 @@ AC_PROG_RANLIB RRA_LIB_REMCTL RRA_LIB_KRB5 RRA_LIB_KRB5_SWITCH -AC_CHECK_FUNCS([krb5_kt_free_entry]) +AC_CHECK_FUNCS([krb5_get_init_creds_opt_alloc \ + krb5_get_init_creds_opt_set_default_flags \ + krb5_kt_free_entry]) AC_CHECK_MEMBERS([krb5_keytab_entry.keyblock], , , [#include ]) RRA_LIB_KRB5_RESTORE diff --git a/portable/krb5-extra.c b/portable/krb5-extra.c new file mode 100644 index 0000000..09a717b --- /dev/null +++ b/portable/krb5-extra.c @@ -0,0 +1,108 @@ +/* + * Portability glue functions for Kerberos. + * + * This file provides definitions of the interfaces that portable/krb5.h + * ensures exist if the function wasn't available in the Kerberos libraries. + * Everything in this file will be protected by #ifndef. If the native + * Kerberos libraries are fully capable, this file will be skipped. + * + * Written by Russ Allbery + * This work is hereby placed in the public domain by its author. + */ + +#include +#include +#include + +#include + +/* Figure out what header files to include for error reporting. */ +#if !defined(HAVE_KRB5_GET_ERROR_MESSAGE) && !defined(HAVE_KRB5_GET_ERR_TEXT) +# if !defined(HAVE_KRB5_GET_ERROR_STRING) +# if defined(HAVE_IBM_SVC_KRB5_SVC_H) +# include +# elif defined(HAVE_ET_COM_ERR_H) +# include +# else +# include +# endif +# endif +#endif + +/* Used for unused parameters to silence gcc warnings. */ +#define UNUSED __attribute__((__unused__)) + +/* + * This string is returned for unknown error messages. We use a static + * variable so that we can be sure not to free it. + */ +static const char error_unknown[] = "unknown error"; + + +#ifndef HAVE_KRB5_GET_ERROR_MESSAGE +/* + * Given a Kerberos error code, return the corresponding error. Prefer the + * Kerberos interface if available since it will provide context-specific + * error information, whereas the error_message() call will only provide a + * fixed message. + */ +const char * +krb5_get_error_message(krb5_context ctx UNUSED, krb5_error_code code UNUSED) +{ + const char *msg = NULL; + +# if defined(HAVE_KRB5_GET_ERROR_STRING) + msg = krb5_get_error_string(ctx); +# elif defined(HAVE_KRB5_GET_ERR_TEXT) + msg = krb5_get_err_text(ctx, code); +# elif defined(HAVE_KRB5_SVC_GET_MSG) + krb5_svc_get_msg(code, (char **) &msg); +# else + msg = error_message(code); +# endif + if (msg == NULL) + return error_unknown; + else + return msg; +} +#endif /* !HAVE_KRB5_GET_ERROR_MESSAGE */ + + +#ifndef HAVE_KRB5_FREE_ERROR_MESSAGE +/* + * Free an error string if necessary. If we returned a static string, make + * sure we don't free it. + * + * This code assumes that the set of implementations that have + * krb5_free_error_message is a subset of those with krb5_get_error_message. + * If this assumption ever breaks, we may call the wrong free function. + */ +static void +krb5_free_error_message(krb5_context ctx UNUSED, const char *msg) +{ + if (msg == error_unknown) + return; +# if defined(HAVE_KRB5_GET_ERROR_STRING) + krb5_free_error_string(ctx, (char *) msg); +# elif defined(HAVE_KRB5_SVC_GET_MSG) + krb5_free_string(ctx, (char *) msg); +# endif +} +#endif /* !HAVE_KRB5_FREE_ERROR_MESSAGE */ + + +#ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC +/* + * Allocate and initialize a krb5_get_init_creds_opt struct. This code + * assumes that an all-zero bit pattern will create a NULL pointer. + */ +krb5_error_code +krb5_get_init_creds_opt_alloc(krb5_context ctx, krb5_get_init_creds_opt **opts) +{ + *opts = calloc(1, sizeof(krb5_get_init_creds_opt)); + if (*opts == NULL) + return errno; + krb5_get_init_creds_opt_init(*opts); + return 0; +} +#endif /* !HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC */ diff --git a/portable/krb5.h b/portable/krb5.h new file mode 100644 index 0000000..117f5ce --- /dev/null +++ b/portable/krb5.h @@ -0,0 +1,74 @@ +/* + * Portability wrapper around krb5.h. + * + * This header includes krb5.h and then adjusts for various portability + * issues, primarily between MIT Kerberos and Heimdal, so that code can be + * written to a consistent API. + * + * Unfortunately, due to the nature of the differences between MIT Kerberos + * and Heimdal, it's not possible to write code to either one of the APIs and + * adjust for the other one. In general, this header tries to make available + * the Heimdal API and fix it for MIT Kerberos, but there are places where MIT + * Kerberos requires a more specific call. For those cases, it provides the + * most specific interface. + * + * For example, MIT Kerberos has krb5_free_unparsed_name() whereas Heimdal + * prefers the generic krb5_xfree(). In this case, this header provides + * krb5_free_unparsed_name() for both APIs since it's the most specific call. + * + * Written by Russ Allbery + * This work is hereby placed in the public domain by its author. + */ + +#ifndef PORTABLE_KRB5_H +#define PORTABLE_KRB5_H 1 + +#include +#include + +#include + +BEGIN_DECLS + +/* Default to a hidden visibility for all portability functions. */ +#pragma GCC visibility push(hidden) + +/* + * krb5_{get,free}_error_message are the preferred APIs for both current MIT + * and current Heimdal, but there are tons of older APIs we may have to fall + * back on for earlier versions. + * + * This function should be called immediately after the corresponding error + * without any intervening Kerberos calls. Otherwise, the correct error + * message and supporting information may not be returned. + */ +#ifndef HAVE_KRB5_GET_ERROR_MESSAGE +const char *krb5_get_error_message(krb5_context, krb5_error_code); +#endif +#ifndef HAVE_KRB5_FREE_ERROR_MESSAGE +void krb5_free_error_message(krb5_context, const char *); +#endif + +/* + * Both current MIT and current Heimdal prefer _opt_alloc, but older versions + * of both require allocating your own struct and calling _opt_init. + */ +#ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC +krb5_error_code krb5_get_init_creds_opt_alloc(krb5_context, + krb5_get_init_creds_opt **); +#endif + +/* Heimdal-specific. */ +#ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_DEFAULT_FLAGS +#define krb5_get_init_creds_opt_set_default_flags(c, p, r, o) /* empty */ +#endif + +/* Heimdal: krb5_kt_free_entry, MIT: krb5_free_keytab_entry_contents. */ +#ifndef HAVE_KRB5_KT_FREE_ENTRY +# define krb5_kt_free_entry(c, e) krb5_free_keytab_entry_contents((c), (e)) +#endif + +/* Undo default visibility change. */ +#pragma GCC visibility pop + +#endif /* !PORTABLE_KRB5_H */ diff --git a/util/concat.c b/util/concat.c index bef67db..bdbd836 100644 --- a/util/concat.c +++ b/util/concat.c @@ -25,7 +25,8 @@ #include #include -#include +#include +#include /* Abbreviation for cleaner code. */ #define VA_NEXT(var, type) ((var) = (type) va_arg(args, type)) diff --git a/util/concat.h b/util/concat.h new file mode 100644 index 0000000..ef8b38d --- /dev/null +++ b/util/concat.h @@ -0,0 +1,36 @@ +/* + * Prototypes for string concatenation with dynamic memory allocation. + * + * Written by Russ Allbery + * This work is hereby placed in the public domain by its author. + */ + +#ifndef UTIL_CONCAT_H +#define UTIL_CONCAT_H 1 + +#include +#include + +BEGIN_DECLS + +/* Default to a hidden visibility for all util functions. */ +#pragma GCC visibility push(hidden) + +/* Concatenate NULL-terminated strings into a newly allocated string. */ +char *concat(const char *first, ...) + __attribute__((__malloc__, __nonnull__(1))); + +/* + * Given a base path and a file name, create a newly allocated path string. + * The name will be appended to base with a / between them. Exceptionally, if + * name begins with a slash, it will be strdup'd and returned as-is. + */ +char *concatpath(const char *base, const char *name) + __attribute__((__malloc__, __nonnull__(2))); + +/* Undo default visibility change. */ +#pragma GCC visibility pop + +END_DECLS + +#endif /* UTIL_CONCAT_H */ diff --git a/util/macros.h b/util/macros.h new file mode 100644 index 0000000..97b2c2b --- /dev/null +++ b/util/macros.h @@ -0,0 +1,17 @@ +/* + * Some standard helpful macros. + * + * Written by Russ Allbery + * This work is hereby placed in the public domain by its author. + */ + +#ifndef UTIL_MACROS_H +#define UTIL_MACROS_H 1 + +#include +#include + +/* Used for unused parameters to silence gcc warnings. */ +#define UNUSED __attribute__((__unused__)) + +#endif /* UTIL_MACROS_H */ diff --git a/util/messages-krb5.c b/util/messages-krb5.c index 00f4a2e..7f35d29 100644 --- a/util/messages-krb5.c +++ b/util/messages-krb5.c @@ -6,76 +6,20 @@ * formatted message. * * Written by Russ Allbery - * Copyright 2006, 2007, 2008 + * Copyright 2006, 2007, 2008, 2009, 2010 * Board of Trustees, Leland Stanford Jr. University * * See LICENSE for licensing terms. */ #include +#include #include -#include -#if !defined(HAVE_KRB5_GET_ERROR_MESSAGE) && !defined(HAVE_KRB5_GET_ERR_TEXT) -# if defined(HAVE_IBM_SVC_KRB5_SVC_H) -# include -# elif defined(HAVE_ET_COM_ERR_H) -# include -# else -# include -# endif -#endif - -#include - -/* - * This string is returned for unknown error messages. We use a static - * variable so that we can be sure not to free it. - */ -static const char error_unknown[] = "unknown error"; - - -/* - * Given a Kerberos error code, return the corresponding error. Prefer the - * Kerberos interface if available since it will provide context-specific - * error information, whereas the error_message() call will only provide a - * fixed message. - */ -static const char * -get_error(krb5_context ctx UNUSED, krb5_error_code code) -{ - const char *msg = NULL; - -#if defined(HAVE_KRB5_GET_ERROR_MESSAGE) - msg = krb5_get_error_message(ctx, code); -#elif defined(HAVE_KRB5_GET_ERR_TEXT) - msg = krb5_get_err_text(ctx, code); -#elif defined(HAVE_KRB5_SVC_GET_MSG) - krb5_svc_get_msg(code, &msg); -#else - msg = error_message(code); -#endif - if (msg == NULL) - return error_unknown; - else - return msg; -} - - -/* - * Free an error string if necessary. - */ -static void -free_error(krb5_context ctx UNUSED, const char *msg) -{ - if (msg == error_unknown) - return; -#if defined(HAVE_KRB5_FREE_ERROR_MESSAGE) - krb5_free_error_message(ctx, msg); -#elif defined(HAVE_KRB5_SVC_GET_MSG) - krb5_free_string((char *) msg); -#endif -} +#include +#include +#include +#include /* @@ -88,7 +32,7 @@ die_krb5(krb5_context ctx, krb5_error_code code, const char *format, ...) char *message; va_list args; - k5_msg = get_error(ctx, code); + k5_msg = krb5_get_error_message(ctx, code); va_start(args, format); if (xvasprintf(&message, format, args) < 0) die("internal error: unable to format error message"); @@ -107,12 +51,12 @@ warn_krb5(krb5_context ctx, krb5_error_code code, const char *format, ...) char *message; va_list args; - k5_msg = get_error(ctx, code); + k5_msg = krb5_get_error_message(ctx, code); va_start(args, format); if (xvasprintf(&message, format, args) < 0) die("internal error: unable to format error message"); va_end(args); warn("%s: %s", message, k5_msg); free(message); - free_error(ctx, k5_msg); + krb5_free_error_message(ctx, k5_msg); } diff --git a/util/messages-krb5.h b/util/messages-krb5.h new file mode 100644 index 0000000..3b763c8 --- /dev/null +++ b/util/messages-krb5.h @@ -0,0 +1,39 @@ +/* + * Prototypes for error handling for Kerberos. + * + * Written by Russ Allbery + * Copyright 2006, 2007, 2008, 2009, 2010 + * Board of Trustees, Leland Stanford Jr. University + * + * See LICENSE for licensing terms. + */ + +#ifndef UTIL_MESSAGES_KRB5_H +#define UTIL_MESSAGES_KRB5_H 1 + +#include +#include + +#include +#include + +BEGIN_DECLS + +/* Default to a hidden visibility for all util functions. */ +#pragma GCC visibility push(hidden) + +/* + * The Kerberos versions of the reporting functions. These take a context and + * an error code to get the Kerberos error. + */ +void die_krb5(krb5_context, krb5_error_code, const char *, ...) + __attribute__((__nonnull__, __noreturn__, __format__(printf, 3, 4))); +void warn_krb5(krb5_context, krb5_error_code, const char *, ...) + __attribute__((__nonnull__, __format__(printf, 3, 4))); + +/* Undo default visibility change. */ +#pragma GCC visibility pop + +END_DECLS + +#endif /* UTIL_MESSAGES_KRB5_H */ diff --git a/util/messages.c b/util/messages.c index 0a106f6..ef920b2 100644 --- a/util/messages.c +++ b/util/messages.c @@ -51,26 +51,13 @@ * va_list, and the applicable errno value (if any). * * Copyright 2008 Board of Trustees, Leland Stanford Jr. University - * Copyright 2004, 2005, 2006 + * Copyright (c) 2004, 2005, 2006 * by Internet Systems Consortium, Inc. ("ISC") - * Copyright 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, - * 2003 by The Internet Software Consortium and Rich Salz + * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002, 2003 by The Internet Software Consortium and Rich Salz * - * This code is derived from software contributed to the Internet Software - * Consortium by Rich Salz. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. -*/ + * See LICENSE for licensing terms. + */ #include #include @@ -90,7 +77,9 @@ # define LOG_CRIT EVENTLOG_ERROR_TYPE #endif -#include +#include +#include +#include /* The default handler lists. */ static message_handler_func stdout_handlers[2] = { @@ -211,7 +200,7 @@ message_log_syslog(int pri, int len, const char *fmt, va_list args, int err) eventlog = RegisterEventSource(NULL, message_program_name); if (eventlog != NULL) { - ReportEvent(eventlog, pri, 0, 0, NULL, 1, 0, &buffer, NULL); + ReportEvent(eventlog, (WORD) pri, 0, 0, NULL, 1, 0, &buffer, NULL); CloseEventLog(eventlog); } } diff --git a/util/messages.h b/util/messages.h new file mode 100644 index 0000000..ff86f39 --- /dev/null +++ b/util/messages.h @@ -0,0 +1,96 @@ +/* + * Prototypes for message and error reporting (possibly fatal). + * + * Copyright 2008, 2010 Board of Trustees, Leland Stanford Jr. University + * Copyright (c) 2004, 2005, 2006 + * by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002, 2003 by The Internet Software Consortium and Rich Salz + * + * See LICENSE for licensing terms. + */ + +#ifndef UTIL_MESSAGES_H +#define UTIL_MESSAGES_H 1 + +#include +#include + +#include + +BEGIN_DECLS + +/* Default to a hidden visibility for all util functions. */ +#pragma GCC visibility push(hidden) + +/* + * The reporting functions. The ones prefaced by "sys" add a colon, a space, + * and the results of strerror(errno) to the output and are intended for + * reporting failures of system calls. + */ +void debug(const char *, ...) + __attribute__((__nonnull__, __format__(printf, 1, 2))); +void notice(const char *, ...) + __attribute__((__nonnull__, __format__(printf, 1, 2))); +void sysnotice(const char *, ...) + __attribute__((__nonnull__, __format__(printf, 1, 2))); +void warn(const char *, ...) + __attribute__((__nonnull__, __format__(printf, 1, 2))); +void syswarn(const char *, ...) + __attribute__((__nonnull__, __format__(printf, 1, 2))); +void die(const char *, ...) + __attribute__((__nonnull__, __noreturn__, __format__(printf, 1, 2))); +void sysdie(const char *, ...) + __attribute__((__nonnull__, __noreturn__, __format__(printf, 1, 2))); + +/* + * Set the handlers for various message functions. All of these functions + * take a count of the number of handlers and then function pointers for each + * of those handlers. These functions are not thread-safe; they set global + * variables. + */ +void message_handlers_debug(int count, ...); +void message_handlers_notice(int count, ...); +void message_handlers_warn(int count, ...); +void message_handlers_die(int count, ...); + +/* + * Some useful handlers, intended to be passed to message_handlers_*. All + * handlers take the length of the formatted message, the format, a variadic + * argument list, and the errno setting if any. + */ +void message_log_stdout(int, const char *, va_list, int) + __attribute((__nonnull__)); +void message_log_stderr(int, const char *, va_list, int) + __attribute((__nonnull__)); +void message_log_syslog_debug(int, const char *, va_list, int) + __attribute((__nonnull__)); +void message_log_syslog_info(int, const char *, va_list, int) + __attribute((__nonnull__)); +void message_log_syslog_notice(int, const char *, va_list, int) + __attribute((__nonnull__)); +void message_log_syslog_warning(int, const char *, va_list, int) + __attribute((__nonnull__)); +void message_log_syslog_err(int, const char *, va_list, int) + __attribute((__nonnull__)); +void message_log_syslog_crit(int, const char *, va_list, int) + __attribute((__nonnull__)); + +/* The type of a message handler. */ +typedef void (*message_handler_func)(int, const char *, va_list, int); + +/* If non-NULL, called before exit and its return value passed to exit. */ +extern int (*message_fatal_cleanup)(void); + +/* + * If non-NULL, prepended (followed by ": ") to all messages printed by either + * message_log_stdout or message_log_stderr. + */ +extern const char *message_program_name; + +/* Undo default visibility change. */ +#pragma GCC visibility pop + +END_DECLS + +#endif /* UTIL_MESSAGES_H */ diff --git a/util/util.h b/util/util.h deleted file mode 100644 index 6ac7fa7..0000000 --- a/util/util.h +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Utility functions. - * - * This is a variety of utility functions that are used internally by pieces - * of remctl. Many of them came originally from INN. - * - * Written by Russ Allbery - * Copyright 2005, 2006, 2007, 2008 - * Board of Trustees, Leland Stanford Jr. University - * Copyright 2004, 2005, 2006, 2007 - * by Internet Systems Consortium, Inc. ("ISC") - * Copyright 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, - * 2003 by The Internet Software Consortium and Rich Salz - * - * See LICENSE for licensing terms. - */ - -#ifndef UTIL_UTIL_H -#define UTIL_UTIL_H 1 - -#include -#include - -#include -#include -#include - -/* Used for unused parameters to silence gcc warnings. */ -#define UNUSED __attribute__((__unused__)) - -BEGIN_DECLS - -/* Concatenate NULL-terminated strings into a newly allocated string. */ -extern char *concat(const char *first, ...); - -/* - * Given a base path and a file name, create a newly allocated path string. - * The name will be appended to base with a / between them. Exceptionally, if - * name begins with a slash, it will be strdup'd and returned as-is. - */ -extern char *concatpath(const char *base, const char *name); - -/* - * The reporting functions. The ones prefaced by "sys" add a colon, a space, - * and the results of strerror(errno) to the output and are intended for - * reporting failures of system calls. - */ -extern void debug(const char *, ...) - __attribute__((__format__(printf, 1, 2))); -extern void notice(const char *, ...) - __attribute__((__format__(printf, 1, 2))); -extern void sysnotice(const char *, ...) - __attribute__((__format__(printf, 1, 2))); -extern void warn(const char *, ...) - __attribute__((__format__(printf, 1, 2))); -extern void syswarn(const char *, ...) - __attribute__((__format__(printf, 1, 2))); -extern void die(const char *, ...) - __attribute__((__noreturn__, __format__(printf, 1, 2))); -extern void sysdie(const char *, ...) - __attribute__((__noreturn__, __format__(printf, 1, 2))); - -/* - * The Kerberos versions of the reporting functions. These take a context and - * an error code to get the Kerberos error. - */ -void die_krb5(krb5_context, krb5_error_code, const char *, ...) - __attribute__((__noreturn__, __format__(printf, 3, 4))); -void warn_krb5(krb5_context, krb5_error_code, const char *, ...) - __attribute__((__format__(printf, 3, 4))); - -/* - * Set the handlers for various message functions. All of these functions - * take a count of the number of handlers and then function pointers for each - * of those handlers. These functions are not thread-safe; they set global - * variables. - */ -extern void message_handlers_debug(int count, ...); -extern void message_handlers_notice(int count, ...); -extern void message_handlers_warn(int count, ...); -extern void message_handlers_die(int count, ...); - -/* - * Some useful handlers, intended to be passed to message_handlers_*. All - * handlers take the length of the formatted message, the format, a variadic - * argument list, and the errno setting if any. - */ -extern void message_log_stdout(int, const char *, va_list, int); -extern void message_log_stderr(int, const char *, va_list, int); -extern void message_log_syslog_debug(int, const char *, va_list, int); -extern void message_log_syslog_info(int, const char *, va_list, int); -extern void message_log_syslog_notice(int, const char *, va_list, int); -extern void message_log_syslog_warning(int, const char *, va_list, int); -extern void message_log_syslog_err(int, const char *, va_list, int); -extern void message_log_syslog_crit(int, const char *, va_list, int); - -/* The type of a message handler. */ -typedef void (*message_handler_func)(int, const char *, va_list, int); - -/* If non-NULL, called before exit and its return value passed to exit. */ -extern int (*message_fatal_cleanup)(void); - -/* - * If non-NULL, prepended (followed by ": ") to all messages printed by either - * message_log_stdout or message_log_stderr. - */ -extern const char *message_program_name; - -/* - * The functions are actually macros so that we can pick up the file and line - * number information for debugging error messages without the user having to - * pass those in every time. - */ -#define xcalloc(n, size) x_calloc((n), (size), __FILE__, __LINE__) -#define xmalloc(size) x_malloc((size), __FILE__, __LINE__) -#define xrealloc(p, size) x_realloc((p), (size), __FILE__, __LINE__) -#define xstrdup(p) x_strdup((p), __FILE__, __LINE__) -#define xstrndup(p, size) x_strndup((p), (size), __FILE__, __LINE__) -#define xvasprintf(p, f, a) x_vasprintf((p), (f), (a), __FILE__, __LINE__) - -/* - * asprintf is a special case since it takes variable arguments. If we have - * support for variadic macros, we can still pass in the file and line and - * just need to put them somewhere else in the argument list than last. - * Otherwise, just call x_asprintf directly. This means that the number of - * arguments x_asprintf takes must vary depending on whether variadic macros - * are supported. - */ -#ifdef HAVE_C99_VAMACROS -# define xasprintf(p, f, ...) \ - x_asprintf((p), __FILE__, __LINE__, (f), __VA_ARGS__) -#elif HAVE_GNU_VAMACROS -# define xasprintf(p, f, args...) \ - x_asprintf((p), __FILE__, __LINE__, (f), args) -#else -# define xasprintf x_asprintf -#endif - -/* - * Last two arguments are always file and line number. These are internal - * implementations that should not be called directly. - */ -extern void *x_calloc(size_t, size_t, const char *, int); -extern void *x_malloc(size_t, const char *, int); -extern void *x_realloc(void *, size_t, const char *, int); -extern char *x_strdup(const char *, const char *, int); -extern char *x_strndup(const char *, size_t, const char *, int); -extern int x_vasprintf(char **, const char *, va_list, const char *, int); - -/* asprintf special case. */ -#if HAVE_C99_VAMACROS || HAVE_GNU_VAMACROS -extern int x_asprintf(char **, const char *, int, const char *, ...); -#else -extern int x_asprintf(char **, const char *, ...); -#endif - -/* Failure handler takes the function, the size, the file, and the line. */ -typedef void (*xmalloc_handler_type)(const char *, size_t, const char *, int); - -/* The default error handler. */ -void xmalloc_fail(const char *, size_t, const char *, int); - -/* - * Assign to this variable to choose a handler other than the default, which - * just calls sysdie. - */ -extern xmalloc_handler_type xmalloc_error_handler; - -END_DECLS - -#endif /* UTIL_UTIL_H */ diff --git a/util/xmalloc.c b/util/xmalloc.c index 412890e..4e05f96 100644 --- a/util/xmalloc.c +++ b/util/xmalloc.c @@ -55,25 +55,12 @@ * header file defines macros named xmalloc, etc. that pass the file name and * line number to these functions. * - * Copyright 2004, 2005, 2006 + * Copyright (c) 2004, 2005, 2006 * by Internet Systems Consortium, Inc. ("ISC") - * Copyright 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, - * 2003 by The Internet Software Consortium and Rich Salz + * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002, 2003 by The Internet Software Consortium and Rich Salz * - * This code is derived from software contributed to the Internet Software - * Consortium by Rich Salz. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. + * See LICENSE for licensing terms. */ #include @@ -81,7 +68,8 @@ #include -#include +#include +#include /* diff --git a/util/xmalloc.h b/util/xmalloc.h new file mode 100644 index 0000000..657a6bb --- /dev/null +++ b/util/xmalloc.h @@ -0,0 +1,100 @@ +/* + * Prototypes for malloc routines with failure handling. + * + * Copyright 2010 Board of Trustees, Leland Stanford Jr. University + * Copyright (c) 2004, 2005, 2006 + * by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002, 2003 by The Internet Software Consortium and Rich Salz + * + * See LICENSE for licensing terms. + */ + +#ifndef UTIL_XMALLOC_H +#define UTIL_XMALLOC_H 1 + +#include +#include + +#include + +/* + * The functions are actually macros so that we can pick up the file and line + * number information for debugging error messages without the user having to + * pass those in every time. + */ +#define xcalloc(n, size) x_calloc((n), (size), __FILE__, __LINE__) +#define xmalloc(size) x_malloc((size), __FILE__, __LINE__) +#define xrealloc(p, size) x_realloc((p), (size), __FILE__, __LINE__) +#define xstrdup(p) x_strdup((p), __FILE__, __LINE__) +#define xstrndup(p, size) x_strndup((p), (size), __FILE__, __LINE__) +#define xvasprintf(p, f, a) x_vasprintf((p), (f), (a), __FILE__, __LINE__) + +/* + * asprintf is a special case since it takes variable arguments. If we have + * support for variadic macros, we can still pass in the file and line and + * just need to put them somewhere else in the argument list than last. + * Otherwise, just call x_asprintf directly. This means that the number of + * arguments x_asprintf takes must vary depending on whether variadic macros + * are supported. + */ +#ifdef HAVE_C99_VAMACROS +# define xasprintf(p, f, ...) \ + x_asprintf((p), __FILE__, __LINE__, (f), __VA_ARGS__) +#elif HAVE_GNU_VAMACROS +# define xasprintf(p, f, args...) \ + x_asprintf((p), __FILE__, __LINE__, (f), args) +#else +# define xasprintf x_asprintf +#endif + +BEGIN_DECLS + +/* Default to a hidden visibility for all util functions. */ +#pragma GCC visibility push(hidden) + +/* + * Last two arguments are always file and line number. These are internal + * implementations that should not be called directly. + */ +void *x_calloc(size_t, size_t, const char *, int) + __attribute__((__alloc_size__(1, 2), __malloc__, __nonnull__)); +void *x_malloc(size_t, const char *, int) + __attribute__((__alloc_size__(1), __malloc__, __nonnull__)); +void *x_realloc(void *, size_t, const char *, int) + __attribute__((__alloc_size__(2), __malloc__, __nonnull__(3))); +char *x_strdup(const char *, const char *, int) + __attribute__((__malloc__, __nonnull__)); +char *x_strndup(const char *, size_t, const char *, int) + __attribute__((__malloc__, __nonnull__)); +int x_vasprintf(char **, const char *, va_list, const char *, int) + __attribute__((__nonnull__)); + +/* asprintf special case. */ +#if HAVE_C99_VAMACROS || HAVE_GNU_VAMACROS +int x_asprintf(char **, const char *, int, const char *, ...) + __attribute__((__nonnull__, __format__(printf, 4, 5))); +#else +int x_asprintf(char **, const char *, ...) + __attribute__((__nonnull__, __format__(printf, 2, 3))); +#endif + +/* Failure handler takes the function, the size, the file, and the line. */ +typedef void (*xmalloc_handler_type)(const char *, size_t, const char *, int); + +/* The default error handler. */ +void xmalloc_fail(const char *, size_t, const char *, int) + __attribute__((__nonnull__)); + +/* + * Assign to this variable to choose a handler other than the default, which + * just calls sysdie. + */ +extern xmalloc_handler_type xmalloc_error_handler; + +/* Undo default visibility change. */ +#pragma GCC visibility pop + +END_DECLS + +#endif /* UTIL_XMALLOC_H */ -- cgit v1.2.3 From c02942ddc12408f0e5b9d828cddf240519d1fe93 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Tue, 9 Feb 2010 18:40:22 -0800 Subject: Update to C TAP Harness 1.1 and rra-c-util 3.0 tests * Update portable and util tests for C TAP Harness 1.1. * Remove the need for Autoconf substitution in test programs. * Support running a single test program with runtests -o. * Properly handle test cases that are skipped in their entirety. * Much improved C TAP library more closely matching Test::More. Rewrite client/basic-t to use the new test library functions and my current test case coding style. --- .gitignore | 4 +- Makefile.am | 52 ++++--- NEWS | 8 + README | 8 +- configure.ac | 1 - tests/TESTS | 3 + tests/client/basic-t.in | 220 +++++++++----------------- tests/libtest.c | 203 ------------------------ tests/libtest.h | 69 --------- tests/libtest.sh | 80 ---------- tests/portable/asprintf-t.c | 34 +++-- tests/portable/mkstemp-t.c | 73 +++++++++ tests/portable/mkstemp.c | 2 + tests/portable/setenv-t.c | 46 ++++++ tests/portable/setenv.c | 2 + tests/portable/snprintf-t.c | 123 +++++++-------- tests/portable/strlcat-t.c | 84 +++++----- tests/portable/strlcpy-t.c | 73 +++++---- tests/runtests.c | 327 ++++++++++++++++++++++++++++++--------- tests/tap/basic.c | 356 +++++++++++++++++++++++++++++++++++++++++++ tests/tap/basic.h | 98 ++++++++++++ tests/tap/kerberos.c | 164 ++++++++++++++++++++ tests/tap/kerberos.h | 32 ++++ tests/tap/kerberos.sh | 48 ++++++ tests/tap/libtap.sh | 148 ++++++++++++++++++ tests/tap/messages.c | 80 ++++++++++ tests/tap/messages.h | 35 +++++ tests/tap/process.c | 100 ++++++++++++ tests/tap/process.h | 37 +++++ tests/tap/remctl.sh | 46 ++++++ tests/util/concat-t.c | 60 +++----- tests/util/messages-krb5-t.c | 99 ++++++++++++ tests/util/messages-t.c | 201 +++++++----------------- tests/util/xmalloc-t | 127 +++++++++++++++ tests/util/xmalloc-t.in | 126 --------------- tests/util/xmalloc.c | 85 ++++++----- 36 files changed, 2138 insertions(+), 1116 deletions(-) delete mode 100644 tests/libtest.c delete mode 100644 tests/libtest.h delete mode 100644 tests/libtest.sh create mode 100644 tests/portable/mkstemp-t.c create mode 100644 tests/portable/mkstemp.c create mode 100644 tests/portable/setenv-t.c create mode 100644 tests/portable/setenv.c create mode 100644 tests/tap/basic.c create mode 100644 tests/tap/basic.h create mode 100644 tests/tap/kerberos.c create mode 100644 tests/tap/kerberos.h create mode 100644 tests/tap/kerberos.sh create mode 100644 tests/tap/libtap.sh create mode 100644 tests/tap/messages.c create mode 100644 tests/tap/messages.h create mode 100644 tests/tap/process.c create mode 100644 tests/tap/process.h create mode 100644 tests/tap/remctl.sh create mode 100644 tests/util/messages-krb5-t.c create mode 100755 tests/util/xmalloc-t delete mode 100644 tests/util/xmalloc-t.in (limited to 'configure.ac') diff --git a/.gitignore b/.gitignore index 4599484..09ae109 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,8 @@ /tests/data/test.krbtype /tests/kasetkey/basic-t /tests/portable/asprintf-t +/tests/portable/mkstemp-t +/tests/portable/setenv-t /tests/portable/snprintf-t /tests/portable/strlcat-t /tests/portable/strlcpy-t @@ -37,9 +39,9 @@ /tests/server/keytab-t /tests/server/pod-t /tests/util/concat-t +/tests/util/messages-krb5-t /tests/util/messages-t /tests/util/xmalloc -/tests/util/xmalloc-t /wallet-*.tar.gz /stamp-h1 .deps diff --git a/Makefile.am b/Makefile.am index 27a6e39..056229b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -117,33 +117,45 @@ distclean-local: fi # The bits below are for the test suite, not for the main package. -check_PROGRAMS = tests/runtests tests/portable/asprintf-t \ - tests/portable/snprintf-t tests/portable/strlcat-t \ - tests/portable/strlcpy-t tests/util/concat-t tests/util/messages-t \ - tests/util/xmalloc -check_LIBRARIES = tests/libtest.a -tests_libtest_a_SOURCES = tests/libtest.c tests/libtest.h +check_PROGRAMS = tests/runtests tests/portable/asprintf-t \ + tests/portable/mkstemp-t tests/portable/setenv-t \ + tests/portable/snprintf-t tests/portable/strlcat-t \ + tests/portable/strlcpy-t tests/util/concat-t \ + tests/util/messages-krb5-t tests/util/messages-t tests/util/xmalloc +tests_runtests_CPPFLAGS = -DSOURCE='"$(abs_top_srcdir)/tests"' \ + -DBUILD='"$(abs_top_builddir)/tests"' +check_LIBRARIES = tests/tap/libtap.a +tests_tap_libtap_a_CPPFLAGS = -I$(abs_top_srcdir)/tests $(KRB5_CPPFLAGS) +tests_tap_libtap_a_SOURCES = tests/tap/basic.c tests/tap/basic.h \ + tests/tap/kerberos.c tests/tap/kerberos.h tests/tap/messages.c \ + tests/tap/messages.h tests/tap/process.c tests/tap/process.h # All of the test programs. tests_portable_asprintf_t_SOURCES = tests/portable/asprintf-t.c \ - tests/portable/asprintf.c -tests_portable_asprintf_t_LDADD = tests/libtest.a util/libutil.a \ - portable/libportable.a + tests/portable/asprintf.c +tests_portable_asprintf_t_LDADD = tests/tap/libtap.a portable/libportable.a +tests_portable_mkstemp_t_SOURCES = tests/portable/mkstemp-t.c \ + tests/portable/mkstemp.c +tests_portable_mkstemp_t_LDADD = tests/tap/libtap.a portable/libportable.a +tests_portable_setenv_t_SOURCES = tests/portable/setenv-t.c \ + tests/portable/setenv.c +tests_portable_setenv_t_LDADD = tests/tap/libtap.a portable/libportable.a tests_portable_snprintf_t_SOURCES = tests/portable/snprintf-t.c \ - tests/portable/snprintf.c -tests_portable_snprintf_t_LDADD = tests/libtest.a util/libutil.a \ - portable/libportable.a + tests/portable/snprintf.c +tests_portable_snprintf_t_LDADD = tests/tap/libtap.a portable/libportable.a tests_portable_strlcat_t_SOURCES = tests/portable/strlcat-t.c \ - tests/portable/strlcat.c -tests_portable_strlcat_t_LDADD = tests/libtest.a util/libutil.a \ - portable/libportable.a + tests/portable/strlcat.c +tests_portable_strlcat_t_LDADD = tests/tap/libtap.a portable/libportable.a tests_portable_strlcpy_t_SOURCES = tests/portable/strlcpy-t.c \ - tests/portable/strlcpy.c -tests_portable_strlcpy_t_LDADD = tests/libtest.a util/libutil.a \ - portable/libportable.a -tests_util_concat_t_LDADD = tests/libtest.a util/libutil.a \ + tests/portable/strlcpy.c +tests_portable_strlcpy_t_LDADD = tests/tap/libtap.a portable/libportable.a +tests_util_concat_t_LDADD = tests/tap/libtap.a util/libutil.a \ portable/libportable.a -tests_util_messages_t_LDADD = tests/libtest.a util/libutil.a \ +tests_util_messages_krb5_t_CPPFLAGS = $(KRB5_CPPFLAGS) +tests_util_messages_krb5_t_LDFLAGS = $(KRB5_LDFLAGS) +tests_util_messages_krb5_t_LDADD = tests/tap/libtap.a util/libutil.a \ + portable/libportable.a $(KRB5_LIBS) +tests_util_messages_t_LDADD = tests/tap/libtap.a util/libutil.a \ portable/libportable.a tests_util_xmalloc_LDADD = util/libutil.a portable/libportable.a diff --git a/NEWS b/NEWS index 1d3a5e3..96962f8 100644 --- a/NEWS +++ b/NEWS @@ -67,6 +67,14 @@ wallet 0.10 (unreleased) * Use AC_TYPE_LONG_LONG_INT instead of AC_CHECK_TYPES([long long]). * Provide a proper bool type with Sun Studio 12 on Solaris 10. * Break util/util.h into separate header files per module. + * Update portable and util tests for C TAP Harness 1.1. + + Update to C TAP Harness 1.1: + + * Remove the need for Autoconf substitution in test programs. + * Support running a single test program with runtests -o. + * Properly handle test cases that are skipped in their entirety. + * Much improved C TAP library more closely matching Test::More. wallet 0.9 (2008-04-24) diff --git a/README b/README index 7302c06..eb9b39c 100644 --- a/README +++ b/README @@ -233,8 +233,12 @@ TESTING not available, but this has not yet been fully tested in all of its possible permutations. - If a test case fails, please run that individual test program directly - and send me the output when reporting the problem. + If a test fails, you can run a single test with verbose output via: + + tests/runtests -o + + Do this instead of running the test program directly since it will + ensure that necessary environment variables are set up. CONFIGURATION diff --git a/configure.ac b/configure.ac index f66a682..0330aa9 100644 --- a/configure.ac +++ b/configure.ac @@ -70,5 +70,4 @@ AC_CONFIG_FILES([tests/server/admin-t], [chmod +x tests/server/admin-t]) AC_CONFIG_FILES([tests/server/backend-t], [chmod +x tests/server/backend-t]) AC_CONFIG_FILES([tests/server/keytab-t], [chmod +x tests/server/keytab-t]) AC_CONFIG_FILES([tests/server/pod-t], [chmod +x tests/server/pod-t]) -AC_CONFIG_FILES([tests/util/xmalloc-t], [chmod +x tests/util/xmalloc-t]) AC_OUTPUT diff --git a/tests/TESTS b/tests/TESTS index a446643..ac6fd82 100644 --- a/tests/TESTS +++ b/tests/TESTS @@ -3,6 +3,8 @@ client/full client/pod client/prompt portable/asprintf +portable/mkstemp +portable/setenv portable/snprintf portable/strlcat portable/strlcpy @@ -12,4 +14,5 @@ server/keytab server/pod util/concat util/messages +util/messages-krb5 util/xmalloc diff --git a/tests/client/basic-t.in b/tests/client/basic-t.in index 96b165e..1dbc0b9 100644 --- a/tests/client/basic-t.in +++ b/tests/client/basic-t.in @@ -9,43 +9,10 @@ # See LICENSE for licensing terms. # Load the test library. -. "@abs_top_srcdir@/tests/libtest.sh" - -# Print the number of tests. -total=31 -count=1 -echo "$total" - -# Find the client program. -chdir_data '../client/wallet' -if [ ! -f 'data/test.keytab' ] || [ -z '@REMCTLD@' ] ; then - skip 1 "$total" 'no Kerberos configuration' - exit 0 -fi -wallet='../client/wallet' - -# Start the remctld daemon and wait for it to start. -principal=`cat data/test.principal` -rm -f data/pid -( @REMCTLD@ -m -p 14373 -s "$principal" -P data/pid -f data/basic.conf \ - -S -F -k data/test.keytab &) -KRB5CCNAME=data/test.cache; export KRB5CCNAME -kinit -k -t data/test.keytab "$principal" > /dev/null 2>&1 -if [ $? != 0 ] ; then - kinit -t data/test.keytab "$principal" > /dev/null 2>&1 -fi -if [ $? != 0 ] ; then - kinit -T /bin/true -k -K data/test.keytab "$principal" > /dev/null 2>&1 -fi -if [ $? != 0 ] ; then - echo 'Unable to obtain Kerberos tickets' >&2 - exit 1 -fi -[ -f data/pid ] || sleep 1 -if [ ! -f data/pid ] ; then - echo 'remctld did not start' >&2 - exit 1 -fi +. "$SOURCE/tap/libtap.sh" +. "$SOURCE/tap/kerberos.sh" +. "$SOURCE/tap/remctl.sh" +cd "$BUILD" # We need a modified krb5.conf file to test wallet configuration settings in # krb5.conf. Despite the hard-coding of test-k5.stanford.edu, this test isn't @@ -73,43 +40,39 @@ EOF fi done if [ -z "$krb5conf" ] ; then - echo 'No krb5.conf found -- put one in tests/data/krb5.conf' >&2 - exit 1 + skip_all 'no krb5.conf found, put one in tests/data/krb5.conf' +fi + +# Test setup. +kerberos_setup +if [ $? != 0 ] ; then + skip_all 'Kerberos tests not configured' +elif [ -z '@REMCTLD@' ] ; then + skip_all 'No remctld found' +else + plan 34 fi +remctld_start '@REMCTLD@' "$SOURCE/data/basic.conf" +wallet="$BUILD/../client/wallet" # Make sure everything's clean. rm -f output output.bak keytab keytab.bak srvtab srvtab.bak autocreated # Now, we can finally run our tests. First, basic operations. -runsuccess "" "$wallet" -k "$principal" -p 14373 -s localhost -c fake-wallet \ - -f output get file fake-test -if cmp output data/fake-data >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi -if [ -f output.bak ] || [ -f output.new ] ; then - printcount "not ok" -else - printcount "ok" -fi -if [ -f autocreated ] ; then - printcount "ok" -else - printcount "not ok" -fi -runsuccess "" "$wallet" -k "$principal" -p 14373 -s localhost -c fake-wallet \ - -f output get file fake-test -if cmp output data/fake-data >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi -if [ -f output.new ] || [ ! -f output.bak ] ; then - printcount "not ok" -else - printcount "ok" -fi +ok_program 'get file' 0 '' \ + "$wallet" -k "$principal" -p 14373 -s localhost -c fake-wallet -f output \ + get file fake-test +ok '...and file is correct' cmp output data/fake-data +ok '...and no backup files' [ ! -f output.bak ] +ok '...and no new files' [ ! -f output.new ] +ok '...and we tried autocreation' [ -f autocreated ] +ok_program 'get file again' 0 '' \ + "$wallet" -k "$principal" -p 14373 -s localhost -c fake-wallet -f output \ + get file fake-test +ok '...and file is correct' cmp output data/fake-data +ok '...and now there is a backup file' [ -f output.bak ] +ok '...which has the right contents' cmp output.bak data/fake-data +ok '...but there is no new file' [ ! -f output.new ] # Now, append configuration to krb5.conf and test getting configuration from # there. @@ -123,116 +86,79 @@ cat >> krb5.conf </dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi +ok_program 'get file with configuration' 0 '' \ + "$wallet" -f output get file fake-test +ok '...and file is correct' cmp output data/fake-data rm -f output output.bak # Test keytab support. -runsuccess "" "$wallet" -f keytab get keytab service/fake-srvtab -if cmp keytab data/fake-keytab >/dev/null 2>&1 ; then - printcount "ok" - rm keytab -else - printcount "not ok" -fi +ok_program 'get keytab' 0 '' \ + "$wallet" -f keytab get keytab service/fake-srvtab +ok '...and keytab is correct' cmp keytab data/fake-keytab +rm -f keytab # Test srvtab support. -runsuccess "" "$wallet" -f keytab -S srvtab get keytab service/fake-srvtab -if cmp keytab data/fake-keytab >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi -rm keytab -runsuccess "" "$wallet" -f keytab -S srvtab get keytab service/fake-srvtab -if cmp keytab data/fake-keytab >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi -if cmp srvtab data/fake-srvtab >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi -if cmp srvtab.bak data/fake-srvtab >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi +ok_program 'get srvtab' 0 '' \ + "$wallet" -f keytab -S srvtab get keytab service/fake-srvtab +ok '...and keytab is correct' cmp keytab data/fake-keytab +rm -f keytab +ok_program 'get srvtab again' 0 '' \ + "$wallet" -f keytab -S srvtab get keytab service/fake-srvtab +ok '...and keytab is correct' cmp keytab data/fake-keytab +ok '...and srvtab is correct' cmp srvtab data/fake-srvtab +ok '...and srvtab backup is correct' cmp srvtab.bak data/fake-srvtab rm -f srvtab srvtab.bak # Test keytab merging. -runsuccess "" "$wallet" -f keytab get keytab service/fake-keytab +ok_program 'keytab merging' 0 '' \ + "$wallet" -f keytab get keytab service/fake-keytab (klist -keK keytab 2>&1) | sed '/Keytab name:/d' > klist-seen (klist -keK data/fake-keytab-merge 2>&1) | sed '/Keytab name:/d' > klist-good -if cmp klist-seen klist-good >/dev/null 2>&1 ; then - printcount "ok" - rm -f keytab klist-seen klist-good -else - printcount "not ok" -fi +ok '...and the merged keytab is correct' cmp klist-seen klist-good +rm -f keytab klist-seen klist-good # Test srvtab download into a merged keytab with an older version. cp data/fake-keytab-old keytab -runsuccess "" "$wallet" -f keytab -S srvtab get keytab service/fake-srvtab -if cmp srvtab data/fake-srvtab >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi +ok_program 'keytab merging with srvtab creation' 0 '' \ + "$wallet" -f keytab -S srvtab get keytab service/fake-srvtab +ok '...and the srvtab is correct' cmp srvtab data/fake-srvtab rm -f keytab srvtab # Test store from standard input. -echo "This is a test of store" | runsuccess "" "$wallet" store file fake-test -count=`expr $count + 1` +echo "This is a test of store" > input +ok_program 'store from stdin' 0 '' "$wallet" store file fake-test < input +rm -f input echo "file fake-test" > store-correct echo "This is a test of store" >> store-correct -if cmp store-output store-correct >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" - echo == store-output == - cat store-output - echo == store-correct == - cat store-correct -fi +ok '...and the correct data was stored' diff store-output store-correct rm -f store-output store-correct # Test store with -f. echo "This is more store input" > store-input echo "file fake-test" > store-correct cat store-input >> store-correct -runsuccess "" "$wallet" -f store-input store file fake-test -if cmp store-output store-correct >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi +ok_program 'store from a file' 0 '' \ + "$wallet" -f store-input store file fake-test +ok '...and the correct data was stored' cmp store-output store-correct rm -f store-input store-output store-correct # Test various other client functions and errors. -runsuccess "This is a fake keytab." "$wallet" get keytab service/fake-output -runsuccess "Some stuff about file fake-test" \ +ok_program 'get output to stdout' 0 'This is a fake keytab.' \ + "$wallet" get keytab service/fake-output +ok_program 'show output' 0 'Some stuff about file fake-test' \ "$wallet" show file fake-test -runfailure 1 "wallet: Unknown object type srvtab" \ +ok_program 'unknown object type' 1 'wallet: Unknown object type srvtab' \ "$wallet" get srvtab service/fake-test -runfailure 1 "wallet: Unknown keytab service/unknown" \ +ok_program 'unknown keytab name in show' 1 \ + 'wallet: Unknown keytab service/unknown' \ "$wallet" show keytab service/unknown -runfailure 1 "wallet: Unknown keytab service/unknown" \ +ok_program 'unknown keytab name in get' 1 \ + 'wallet: Unknown keytab service/unknown' \ "$wallet" get keytab service/unknown -runsuccess "Expiration date of keytab service/fake-test" \ +ok_program 'expiration date' 0 'Expiration date of keytab service/fake-test' \ "$wallet" expires keytab service/fake-test # Clean up. -KRB5_CONFIG= -rm krb5.conf -rm -f autocreated data/test.cache -if [ -f data/pid ] ; then - kill `cat data/pid` - rm -f data/pid -fi +rm -f autocreated krb5.conf +remctld_stop +kerberos_cleanup diff --git a/tests/libtest.c b/tests/libtest.c deleted file mode 100644 index bddaf91..0000000 --- a/tests/libtest.c +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Some utility routines for writing tests. - * - * Herein are a variety of utility routines for writing tests. All routines - * of the form ok*() take a test number and some number of appropriate - * arguments, check to be sure the results match the expected output using the - * arguments, and print out something appropriate for that test number. Other - * utility routines help in constructing more complex tests. - * - * Copyright 2006, 2007 Board of Trustees, Leland Stanford Jr. University - * Copyright (c) 2004, 2005, 2006 - * by Internet Systems Consortium, Inc. ("ISC") - * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, - * 2002, 2003 by The Internet Software Consortium and Rich Salz - * - * This code is derived from software contributed to the Internet Software - * Consortium by Rich Salz. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#include -#include - -#include -#include - -#include -#include - -/* A global buffer into which message_log_buffer stores error messages. */ -char *errors = NULL; - - -/* - * Initialize things. Turns on line buffering on stdout and then prints out - * the number of tests in the test suite. - */ -void -test_init(int count) -{ - if (setvbuf(stdout, NULL, _IOLBF, BUFSIZ) != 0) - syswarn("cannot set stdout to line buffered"); - printf("%d\n", count); -} - - -/* - * Takes a boolean success value and assumes the test passes if that value - * is true and fails if that value is false. - */ -void -ok(int n, int success) -{ - printf("%sok %d\n", success ? "" : "not ", n); -} - - -/* - * Takes an expected integer and a seen integer and assumes the test passes - * if those two numbers match. - */ -void -ok_int(int n, int wanted, int seen) -{ - if (wanted == seen) - printf("ok %d\n", n); - else - printf("not ok %d\n wanted: %d\n seen: %d\n", n, wanted, seen); -} - - -/* - * Takes a string and what the string should be, and assumes the test passes - * if those strings match (using strcmp). - */ -void -ok_string(int n, const char *wanted, const char *seen) -{ - if (wanted == NULL) - wanted = "(null)"; - if (seen == NULL) - seen = "(null)"; - if (strcmp(wanted, seen) != 0) - printf("not ok %d\n wanted: %s\n seen: %s\n", n, wanted, seen); - else - printf("ok %d\n", n); -} - - -/* - * Takes an expected integer and a seen integer and assumes the test passes if - * those two numbers match. - */ -void -ok_double(int n, double wanted, double seen) -{ - if (wanted == seen) - printf("ok %d\n", n); - else - printf("not ok %d\n wanted: %g\n seen: %g\n", n, wanted, seen); -} - - -/* - * Skip a test. - */ -void -skip(int n, const char *reason) -{ - printf("ok %d # skip", n); - if (reason != NULL) - printf(" - %s", reason); - putchar('\n'); -} - - -/* - * Report the same status on the next count tests. - */ -void -ok_block(int n, int count, int status) -{ - int i; - - for (i = 0; i < count; i++) - ok(n++, status); -} - - -/* - * Skip the next count tests. - */ -void -skip_block(int n, int count, const char *reason) -{ - int i; - - for (i = 0; i < count; i++) - skip(n++, reason); -} - - -/* - * An error handler that appends all errors to the errors global. Used by - * error_capture. - */ -static void -message_log_buffer(int len, const char *fmt, va_list args, int error UNUSED) -{ - char *message; - - message = xmalloc(len + 1); - vsnprintf(message, len + 1, fmt, args); - if (errors == NULL) { - errors = concat(message, "\n", (char *) 0); - } else { - char *new_errors; - - new_errors = concat(errors, message, "\n", (char *) 0); - free(errors); - errors = new_errors; - } - free(message); -} - - -/* - * Turn on the capturing of errors. Errors will be stored in the global - * errors variable where they can be checked by the test suite. Capturing is - * turned off with errors_uncapture. - */ -void -errors_capture(void) -{ - if (errors != NULL) { - free(errors); - errors = NULL; - } - message_handlers_warn(1, message_log_buffer); - message_handlers_notice(1, message_log_buffer); -} - - -/* - * Turn off the capturing of errors again. - */ -void -errors_uncapture(void) -{ - message_handlers_warn(1, message_log_stderr); - message_handlers_notice(1, message_log_stdout); -} diff --git a/tests/libtest.h b/tests/libtest.h deleted file mode 100644 index ad4f591..0000000 --- a/tests/libtest.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Some utility routines for writing tests. - * - * Copyright 2006, 2007 Board of Trustees, Leland Stanford Jr. University - * Copyright (c) 2004, 2005, 2006 - * by Internet Systems Consortium, Inc. ("ISC") - * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, - * 2002, 2003 by The Internet Software Consortium and Rich Salz - * - * This code is derived from software contributed to the Internet Software - * Consortium by Rich Salz. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef LIBTEST_H -#define LIBTEST_H 1 - -#include -#include - -/* - * Used for iterating through arrays. ARRAY_SIZE returns the number of - * elements in the array (useful for a < upper bound in a for loop) and - * ARRAY_END returns a pointer to the element past the end (ISO C99 makes it - * legal to refer to such a pointer as long as it's never dereferenced). - */ -#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) -#define ARRAY_END(array) (&(array)[ARRAY_SIZE(array)]) - -/* A global buffer into which errors_capture stores errors. */ -extern char *errors; - -BEGIN_DECLS - -void ok(int n, int success); -void ok_int(int n, int wanted, int seen); -void ok_double(int n, double wanted, double seen); -void ok_string(int n, const char *wanted, const char *seen); -void skip(int n, const char *reason); - -/* Report the same status on, or skip, the next count tests. */ -void ok_block(int n, int count, int success); -void skip_block(int n, int count, const char *reason); - -/* Print out the number of tests and set standard output to line buffered. */ -void test_init(int count); - -/* - * Turn on capturing of errors with errors_capture. Errors reported by warn - * will be stored in the global errors variable. Turn this off again with - * errors_uncapture. Caller is responsible for freeing errors when done. - */ -void errors_capture(void); -void errors_uncapture(void); - -END_DECLS - -#endif /* LIBTEST_H */ diff --git a/tests/libtest.sh b/tests/libtest.sh deleted file mode 100644 index 74f5ee6..0000000 --- a/tests/libtest.sh +++ /dev/null @@ -1,80 +0,0 @@ -# Shell function library for test cases. -# -# Written by Russ Allbery -# Copyright 2006, 2007, 2008 Board of Trustees, Leland Stanford Jr. University -# -# See LICENSE for licensing terms. - -# The count starts at 1 and is updated each time ok is printed. printcount -# takes "ok" or "not ok". -count=1 -printcount () { - echo "$1 $count $2" - count=`expr $count + 1` -} - -# Run a program expected to succeed, and print ok if it does and produces -# the correct output. Takes the output as the first argument, the command to -# run as the second argument, and then all subsequent arguments are arguments -# to the command. -runsuccess () { - w_output="$1" - shift - output=`"$@" 2>&1` - status=$? - if [ $status = 0 ] && [ x"$output" = x"$w_output" ] ; then - printcount 'ok' - else - printcount 'not ok' - echo " saw: $output" - echo " not: $w_output" - fi -} - -# Run a program expected to fail and make sure it fails with the correct exit -# status and the correct failure message. Takes the expected status, the -# expected output, and then everything else is the command and arguments. -# Strip the second colon and everything after it off the error message since -# it's system-specific. -runfailure () { - w_status="$1" - shift - w_output="$1" - shift - output=`"$@" 2>&1` - status=$? - output=`echo "$output" | sed 's/\(:[^:]*\):.*/\1/'` - if [ $status = $w_status ] && [ x"$output" = x"$w_output" ] ; then - printcount 'ok' - else - printcount 'not ok' - echo " saw: ($status) $output" - echo " not: ($w_status) $w_output" - fi -} - -# Skip tests from $1 to $2 inclusive with reason $3. -skip () { - n="$1" - while [ "$n" -le "$2" ] ; do - echo ok "$n # skip $3" - n=`expr "$n" + 1` - done -} - -# Given a file name or relative file path, try to cd to the correct directory -# so that the relative file path is valid. Exits with an error if that isn't -# possible. -chdir_data () { - if [ -f "../$1" ] ; then - cd .. - else - if [ -f "tests/$1" ] ; then - cd tests - fi - fi - if [ ! -f "$1" ] ; then - echo "Cannot locate $1" >&2 - exit 1 - fi -} diff --git a/tests/portable/asprintf-t.c b/tests/portable/asprintf-t.c index 689e7c7..04fbd1b 100644 --- a/tests/portable/asprintf-t.c +++ b/tests/portable/asprintf-t.c @@ -2,7 +2,8 @@ * asprintf and vasprintf test suite. * * Written by Russ Allbery - * Copyright 2006, 2008 Board of Trustees, Leland Stanford Jr. University + * Copyright 2006, 2008, 2009 + * Board of Trustees, Leland Stanford Jr. University * * See LICENSE for licensing terms. */ @@ -10,9 +11,10 @@ #include #include -#include +#include -int test_asprintf(char **, const char *, ...); +int test_asprintf(char **, const char *, ...) + __attribute__((__format__(printf, 2, 3))); int test_vasprintf(char **, const char *, va_list); static int @@ -32,25 +34,25 @@ main(void) { char *result = NULL; - test_init(12); + plan(12); - ok_int(1, 7, test_asprintf(&result, "%s", "testing")); - ok_string(2, "testing", result); + is_int(7, test_asprintf(&result, "%s", "testing"), "asprintf length"); + is_string("testing", result, "asprintf result"); free(result); - ok(3, 1); - ok_int(4, 0, test_asprintf(&result, "%s", "")); - ok_string(5, "", result); + ok(3, "free asprintf"); + is_int(0, test_asprintf(&result, "%s", ""), "asprintf empty length"); + is_string("", result, "asprintf empty string"); free(result); - ok(6, 1); + ok(6, "free asprintf of empty string"); - ok_int(7, 6, vatest(&result, "%d %s", 2, "test")); - ok_string(8, "2 test", result); + is_int(6, vatest(&result, "%d %s", 2, "test"), "vasprintf length"); + is_string("2 test", result, "vasprintf result"); free(result); - ok(9, 1); - ok_int(10, 0, vatest(&result, "%s", "")); - ok_string(11, "", result); + ok(9, "free vasprintf"); + is_int(0, vatest(&result, "%s", ""), "vasprintf empty length"); + is_string("", result, "vasprintf empty string"); free(result); - ok(12, 1); + ok(12, "free vasprintf of empty string"); return 0; } diff --git a/tests/portable/mkstemp-t.c b/tests/portable/mkstemp-t.c new file mode 100644 index 0000000..54701f7 --- /dev/null +++ b/tests/portable/mkstemp-t.c @@ -0,0 +1,73 @@ +/* + * mkstemp test suite. + * + * Written by Russ Allbery + * Copyright 2002, 2004, 2008, 2009 + * Board of Trustees, Leland Stanford Jr. University + * + * See LICENSE for licensing terms. + */ + +#include +#include + +#include +#include + +#include + +int test_mkstemp(char *template); + +int +main(void) +{ + int fd; + char template[] = "tsXXXXXXX"; + char tooshort[] = "XXXXX"; + char bad1[] = "/foo/barXXXXX"; + char bad2[] = "/foo/barXXXXXX.out"; + char buffer[256]; + struct stat st1, st2; + ssize_t length; + + plan(20); + + /* First, test a few error messages. */ + errno = 0; + is_int(-1, test_mkstemp(tooshort), "too short of template"); + is_int(EINVAL, errno, "...with correct errno"); + is_string("XXXXX", tooshort, "...and template didn't change"); + errno = 0; + is_int(-1, test_mkstemp(bad1), "bad template"); + is_int(EINVAL, errno, "...with correct errno"); + is_string("/foo/barXXXXX", bad1, "...and template didn't change"); + errno = 0; + is_int(-1, test_mkstemp(bad2), "template doesn't end in XXXXXX"); + is_int(EINVAL, errno, "...with correct errno"); + is_string("/foo/barXXXXXX.out", bad2, "...and template didn't change"); + errno = 0; + + /* Now try creating a real file. */ + fd = test_mkstemp(template); + ok(fd >= 0, "mkstemp works with valid template"); + ok(strcmp(template, "tsXXXXXXX") != 0, "...and template changed"); + ok(strncmp(template, "tsX", 3) == 0, "...and didn't touch first X"); + ok(access(template, F_OK) == 0, "...and the file exists"); + + /* Make sure that it's the same file as template refers to now. */ + ok(stat(template, &st1) == 0, "...and stat of template works"); + ok(fstat(fd, &st2) == 0, "...and stat of open file descriptor works"); + ok(st1.st_ino == st2.st_ino, "...and they're the same file"); + unlink(template); + + /* Make sure the open mode is correct. */ + length = strlen(template); + is_int(length, write(fd, template, length), "write to open file works"); + ok(lseek(fd, 0, SEEK_SET) == 0, "...and rewind works"); + is_int(length, read(fd, buffer, length), "...and the data is there"); + buffer[length] = '\0'; + is_string(template, buffer, "...and matches what we wrote"); + close(fd); + + return 0; +} diff --git a/tests/portable/mkstemp.c b/tests/portable/mkstemp.c new file mode 100644 index 0000000..4632d3d --- /dev/null +++ b/tests/portable/mkstemp.c @@ -0,0 +1,2 @@ +#define TESTING 1 +#include diff --git a/tests/portable/setenv-t.c b/tests/portable/setenv-t.c new file mode 100644 index 0000000..5bc59ce --- /dev/null +++ b/tests/portable/setenv-t.c @@ -0,0 +1,46 @@ +/* + * setenv test suite. + * + * Written by Russ Allbery + * Copyright 2009 Board of Trustees, Leland Stanford Jr. University + * Copyright (c) 2004, 2005, 2006 + * by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002, 2003 by The Internet Software Consortium and Rich Salz + * + * See LICENSE for licensing terms. + */ + +#include +#include + +#include + +#include + +int test_setenv(const char *name, const char *value, int overwrite); + +static const char test_var[] = "SETENV_TEST"; +static const char test_value1[] = "Do not taunt Happy Fun Ball."; +static const char test_value2[] = "Do not use Happy Fun Ball on concrete."; + + +int +main(void) +{ + plan(8); + + if (getenv(test_var)) + bail("%s already in the environment!", test_var); + + ok(test_setenv(test_var, test_value1, 0) == 0, "set string 1"); + is_string(test_value1, getenv(test_var), "...and getenv correct"); + ok(test_setenv(test_var, test_value2, 0) == 0, "set string 2"); + is_string(test_value1, getenv(test_var), "...and getenv unchanged"); + ok(test_setenv(test_var, test_value2, 1) == 0, "overwrite string 2"); + is_string(test_value2, getenv(test_var), "...and getenv changed"); + ok(test_setenv(test_var, "", 1) == 0, "overwrite with empty string"); + is_string("", getenv(test_var), "...and getenv correct"); + + return 0; +} diff --git a/tests/portable/setenv.c b/tests/portable/setenv.c new file mode 100644 index 0000000..79a7efd --- /dev/null +++ b/tests/portable/setenv.c @@ -0,0 +1,2 @@ +#define TESTING 1 +#include diff --git a/tests/portable/snprintf-t.c b/tests/portable/snprintf-t.c index 18c2326..ca6ae61 100644 --- a/tests/portable/snprintf-t.c +++ b/tests/portable/snprintf-t.c @@ -1,32 +1,25 @@ /* * snprintf test suite. * + * Written by Russ Allbery + * Copyright 2009 Board of Trustees, Leland Stanford Jr. University * Copyright (c) 2004, 2005, 2006 * by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, * 2002, 2003 by The Internet Software Consortium and Rich Salz * - * This code is derived from software contributed to the Internet Software - * Consortium by Rich Salz. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. + * See LICENSE for licensing terms. */ #include #include -#include +#include +/* + * Intentionally don't add the printf attribute here since we pass a + * zero-length printf format during testing and don't want warnings. + */ int test_snprintf(char *str, size_t count, const char *fmt, ...); int test_vsnprintf(char *str, size_t count, const char *fmt, va_list args); @@ -93,7 +86,7 @@ static unsigned long long ullong_nums[] = { static void -test_format(int n, int truncate, const char *expected, int count, +test_format(bool truncate, const char *expected, int count, const char *format, ...) { char buf[128]; @@ -103,16 +96,8 @@ test_format(int n, int truncate, const char *expected, int count, va_start(args, format); result = test_vsnprintf(buf, truncate ? 32 : sizeof(buf), format, args); va_end(args); - if (!strcmp(buf, expected) && result == count) { - printf("ok %d\n", n); - } else { - printf("not ok %d\n", n); - printf(" format: %s\n", format); - if (strcmp(buf, expected)) - printf(" saw: %s\n want: %s\n", buf, expected); - if (result != count) - printf(" %d != %d\n", result, count); - } + is_string(expected, buf, "format %s, wanted %s", format, expected); + is_int(count, result, "...and output length correct"); } @@ -124,75 +109,69 @@ main(void) long lcount; char lgbuf[128]; - test_init((26 + (ARRAY_SIZE(fp_formats) - 1) * ARRAY_SIZE(fp_nums) - + (ARRAY_SIZE(int_formats) - 1) * ARRAY_SIZE(int_nums) - + (ARRAY_SIZE(uint_formats) - 1) * ARRAY_SIZE(uint_nums) - + (ARRAY_SIZE(llong_formats) - 1) * ARRAY_SIZE(llong_nums) - + (ARRAY_SIZE(ullong_formats) - 1) * ARRAY_SIZE(ullong_nums))); - - ok(1, test_snprintf(NULL, 0, "%s", "abcd") == 4); - ok(2, test_snprintf(NULL, 0, "%d", 20) == 2); - ok(3, test_snprintf(NULL, 0, "Test %.2s", "abcd") == 7); - ok(4, test_snprintf(NULL, 0, "%c", 'a') == 1); - ok(5, test_snprintf(NULL, 0, "") == 0); - - test_format(6, 1, "abcd", 4, "%s", "abcd"); - test_format(7, 1, "20", 2, "%d", 20); - test_format(8, 1, "Test ab", 7, "Test %.2s", "abcd"); - test_format(9, 1, "a", 1, "%c", 'a'); - test_format(10, 1, "", 0, ""); - test_format(11, 1, "abcdefghijklmnopqrstuvwxyz01234", 36, "%s", - string); - test_format(12, 1, "abcdefghij", 10, "%.10s", string); - test_format(13, 1, " abcdefghij", 12, "%12.10s", string); - test_format(14, 1, " abcdefghijklmnopqrstuvwxyz0", 40, "%40s", - string); - test_format(15, 1, "abcdefghij ", 14, "%-14.10s", string); - test_format(16, 1, " abcdefghijklmnopq", 50, "%50s", - string); - test_format(17, 1, "%abcd%", 6, "%%%0s%%", "abcd"); - test_format(18, 1, "", 0, "%.0s", string); - test_format(19, 1, "abcdefghijklmnopqrstuvwxyz 444", 32, "%.26s %d", + plan(8 + + (18 + (ARRAY_SIZE(fp_formats) - 1) * ARRAY_SIZE(fp_nums) + + (ARRAY_SIZE(int_formats) - 1) * ARRAY_SIZE(int_nums) + + (ARRAY_SIZE(uint_formats) - 1) * ARRAY_SIZE(uint_nums) + + (ARRAY_SIZE(llong_formats) - 1) * ARRAY_SIZE(llong_nums) + + (ARRAY_SIZE(ullong_formats) - 1) * ARRAY_SIZE(ullong_nums)) * 2); + + is_int(4, test_snprintf(NULL, 0, "%s", "abcd"), "simple string length"); + is_int(2, test_snprintf(NULL, 0, "%d", 20), "number length"); + is_int(7, test_snprintf(NULL, 0, "Test %.2s", "abcd"), "limited string"); + is_int(1, test_snprintf(NULL, 0, "%c", 'a'), "character length"); + is_int(0, test_snprintf(NULL, 0, ""), "empty format length"); + + test_format(true, "abcd", 4, "%s", "abcd"); + test_format(true, "20", 2, "%d", 20); + test_format(true, "Test ab", 7, "Test %.2s", "abcd"); + test_format(true, "a", 1, "%c", 'a'); + test_format(true, "", 0, ""); + test_format(true, "abcdefghijklmnopqrstuvwxyz01234", 36, "%s", string); + test_format(true, "abcdefghij", 10, "%.10s", string); + test_format(true, " abcdefghij", 12, "%12.10s", string); + test_format(true, " abcdefghijklmnopqrstuvwxyz0", 40, "%40s", string); + test_format(true, "abcdefghij ", 14, "%-14.10s", string); + test_format(true, " abcdefghijklmnopq", 50, "%50s", string); + test_format(true, "%abcd%", 6, "%%%0s%%", "abcd"); + test_format(true, "", 0, "%.0s", string); + test_format(true, "abcdefghijklmnopqrstuvwxyz 444", 32, "%.26s %d", string, 4444); - test_format(20, 1, "abcdefghijklmnopqrstuvwxyz -2.", 32, - "%.26s %.1f", string, -2.5); - test_format(21, 1, "abcdefghij4444", 14, "%.10s%n%d", string, &count, - 4444); - ok(22, count == 10); - test_format(23, 1, "abcdefghijklmnopqrstuvwxyz01234", 36, "%n%s%ln", + test_format(true, "abcdefghijklmnopqrstuvwxyz -2.", 32, "%.26s %.1f", + string, -2.5); + test_format(true, "abcdefghij4444", 14, "%.10s%n%d", string, &count, 4444); + is_int(10, count, "correct output from %%n"); + test_format(true, "abcdefghijklmnopqrstuvwxyz01234", 36, "%n%s%ln", &count, string, &lcount); - ok(24, count == 0); - ok(25, lcount == 31); - test_format(26, 1, "(null)", 6, "%s", NULL); + is_int(0, count, "correct output from two %%n"); + is_int(31, lcount, "correct output from long %%ln"); + test_format(true, "(null)", 6, "%s", NULL); n = 26; for (i = 0; fp_formats[i] != NULL; i++) for (j = 0; j < ARRAY_SIZE(fp_nums); j++) { count = sprintf(lgbuf, fp_formats[i], fp_nums[j]); - test_format(++n, 0, lgbuf, count, fp_formats[i], fp_nums[j]); + test_format(false, lgbuf, count, fp_formats[i], fp_nums[j]); } for (i = 0; int_formats[i] != NULL; i++) for (j = 0; j < ARRAY_SIZE(int_nums); j++) { count = sprintf(lgbuf, int_formats[i], int_nums[j]); - test_format(++n, 0, lgbuf, count, int_formats[i], - int_nums[j]); + test_format(false, lgbuf, count, int_formats[i], int_nums[j]); } for (i = 0; uint_formats[i] != NULL; i++) for (j = 0; j < ARRAY_SIZE(uint_nums); j++) { count = sprintf(lgbuf, uint_formats[i], uint_nums[j]); - test_format(++n, 0, lgbuf, count, uint_formats[i], - uint_nums[j]); + test_format(false, lgbuf, count, uint_formats[i], uint_nums[j]); } for (i = 0; llong_formats[i] != NULL; i++) for (j = 0; j < ARRAY_SIZE(llong_nums); j++) { count = sprintf(lgbuf, llong_formats[i], llong_nums[j]); - test_format(++n, 0, lgbuf, count, llong_formats[i], - llong_nums[j]); + test_format(false, lgbuf, count, llong_formats[i], llong_nums[j]); } for (i = 0; ullong_formats[i] != NULL; i++) for (j = 0; j < ARRAY_SIZE(ullong_nums); j++) { count = sprintf(lgbuf, ullong_formats[i], ullong_nums[j]); - test_format(++n, 0, lgbuf, count, ullong_formats[i], + test_format(false, lgbuf, count, ullong_formats[i], ullong_nums[j]); } diff --git a/tests/portable/strlcat-t.c b/tests/portable/strlcat-t.c index 2f39925..e02c277 100644 --- a/tests/portable/strlcat-t.c +++ b/tests/portable/strlcat-t.c @@ -1,31 +1,20 @@ /* * strlcat test suite. * + * Written by Russ Allbery + * Copyright 2009 Board of Trustees, Leland Stanford Jr. University * Copyright (c) 2004, 2005, 2006 * by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, * 2002, 2003 by The Internet Software Consortium and Rich Salz * - * This code is derived from software contributed to the Internet Software - * Consortium by Rich Salz. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. + * See LICENSE for licensing terms. */ #include #include -#include +#include size_t test_strlcat(char *, const char *, size_t); @@ -35,42 +24,51 @@ main(void) { char buffer[10] = ""; - test_init(27); + plan(27); - ok_int(1, 3, test_strlcat(buffer, "foo", sizeof(buffer))); - ok_string(2, "foo", buffer); - ok_int(3, 7, test_strlcat(buffer, " bar", sizeof(buffer))); - ok_string(4, "foo bar", buffer); - ok_int(5, 9, test_strlcat(buffer, "!!", sizeof(buffer))); - ok_string(6, "foo bar!!", buffer); - ok_int(7, 10, test_strlcat(buffer, "!", sizeof(buffer))); - ok_string(8, "foo bar!!", buffer); - ok(9, buffer[9] == '\0'); + is_int(3, test_strlcat(buffer, "foo", sizeof(buffer)), + "strlcat into empty buffer"); + is_string("foo", buffer, "...with right output"); + is_int(7, test_strlcat(buffer, " bar", sizeof(buffer)), + "...and append more"); + is_string("foo bar", buffer, "...and output is still correct"); + is_int(9, test_strlcat(buffer, "!!", sizeof(buffer)), + "...and append to buffer limit"); + is_string("foo bar!!", buffer, "...output is still correct"); + is_int(10, test_strlcat(buffer, "!", sizeof(buffer)), + "...append one more character"); + is_string("foo bar!!", buffer, "...and output didn't change"); + ok(buffer[9] == '\0', "...buffer still nul-terminated"); buffer[0] = '\0'; - ok_int(10, 11, test_strlcat(buffer, "hello world", sizeof(buffer))); - ok_string(11, "hello wor", buffer); - ok(12, buffer[9] == '\0'); + is_int(11, test_strlcat(buffer, "hello world", sizeof(buffer)), + "append single long string"); + is_string("hello wor", buffer, "...string truncates properly"); + ok(buffer[9] == '\0', "...buffer still nul-terminated"); buffer[0] = '\0'; - ok_int(13, 7, test_strlcat(buffer, "sausage", 5)); - ok_string(14, "saus", buffer); - ok_int(15, 14, test_strlcat(buffer, "bacon eggs", sizeof(buffer))); - ok_string(16, "sausbacon", buffer); + is_int(7, test_strlcat(buffer, "sausage", 5), "lie about buffer length"); + is_string("saus", buffer, "...contents are correct"); + is_int(14, test_strlcat(buffer, "bacon eggs", sizeof(buffer)), + "...add more up to real size"); + is_string("sausbacon", buffer, "...and result is correct"); /* Make sure that with a size of 0, the destination isn't changed. */ - ok_int(17, 11, test_strlcat(buffer, "!!", 0)); - ok_string(18, "sausbacon", buffer); + is_int(11, test_strlcat(buffer, "!!", 0), "no change with size of 0"); + is_string("sausbacon", buffer, "...and content is the same"); /* Now play with empty strings. */ - ok_int(19, 9, test_strlcat(buffer, "", 0)); - ok_string(20, "sausbacon", buffer); + is_int(9, test_strlcat(buffer, "", 0), + "correct count when appending empty string"); + is_string("sausbacon", buffer, "...and contents are unchanged"); buffer[0] = '\0'; - ok_int(21, 0, test_strlcat(buffer, "", sizeof(buffer))); - ok_string(22, "", buffer); - ok_int(23, 3, test_strlcat(buffer, "foo", 2)); - ok_string(24, "f", buffer); - ok(25, buffer[1] == '\0'); - ok_int(26, 1, test_strlcat(buffer, "", sizeof(buffer))); - ok(27, buffer[1] == '\0'); + is_int(0, test_strlcat(buffer, "", sizeof(buffer)), + "correct count when appending empty string to empty buffer"); + is_string("", buffer, "...and buffer content is correct"); + is_int(3, test_strlcat(buffer, "foo", 2), "append to length 2 buffer"); + is_string("f", buffer, "...and got only a single character"); + ok(buffer[1] == '\0', "...and buffer is still nul-terminated"); + is_int(1, test_strlcat(buffer, "", sizeof(buffer)), + "append an empty string"); + ok(buffer[1] == '\0', "...and buffer is still nul-terminated"); return 0; } diff --git a/tests/portable/strlcpy-t.c b/tests/portable/strlcpy-t.c index 74c9ecd..ba224ba 100644 --- a/tests/portable/strlcpy-t.c +++ b/tests/portable/strlcpy-t.c @@ -1,31 +1,20 @@ /* * strlcpy test suite. * + * Written by Russ Allbery + * Copyright 2009 Board of Trustees, Leland Stanford Jr. University * Copyright (c) 2004, 2005, 2006 * by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, * 2002, 2003 by The Internet Software Consortium and Rich Salz * - * This code is derived from software contributed to the Internet Software - * Consortium by Rich Salz. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. + * See LICENSE for licensing terms. */ #include #include -#include +#include size_t test_strlcpy(char *, const char *, size_t); @@ -35,37 +24,43 @@ main(void) { char buffer[10]; - test_init(23); + plan(23); - ok_int(1, 3, test_strlcpy(buffer, "foo", sizeof(buffer))); - ok_string(2, "foo", buffer); - ok_int(3, 9, test_strlcpy(buffer, "hello wor", sizeof(buffer))); - ok_string(4, "hello wor", buffer); - ok_int(5, 10, test_strlcpy(buffer, "world hell", sizeof(buffer))); - ok_string(6, "world hel", buffer); - ok(7, buffer[9] == '\0'); - ok_int(8, 11, test_strlcpy(buffer, "hello world", sizeof(buffer))); - ok_string(9, "hello wor", buffer); - ok(10, buffer[9] == '\0'); + is_int(3, test_strlcpy(buffer, "foo", sizeof(buffer)), "simple strlcpy"); + is_string("foo", buffer, "...result is correct"); + is_int(9, test_strlcpy(buffer, "hello wor", sizeof(buffer)), + "strlcpy exact length of buffer"); + is_string("hello wor", buffer, "...result is correct"); + is_int(10, test_strlcpy(buffer, "world hell", sizeof(buffer)), + "strlcpy one more than buffer length"); + is_string("world hel", buffer, "...result is correct"); + ok(buffer[9] == '\0', "...buffer is nul-terminated"); + is_int(11, test_strlcpy(buffer, "hello world", sizeof(buffer)), + "strlcpy more than buffer length"); + is_string("hello wor", buffer, "...result is correct"); + ok(buffer[9] == '\0', "...buffer is nul-terminated"); /* Make sure that with a size of 0, the destination isn't changed. */ - ok_int(11, 3, test_strlcpy(buffer, "foo", 0)); - ok_string(12, "hello wor", buffer); + is_int(3, test_strlcpy(buffer, "foo", 0), "buffer unchanged if size 0"); + is_string("hello wor", buffer, "...contents still the same"); /* Now play with empty strings. */ - ok_int(13, 0, test_strlcpy(buffer, "", 0)); - ok_string(14, "hello wor", buffer); - ok_int(15, 0, test_strlcpy(buffer, "", sizeof(buffer))); - ok_string(16, "", buffer); - ok_int(17, 3, test_strlcpy(buffer, "foo", 2)); - ok_string(18, "f", buffer); - ok(19, buffer[1] == '\0'); - ok_int(20, 0, test_strlcpy(buffer, "", 1)); - ok(21, buffer[0] == '\0'); + is_int(0, test_strlcpy(buffer, "", 0), "copy empty string with size 0"); + is_string("hello wor", buffer, "...buffer unchanged"); + is_int(0, test_strlcpy(buffer, "", sizeof(buffer)), + "copy empty string into full buffer"); + is_string("", buffer, "...buffer now empty string"); + is_int(3, test_strlcpy(buffer, "foo", 2), + "copy string into buffer of size 2"); + is_string("f", buffer, "...got one character"); + ok(buffer[1] == '\0', "...buffer is nul-terminated"); + is_int(0, test_strlcpy(buffer, "", 1), + "copy empty string into buffer of size 1"); + ok(buffer[0] == '\0', "...buffer is empty string"); /* Finally, check using strlcpy as strlen. */ - ok_int(22, 3, test_strlcpy(NULL, "foo", 0)); - ok_int(23, 11, test_strlcpy(NULL, "hello world", 0)); + is_int(3, test_strlcpy(NULL, "foo", 0), "use strlcpy as strlen"); + is_int(11, test_strlcpy(NULL, "hello world", 0), "...again"); return 0; } diff --git a/tests/runtests.c b/tests/runtests.c index 060c8ad..1670012 100644 --- a/tests/runtests.c +++ b/tests/runtests.c @@ -17,12 +17,13 @@ * * where is the number of the test. ok indicates success, not ok * indicates failure, and "# skip" indicates the test was skipped for some - * reason (maybe because it doesn't apply to this platform). + * reason (maybe because it doesn't apply to this platform). This is a subset + * of TAP as documented in Test::Harness::TAP, which comes with Perl. * * Any bug reports, bug fixes, and improvements are very much welcome and * should be sent to the e-mail address below. * - * Copyright 2000, 2001, 2004, 2006, 2007, 2008 + * Copyright 2000, 2001, 2004, 2006, 2007, 2008, 2009 * Russ Allbery * * Permission is hereby granted, free of charge, to any person obtaining a @@ -44,16 +45,19 @@ * DEALINGS IN THE SOFTWARE. */ -#include -#include - #include #include #include +#include +#include +#include +#include #include #include +#include #include #include +#include /* sys/time.h must be included before sys/resource.h on some platforms. */ #include @@ -63,6 +67,19 @@ # define WCOREDUMP(status) ((unsigned)(status) & 0x80) #endif +/* + * The source and build versions of the tests directory. This is used to set + * the SOURCE and BUILD environment variables and find test programs, if set. + * Normally, this should be set as part of the build process to the test + * subdirectories of $(abs_top_srcdir) and $(abs_top_builddir) respectively. + */ +#ifndef SOURCE +# define SOURCE NULL +#endif +#ifndef BUILD +# define BUILD NULL +#endif + /* Test status codes. */ enum test_status { TEST_FAIL, @@ -78,7 +95,8 @@ enum test_status { /* Structure to hold data for a set of tests. */ struct testset { - const char *file; /* The file name of the test. */ + char *file; /* The file name of the test. */ + char *path; /* The path to the test program. */ int count; /* Expected count of tests. */ int current; /* The last seen test number. */ int length; /* The length of the last status message. */ @@ -89,6 +107,8 @@ struct testset { int aborted; /* Whether the set as aborted. */ int reported; /* Whether the results were reported. */ int status; /* The exit status of the test. */ + int all_skipped; /* Whether all tests were skipped. */ + char *reason; /* Why all tests were skipped. */ }; /* Structure to hold a linked list of test sets. */ @@ -103,8 +123,7 @@ struct testlist { */ static const char banner[] = "\n\ Running all tests listed in %s. If any tests fail, run the failing\n\ -test program by hand to see more details. The test program will have the\n\ -same name as the test set but with \"-t\" appended.\n\n"; +test program with runtests -o to see more details.\n\n"; /* Header for reports of failed tests. */ static const char header[] = "\n\ @@ -115,22 +134,6 @@ Failed Set Fail/Total (%) Skip Stat Failing Tests\n\ #define xmalloc(size) x_malloc((size), __FILE__, __LINE__) #define xstrdup(p) x_strdup((p), __FILE__, __LINE__) -/* Internal prototypes. */ -static void sysdie(const char *format, ...); -static void *x_malloc(size_t, const char *file, int line); -static char *x_strdup(const char *, const char *file, int line); -static int test_analyze(struct testset *); -static int test_batch(const char *testlist); -static void test_checkline(const char *line, struct testset *); -static void test_fail_summary(const struct testlist *); -static int test_init(const char *line, struct testset *); -static int test_print_range(int first, int last, int chars, int limit); -static void test_summarize(struct testset *, int status); -static pid_t test_start(const char *path, int *fd); -static double tv_diff(const struct timeval *, const struct timeval *); -static double tv_seconds(const struct timeval *); -static double tv_sum(const struct timeval *, const struct timeval *); - /* * Report a fatal error, including the results of strerror, and exit. @@ -218,6 +221,19 @@ tv_sum(const struct timeval *tv1, const struct timeval *tv2) } +/* + * Given a pointer to a string, skip any leading whitespace and return a + * pointer to the first non-whitespace character. + */ +static const char * +skip_whitespace(const char *p) +{ + while (isspace((unsigned char)(*p))) + p++; + return p; +} + + /* * Read the first line of test output, which should contain the range of * test numbers, and initialize the testset structure. Assume it was zeroed @@ -234,15 +250,34 @@ test_init(const char *line, struct testset *ts) * such as 1..10, accept that too for compatibility with Perl's * Test::Harness. */ - while (isspace((unsigned char)(*line))) - line++; + line = skip_whitespace(line); if (strncmp(line, "1..", 3) == 0) line += 3; - /* Get the count, check it for validity, and initialize the struct. */ - i = atoi(line); + /* + * Get the count, check it for validity, and initialize the struct. If we + * have something of the form "1..0 # skip foo", the whole file was + * skipped; record that. + */ + i = strtol(line, (char **) &line, 10); + if (i == 0) { + line = skip_whitespace(line); + if (*line == '#') { + line = skip_whitespace(line + 1); + if (strncasecmp(line, "skip", 4) == 0) { + line = skip_whitespace(line + 4); + if (*line != '\0') { + ts->reason = xstrdup(line); + ts->reason[strlen(ts->reason) - 1] = '\0'; + } + ts->all_skipped = 1; + ts->aborted = 1; + return 0; + } + } + } if (i <= 0) { - puts("invalid test count"); + puts("ABORTED (invalid test count)"); ts->aborted = 1; ts->reported = 1; return 0; @@ -329,8 +364,28 @@ static void test_checkline(const char *line, struct testset *ts) { enum test_status status = TEST_PASS; + const char *bail; + char *end; int current; + /* Before anything, check for a test abort. */ + bail = strstr(line, "Bail out!"); + if (bail != NULL) { + bail = skip_whitespace(bail + strlen("Bail out!")); + if (*bail != '\0') { + int length; + + length = strlen(bail); + if (bail[length - 1] == '\n') + length--; + test_backspace(ts); + printf("ABORTED (%.*s)\n", length, bail); + ts->reported = 1; + } + ts->aborted = 1; + return; + } + /* * If the given line isn't newline-terminated, it was too big for an * fgets(), which means ignore it. @@ -343,37 +398,40 @@ test_checkline(const char *line, struct testset *ts) status = TEST_FAIL; line += 4; } - if (strncmp(line, "ok ", 3) != 0) + if (strncmp(line, "ok", 2) != 0) return; - line += 3; - current = atoi(line); - if (current == 0) - return; - if (current < 0 || current > ts->count) { + line = skip_whitespace(line + 2); + errno = 0; + current = strtol(line, &end, 10); + if (errno != 0 || end == line) + current = ts->current + 1; + if (current <= 0 || current > ts->count) { test_backspace(ts); - printf("invalid test number %d\n", current); + printf("ABORTED (invalid test number %d)\n", current); ts->aborted = 1; ts->reported = 1; return; } - while (isspace((unsigned char)(*line))) - line++; + + /* + * Handle directives. We should probably do something more interesting + * with unexpected passes of todo tests. + */ while (isdigit((unsigned char)(*line))) line++; - while (isspace((unsigned char)(*line))) - line++; + line = skip_whitespace(line); if (*line == '#') { - line++; - while (isspace((unsigned char)(*line))) - line++; - if (strncmp(line, "skip", 4) == 0) + line = skip_whitespace(line + 1); + if (strncasecmp(line, "skip", 4) == 0) status = TEST_SKIP; + if (strncasecmp(line, "todo", 4) == 0) + status = (status == TEST_FAIL) ? TEST_SKIP : TEST_FAIL; } /* Make sure that the test number is in range and not a duplicate. */ if (ts->results[current - 1] != TEST_INVALID) { test_backspace(ts); - printf("duplicate test number %d\n", current); + printf("ABORTED (duplicate test number %d)\n", current); ts->aborted = 1; ts->reported = 1; return; @@ -449,9 +507,9 @@ test_summarize(struct testset *ts, int status) int last = 0; if (ts->aborted) { - fputs("aborted", stdout); + fputs("ABORTED", stdout); if (ts->count > 0) - printf(", passed %d/%d", ts->passed, ts->count - ts->skipped); + printf(" (passed %d/%d)", ts->passed, ts->count - ts->skipped); } else { for (i = 0; i < ts->count; i++) { if (ts->results[i] == TEST_INVALID) { @@ -520,19 +578,25 @@ test_analyze(struct testset *ts) { if (ts->reported) return 0; - if (WIFEXITED(ts->status) && WEXITSTATUS(ts->status) != 0) { + if (ts->all_skipped) { + if (ts->reason == NULL) + puts("skipped"); + else + printf("skipped (%s)\n", ts->reason); + return 1; + } else if (WIFEXITED(ts->status) && WEXITSTATUS(ts->status) != 0) { switch (WEXITSTATUS(ts->status)) { case CHILDERR_DUP: if (!ts->reported) - puts("can't dup file descriptors"); + puts("ABORTED (can't dup file descriptors)"); break; case CHILDERR_EXEC: if (!ts->reported) - puts("execution failed (not found?)"); + puts("ABORTED (execution failed -- not found?)"); break; case CHILDERR_STDERR: if (!ts->reported) - puts("can't open /dev/null"); + puts("ABORTED (can't open /dev/null)"); break; default: test_summarize(ts, WEXITSTATUS(ts->status)); @@ -561,17 +625,12 @@ test_run(struct testset *ts) int outfd, i, status; FILE *output; char buffer[BUFSIZ]; - char *file; /* * Initialize the test and our data structures, flagging this set in error * if the initialization fails. */ - file = xmalloc(strlen(ts->file) + 3); - strcpy(file, ts->file); - strcat(file, "-t"); - testpid = test_start(file, &outfd); - free(file); + testpid = test_start(ts->path, &outfd); output = fdopen(outfd, "r"); if (!output) { puts("ABORTED"); @@ -580,11 +639,8 @@ test_run(struct testset *ts) } if (!fgets(buffer, sizeof(buffer), output)) ts->aborted = 1; - if (!ts->aborted && !test_init(buffer, ts)) { - while (fgets(buffer, sizeof(buffer), output)) - ; + if (!ts->aborted && !test_init(buffer, ts)) ts->aborted = 1; - } /* Pass each line of output to test_checkline(). */ while (!ts->aborted && fgets(buffer, sizeof(buffer), output)) @@ -594,16 +650,23 @@ test_run(struct testset *ts) test_backspace(ts); /* - * Close the output descriptor, retrieve the exit status, and pass that - * information to test_analyze() for eventual output. + * Consume the rest of the test output, close the output descriptor, + * retrieve the exit status, and pass that information to test_analyze() + * for eventual output. */ + while (fgets(buffer, sizeof(buffer), output)) + ; fclose(output); child = waitpid(testpid, &ts->status, 0); if (child == (pid_t) -1) { - puts("ABORTED"); - fflush(stdout); + if (!ts->reported) { + puts("ABORTED"); + fflush(stdout); + } sysdie("waitpid for %u failed", (unsigned int) testpid); } + if (ts->all_skipped) + ts->aborted = 0; status = test_analyze(ts); /* Convert missing tests to failed tests. */ @@ -665,13 +728,54 @@ test_fail_summary(const struct testlist *fails) } +/* + * Given the name of a test, a pointer to the testset struct, and the source + * and build directories, find the test. We try first relative to the current + * directory, then in the build directory (if not NULL), then in the source + * directory. In each of those directories, we first try a "-t" extension and + * then a ".t" extension. When we find an executable program, we fill in the + * path member of the testset struct. If none of those paths are executable, + * just fill in the name of the test with "-t" appended. + * + * The caller is responsible for freeing the path member of the testset + * struct. + */ +static void +find_test(const char *name, struct testset *ts, const char *source, + const char *build) +{ + char *path; + const char *bases[] = { ".", build, source, NULL }; + int i; + + for (i = 0; bases[i] != NULL; i++) { + path = xmalloc(strlen(bases[i]) + strlen(name) + 4); + sprintf(path, "%s/%s-t", bases[i], name); + if (access(path, X_OK) != 0) + path[strlen(path) - 2] = '.'; + if (access(path, X_OK) == 0) + break; + free(path); + path = NULL; + } + if (path == NULL) { + path = xmalloc(strlen(name) + 3); + sprintf(path, "%s-t", name); + } + ts->path = path; +} + + /* * Run a batch of tests from a given file listing each test on a line by - * itself. The file must be rewindable. Returns true iff all tests + * itself. Takes two additional parameters: the root of the source directory + * and the root of the build directory. Test programs will be first searched + * for in the current directory, then the build directory, then the source + * directory. The file must be rewindable. Returns true iff all tests * passed. */ static int -test_batch(const char *testlist) +test_batch(const char *testlist, const char *source, const char *build) { FILE *tests; size_t length, i; @@ -741,7 +845,14 @@ test_batch(const char *testlist) fflush(stdout); memset(&ts, 0, sizeof(ts)); ts.file = xstrdup(buffer); - if (!test_run(&ts)) { + find_test(buffer, &ts, source, build); + ts.reason = NULL; + if (test_run(&ts)) { + free(ts.file); + free(ts.path); + if (ts.reason != NULL) + free(ts.reason); + } else { tmp = xmalloc(sizeof(struct testset)); memcpy(tmp, &ts, sizeof(struct testset)); if (!failhead) { @@ -757,9 +868,9 @@ test_batch(const char *testlist) } } aborted += ts.aborted; - total += ts.count; + total += ts.count + ts.all_skipped; passed += ts.passed; - skipped += ts.skipped; + skipped += ts.skipped + ts.all_skipped; failed += ts.failed; } total -= skipped; @@ -769,7 +880,8 @@ test_batch(const char *testlist) getrusage(RUSAGE_CHILDREN, &stats); /* Print out our final results. */ - if (failhead) test_fail_summary(failhead); + if (failhead) + test_fail_summary(failhead); putchar('\n'); if (aborted != 0) { if (aborted == 1) @@ -800,15 +912,80 @@ test_batch(const char *testlist) /* - * Main routine. Given a file listing tests, run each test listed. + * Run a single test case. This involves just running the test program after + * having done the environment setup and finding the test program. + */ +static void +test_single(const char *program, const char *source, const char *build) +{ + struct testset ts; + + memset(&ts, 0, sizeof(ts)); + find_test(program, &ts, source, build); + if (execl(ts.path, ts.path, (char *) 0) == -1) + sysdie("cannot exec %s", ts.path); +} + + +/* + * Main routine. Set the SOURCE and BUILD environment variables and then, + * given a file listing tests, run each test listed. */ int main(int argc, char *argv[]) { - if (argc != 2) { + int option; + int single = 0; + char *setting; + const char *list; + const char *source = SOURCE; + const char *build = BUILD; + + while ((option = getopt(argc, argv, "b:os:")) != EOF) { + switch (option) { + case 'b': + build = optarg; + break; + case 'o': + single = 1; + break; + case 's': + source = optarg; + break; + default: + exit(1); + } + } + argc -= optind; + argv += optind; + if (argc != 1) { fprintf(stderr, "Usage: runtests \n"); exit(1); } - printf(banner, argv[1]); - exit(test_batch(argv[1]) ? 0 : 1); + + if (source != NULL) { + setting = xmalloc(strlen("SOURCE=") + strlen(source) + 1); + sprintf(setting, "SOURCE=%s", source); + if (putenv(setting) != 0) + sysdie("cannot set SOURCE in the environment"); + } + if (build != NULL) { + setting = xmalloc(strlen("BUILD=") + strlen(build) + 1); + sprintf(setting, "BUILD=%s", build); + if (putenv(setting) != 0) + sysdie("cannot set BUILD in the environment"); + } + + if (single) { + test_single(argv[0], source, build); + exit(0); + } else { + list = strrchr(argv[0], '/'); + if (list == NULL) + list = argv[0]; + else + list++; + printf(banner, list); + exit(test_batch(argv[0], source, build) ? 0 : 1); + } } diff --git a/tests/tap/basic.c b/tests/tap/basic.c new file mode 100644 index 0000000..5ca9ff4 --- /dev/null +++ b/tests/tap/basic.c @@ -0,0 +1,356 @@ +/* + * Some utility routines for writing tests. + * + * Herein are a variety of utility routines for writing tests. All routines + * of the form ok*() take a test number and some number of appropriate + * arguments, check to be sure the results match the expected output using the + * arguments, and print out something appropriate for that test number. Other + * utility routines help in constructing more complex tests. + * + * Copyright 2009 Russ Allbery + * Copyright 2006, 2007, 2008 + * Board of Trustees, Leland Stanford Jr. University + * Copyright (c) 2004, 2005, 2006 + * by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002, 2003 by The Internet Software Consortium and Rich Salz + * + * See LICENSE for licensing terms. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * The test count. Always contains the number that will be used for the next + * test status. + */ +int testnum = 1; + +/* + * Status information stored so that we can give a test summary at the end of + * the test case. We store the planned final test and the count of failures. + * We can get the highest test count from testnum. + * + * We also store the PID of the process that called plan() and only summarize + * results when that process exits, so as to not misreport results in forked + * processes. + */ +static int _planned = 0; +static int _failed = 0; +static pid_t _process = 0; + + +/* + * Our exit handler. Called on completion of the test to report a summary of + * results provided we're still in the original process. + */ +static void +finish(void) +{ + int highest = testnum - 1; + + if (_process != 0 && getpid() == _process && _planned > 0) { + if (_planned > highest) + printf("# Looks like you planned %d test%s but only ran %d\n", + _planned, (_planned > 1 ? "s" : ""), highest); + else if (_planned < highest) + printf("# Looks like you planned %d test%s but ran %d extra\n", + _planned, (_planned > 1 ? "s" : ""), highest - _planned); + else if (_failed > 0) + printf("# Looks like you failed %d test%s of %d\n", _failed, + (_failed > 1 ? "s" : ""), _planned); + else if (_planned > 1) + printf("# All %d tests successful or skipped\n", _planned); + else + printf("# %d test successful or skipped\n", _planned); + } +} + + +/* + * Initialize things. Turns on line buffering on stdout and then prints out + * the number of tests in the test suite. + */ +void +plan(int count) +{ + if (setvbuf(stdout, NULL, _IOLBF, BUFSIZ) != 0) + fprintf(stderr, "# cannot set stdout to line buffered: %s\n", + strerror(errno)); + printf("1..%d\n", count); + testnum = 1; + _planned = count; + _process = getpid(); + atexit(finish); +} + + +/* + * Skip the entire test suite and exits. Should be called instead of plan(), + * not after it, since it prints out a special plan line. + */ +void +skip_all(const char *format, ...) +{ + printf("1..0 # skip"); + if (format != NULL) { + va_list args; + + putchar(' '); + va_start(args, format); + vprintf(format, args); + va_end(args); + } + putchar('\n'); + exit(0); +} + + +/* + * Print the test description. + */ +static void +print_desc(const char *format, va_list args) +{ + printf(" - "); + vprintf(format, args); +} + + +/* + * Takes a boolean success value and assumes the test passes if that value + * is true and fails if that value is false. + */ +void +ok(int success, const char *format, ...) +{ + printf("%sok %d", success ? "" : "not ", testnum++); + if (!success) + _failed++; + if (format != NULL) { + va_list args; + + va_start(args, format); + print_desc(format, args); + va_end(args); + } + putchar('\n'); +} + + +/* + * Skip a test. + */ +void +skip(const char *reason, ...) +{ + printf("ok %d # skip", testnum++); + if (reason != NULL) { + va_list args; + + va_start(args, reason); + putchar(' '); + vprintf(reason, args); + va_end(args); + } + putchar('\n'); +} + + +/* + * Report the same status on the next count tests. + */ +void +ok_block(int count, int status, const char *format, ...) +{ + int i; + + for (i = 0; i < count; i++) { + printf("%sok %d", status ? "" : "not ", testnum++); + if (!status) + _failed++; + if (format != NULL) { + va_list args; + + va_start(args, format); + print_desc(format, args); + va_end(args); + } + putchar('\n'); + } +} + + +/* + * Skip the next count tests. + */ +void +skip_block(int count, const char *reason, ...) +{ + int i; + + for (i = 0; i < count; i++) { + printf("ok %d # skip", testnum++); + if (reason != NULL) { + va_list args; + + va_start(args, reason); + putchar(' '); + vprintf(reason, args); + va_end(args); + } + putchar('\n'); + } +} + + +/* + * Takes an expected integer and a seen integer and assumes the test passes + * if those two numbers match. + */ +void +is_int(int wanted, int seen, const char *format, ...) +{ + if (wanted == seen) + printf("ok %d", testnum++); + else { + printf("# wanted: %d\n# seen: %d\n", wanted, seen); + printf("not ok %d", testnum++); + _failed++; + } + if (format != NULL) { + va_list args; + + va_start(args, format); + print_desc(format, args); + va_end(args); + } + putchar('\n'); +} + + +/* + * Takes a string and what the string should be, and assumes the test passes + * if those strings match (using strcmp). + */ +void +is_string(const char *wanted, const char *seen, const char *format, ...) +{ + if (wanted == NULL) + wanted = "(null)"; + if (seen == NULL) + seen = "(null)"; + if (strcmp(wanted, seen) == 0) + printf("ok %d", testnum++); + else { + printf("# wanted: %s\n# seen: %s\n", wanted, seen); + printf("not ok %d", testnum++); + _failed++; + } + if (format != NULL) { + va_list args; + + va_start(args, format); + print_desc(format, args); + va_end(args); + } + putchar('\n'); +} + + +/* + * Takes an expected double and a seen double and assumes the test passes if + * those two numbers match. + */ +void +is_double(double wanted, double seen, const char *format, ...) +{ + if (wanted == seen) + printf("ok %d", testnum++); + else { + printf("# wanted: %g\n# seen: %g\n", wanted, seen); + printf("not ok %d", testnum++); + _failed++; + } + if (format != NULL) { + va_list args; + + va_start(args, format); + print_desc(format, args); + va_end(args); + } + putchar('\n'); +} + + +/* + * Takes an expected unsigned long and a seen unsigned long and assumes the + * test passes if the two numbers match. Otherwise, reports them in hex. + */ +void +is_hex(unsigned long wanted, unsigned long seen, const char *format, ...) +{ + if (wanted == seen) + printf("ok %d", testnum++); + else { + printf("# wanted: %lx\n# seen: %lx\n", (unsigned long) wanted, + (unsigned long) seen); + printf("not ok %d", testnum++); + _failed++; + } + if (format != NULL) { + va_list args; + + va_start(args, format); + print_desc(format, args); + va_end(args); + } + putchar('\n'); +} + + +/* + * Bail out with an error. + */ +void +bail(const char *format, ...) +{ + va_list args; + + fflush(stdout); + printf("Bail out! "); + va_start(args, format); + vprintf(format, args); + va_end(args); + printf("\n"); + exit(1); +} + + +/* + * Bail out with an error, appending strerror(errno). + */ +void +sysbail(const char *format, ...) +{ + va_list args; + int oerrno = errno; + + fflush(stdout); + printf("Bail out! "); + va_start(args, format); + vprintf(format, args); + va_end(args); + printf(": %s\n", strerror(oerrno)); + exit(1); +} diff --git a/tests/tap/basic.h b/tests/tap/basic.h new file mode 100644 index 0000000..efe94ba --- /dev/null +++ b/tests/tap/basic.h @@ -0,0 +1,98 @@ +/* + * Basic utility routines for the TAP protocol. + * + * Copyright 2006, 2007, 2008 + * Board of Trustees, Leland Stanford Jr. University + * Copyright (c) 2004, 2005, 2006 + * by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002, 2003 by The Internet Software Consortium and Rich Salz + * + * See LICENSE for licensing terms. + */ + +#ifndef TAP_BASIC_H +#define TAP_BASIC_H 1 + +#include /* pid_t */ + +/* + * __attribute__ is available in gcc 2.5 and later, but only with gcc 2.7 + * could you use the __format__ form of the attributes, which is what we use + * (to avoid confusion with other macros). + */ +#ifndef __attribute__ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) +# define __attribute__(spec) /* empty */ +# endif +#endif + +/* + * BEGIN_DECLS is used at the beginning of declarations so that C++ + * compilers don't mangle their names. END_DECLS is used at the end. + */ +#undef BEGIN_DECLS +#undef END_DECLS +#ifdef __cplusplus +# define BEGIN_DECLS extern "C" { +# define END_DECLS } +#else +# define BEGIN_DECLS /* empty */ +# define END_DECLS /* empty */ +#endif + +/* + * Used for iterating through arrays. ARRAY_SIZE returns the number of + * elements in the array (useful for a < upper bound in a for loop) and + * ARRAY_END returns a pointer to the element past the end (ISO C99 makes it + * legal to refer to such a pointer as long as it's never dereferenced). + */ +#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) +#define ARRAY_END(array) (&(array)[ARRAY_SIZE(array)]) + +BEGIN_DECLS + +/* + * The test count. Always contains the number that will be used for the next + * test status. + */ +extern int testnum; + +/* Print out the number of tests and set standard output to line buffered. */ +void plan(int count); + +/* Skip the entire test suite. Call instead of plan. */ +void skip_all(const char *format, ...) + __attribute__((__noreturn__, __format__(printf, 1, 2))); + +/* Basic reporting functions. */ +void ok(int success, const char *format, ...) + __attribute__((__format__(printf, 2, 3))); +void skip(const char *reason, ...) + __attribute__((__format__(printf, 1, 2))); + +/* Report the same status on, or skip, the next count tests. */ +void ok_block(int count, int success, const char *format, ...) + __attribute__((__format__(printf, 3, 4))); +void skip_block(int count, const char *reason, ...) + __attribute__((__format__(printf, 2, 3))); + +/* Check an expected value against a seen value. */ +void is_int(int wanted, int seen, const char *format, ...) + __attribute__((__format__(printf, 3, 4))); +void is_double(double wanted, double seen, const char *format, ...) + __attribute__((__format__(printf, 3, 4))); +void is_string(const char *wanted, const char *seen, const char *format, ...) + __attribute__((__format__(printf, 3, 4))); +void is_hex(unsigned long wanted, unsigned long seen, const char *format, ...) + __attribute__((__format__(printf, 3, 4))); + +/* Bail out with an error. sysbail appends strerror(errno). */ +void bail(const char *format, ...) + __attribute__((__noreturn__, __nonnull__, __format__(printf, 1, 2))); +void sysbail(const char *format, ...) + __attribute__((__noreturn__, __nonnull__, __format__(printf, 1, 2))); + +END_DECLS + +#endif /* LIBTEST_H */ diff --git a/tests/tap/kerberos.c b/tests/tap/kerberos.c new file mode 100644 index 0000000..700212e --- /dev/null +++ b/tests/tap/kerberos.c @@ -0,0 +1,164 @@ +/* + * Utility functions for tests that use Kerberos. + * + * Currently only provides kerberos_setup(), which assumes a particular set of + * data files in either the SOURCE or BUILD directories and, using those, + * obtains Kerberos credentials, sets up a ticket cache, and sets the + * environment variable pointing to the Kerberos keytab to use for testing. + * + * Copyright 2006, 2007, 2009, 2010 + * Board of Trustees, Leland Stanford Jr. University + * + * See LICENSE for licensing terms. + */ + +#include +#include +#include + +#include +#include +#include +#include + + +/* + * Given the partial path to a file, look under BUILD and then SOURCE for the + * file and return the full path to the file in newly-allocated memory. + * Returns NULL if the file doesn't exist. + */ +static char * +find_file(const char *file) +{ + char *base; + char *path = NULL; + const char *envs[] = { "BUILD", "SOURCE", NULL }; + int i; + + for (i = 0; envs[i] != NULL; i++) { + base = getenv(envs[i]); + if (base == NULL) + continue; + path = concatpath(base, file); + if (access(path, R_OK) == 0) + break; + free(path); + path = NULL; + } + return path; +} + + +/* + * Obtain Kerberos tickets for the principal specified in test.principal using + * the keytab specified in test.keytab, both of which are presumed to be in + * tests/data in either the build or the source tree. + * + * Returns the contents of test.principal in newly allocated memory or NULL if + * Kerberos tests are apparently not configured. If Kerberos tests are + * configured but something else fails, calls bail(). + * + * The error handling here is not great. We should have a bail_krb5 that uses + * the same logic as messages-krb5.c, which hasn't yet been imported into + * rra-c-util. + */ +char * +kerberos_setup(void) +{ + char *path, *krbtgt; + const char *build, *realm; + FILE *file; + char principal[BUFSIZ]; + krb5_error_code code; + krb5_context ctx; + krb5_ccache ccache; + krb5_principal kprinc; + krb5_keytab keytab; + krb5_get_init_creds_opt *opts; + krb5_creds creds; + + /* Read the principal name and find the keytab file. */ + path = find_file("data/test.principal"); + if (path == NULL) + return NULL; + file = fopen(path, "r"); + if (file == NULL) { + free(path); + return NULL; + } + if (fgets(principal, sizeof(principal), file) == NULL) { + fclose(file); + bail("cannot read %s", path); + } + fclose(file); + if (principal[strlen(principal) - 1] != '\n') + bail("no newline in %s", path); + free(path); + principal[strlen(principal) - 1] = '\0'; + path = find_file("data/test.keytab"); + if (path == NULL) + return NULL; + + /* Set the KRB5CCNAME and KRB5_KTNAME environment variables. */ + build = getenv("BUILD"); + if (build == NULL) + build = "."; + putenv(concat("KRB5CCNAME=", build, "/data/test.cache", (char *) 0)); + putenv(concat("KRB5_KTNAME=", path, (char *) 0)); + + /* Now do the Kerberos initialization. */ + code = krb5_init_context(&ctx); + if (code != 0) + bail("error initializing Kerberos"); + code = krb5_cc_default(ctx, &ccache); + if (code != 0) + bail("error setting ticket cache"); + code = krb5_parse_name(ctx, principal, &kprinc); + if (code != 0) + bail("error parsing principal %s", principal); + realm = krb5_principal_get_realm(ctx, kprinc); + krbtgt = concat("krbtgt/", realm, "@", realm, (char *) 0); + code = krb5_kt_resolve(ctx, path, &keytab); + if (code != 0) + bail("cannot open keytab %s", path); + code = krb5_get_init_creds_opt_alloc(ctx, &opts); + if (code != 0) + bail("cannot allocate credential options"); + krb5_get_init_creds_opt_set_default_flags(ctx, NULL, realm, opts); + krb5_get_init_creds_opt_set_forwardable(opts, 0); + krb5_get_init_creds_opt_set_proxiable(opts, 0); + code = krb5_get_init_creds_keytab(ctx, &creds, kprinc, keytab, 0, krbtgt, + opts); + if (code != 0) + bail("cannot get Kerberos tickets"); + code = krb5_cc_initialize(ctx, ccache, kprinc); + if (code != 0) + bail("error initializing ticket cache"); + code = krb5_cc_store_cred(ctx, ccache, &creds); + if (code != 0) + bail("error storing credentials"); + krb5_cc_close(ctx, ccache); + krb5_free_cred_contents(ctx, &creds); + krb5_kt_close(ctx, keytab); + krb5_free_principal(ctx, kprinc); + krb5_free_context(ctx); + free(krbtgt); + free(path); + + return xstrdup(principal); +} + + +/* + * Clean up at the end of a test. Currently, all this does is remove the + * ticket cache. + */ +void +kerberos_cleanup(void) +{ + char *path; + + path = concatpath(getenv("BUILD"), "data/test.cache"); + unlink(path); + free(path); +} diff --git a/tests/tap/kerberos.h b/tests/tap/kerberos.h new file mode 100644 index 0000000..1c64f70 --- /dev/null +++ b/tests/tap/kerberos.h @@ -0,0 +1,32 @@ +/* + * Utility functions for tests that use Kerberos. + * + * Copyright 2006, 2007, 2009 + * Board of Trustees, Leland Stanford Jr. University + * + * See LICENSE for licensing terms. + */ + +#ifndef TAP_KERBEROS_H +#define TAP_KERBEROS_H 1 + +#include +#include + +BEGIN_DECLS + +/* + * Set up Kerberos, returning the test principal in newly allocated memory if + * we were successful. If there is no principal in tests/data/test.principal + * or no keytab in tests/data/test.keytab, return NULL. Otherwise, on + * failure, calls bail(). + */ +char *kerberos_setup(void) + __attribute__((__malloc__)); + +/* Clean up at the end of a test. */ +void kerberos_cleanup(void); + +END_DECLS + +#endif /* !TAP_MESSAGES_H */ diff --git a/tests/tap/kerberos.sh b/tests/tap/kerberos.sh new file mode 100644 index 0000000..da07e66 --- /dev/null +++ b/tests/tap/kerberos.sh @@ -0,0 +1,48 @@ +# Shell function library to initialize Kerberos credentials +# +# Written by Russ Allbery +# Copyright 2009 Board of Trustees, Leland Stanford Jr. University +# +# See LICENSE for licensing terms. + +# Set up Kerberos, including the ticket cache environment variable. Bail out +# if not successful, return 0 if successful, and return 1 if Kerberos is not +# configured. Sets the global principal variable to the principal to use. +kerberos_setup () { + local keytab + keytab='' + for f in "$BUILD/data/test.keytab" "$SOURCE/data/test.keytab" ; do + if [ -r "$f" ] ; then + keytab="$f" + fi + done + principal='' + for f in "$BUILD/data/test.principal" "$SOURCE/data/test.principal" ; do + if [ -r "$f" ] ; then + principal=`cat "$BUILD/data/test.principal"` + fi + done + if [ -z "$keytab" ] || [ -z "$principal" ] ; then + return 1 + fi + KRB5CCNAME="$BUILD/data/test.cache"; export KRB5CCNAME + kinit -k -t "$keytab" "$principal" >/dev/null /dev/null /dev/null +# Copyright 2009 Russ Allbery +# Copyright 2006, 2007, 2008 Board of Trustees, Leland Stanford Jr. University +# +# See LICENSE for licensing terms. + +# Print out the number of test cases we expect to run. +plan () { + count=1 + planned="$1" + failed=0 + echo "1..$1" + trap finish 0 +} + +# Report the test status on exit. +finish () { + local highest looks + highest=`expr "$count" - 1` + looks='# Looks like you' + if [ "$planned" -gt 0 ] ; then + if [ "$planned" -gt "$highest" ] ; then + if [ "$planned" -gt 1 ] ; then + echo "$looks planned $planned tests but only ran $highest" + else + echo "$looks planned $planned test but only ran $highest" + fi + elif [ "$planned" -lt "$highest" ] ; then + local extra + extra=`expr "$highest" - "$planned"` + if [ "$planned" -gt 1 ] ; then + echo "$looks planned $planned tests but ran $extra extra" + else + echo "$looks planned $planned test but ran $extra extra" + fi + elif [ "$failed" -gt 0 ] ; then + if [ "$failed" -gt 1 ] ; then + echo "$looks failed $failed tests of $planned" + else + echo "$looks failed $failed test of $planned" + fi + elif [ "$planned" -gt 1 ] ; then + echo "# All $planned tests successful or skipped" + else + echo "# $planned test successful or skipped" + fi + fi +} + +# Skip the entire test suite. Should be run instead of plan. +skip_all () { + local desc + desc="$1" + if [ -n "$desc" ] ; then + echo "1..0 # skip $desc" + else + echo "1..0 # skip" + fi + exit 0 +} + +# ok takes a test description and a command to run and prints success if that +# command is successful, false otherwise. The count starts at 1 and is +# updated each time ok is printed. +ok () { + local desc + desc="$1" + if [ -n "$desc" ] ; then + desc=" - $desc" + fi + shift + if "$@" ; then + echo ok $count$desc + else + echo not ok $count$desc + failed=`expr $failed + 1` + fi + count=`expr $count + 1` +} + +# Skip the next test. Takes the reason why the test is skipped. +skip () { + echo "ok $count # skip $*" + count=`expr $count + 1` +} + +# Report the same status on a whole set of tests. Takes the count of tests, +# the description, and then the command to run to determine the status. +ok_block () { + local end i desc + i=$count + end=`expr $count + $1` + shift + desc="$1" + shift + while [ "$i" -lt "$end" ] ; do + ok "$desc" "$@" + i=`expr $i + 1` + done +} + +# Skip a whole set of tests. Takes the count and then the reason for skipping +# the test. +skip_block () { + local i end + i=$count + end=`expr $count + $1` + shift + while [ "$i" -lt "$end" ] ; do + skip "$@" + i=`expr $i + 1` + done +} + +# Run a program expected to succeed, and print ok if it does and produces the +# correct output. Takes the description, expected exit status, the expected +# output, the command to run, and then any arguments for that command. Strip +# a colon and everything after it off the output if the expected status is +# non-zero, since this is probably a system-specific error message. +ok_program () { + local desc w_status w_output output status + desc="$1" + shift + w_status="$1" + shift + w_output="$1" + shift + output=`"$@" 2>&1` + status=$? + if [ "$w_status" -ne 0 ] ; then + output=`echo "$output" | sed 's/^\([^:]* [^:]*\):.*/\1/'` + fi + if [ $status = $w_status ] && [ x"$output" = x"$w_output" ] ; then + ok "$desc" true + else + echo "# saw: ($status) $output" + echo "# not: ($w_status) $w_output" + ok "$desc" false + fi +} + +# Bail out with an error message. +bail () { + echo 'Bail out!' "$@" + exit 1 +} diff --git a/tests/tap/messages.c b/tests/tap/messages.c new file mode 100644 index 0000000..3bb9a1a --- /dev/null +++ b/tests/tap/messages.c @@ -0,0 +1,80 @@ +/* + * Utility functions to test message handling. + * + * These functions set up a message handler to trap warn and notice output + * into a buffer that can be inspected later, allowing testing of error + * handling. + * + * Copyright 2006, 2007, 2009 + * Board of Trustees, Leland Stanford Jr. University + * Copyright (c) 2004, 2005, 2006 + * by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002, 2003 by The Internet Software Consortium and Rich Salz + * + * See LICENSE for licensing terms. + */ + +#include +#include + +#include +#include +#include +#include +#include + +/* A global buffer into which message_log_buffer stores error messages. */ +char *errors = NULL; + + +/* + * An error handler that appends all errors to the errors global. Used by + * error_capture. + */ +static void +message_log_buffer(int len, const char *fmt, va_list args, int error UNUSED) +{ + char *message; + + message = xmalloc(len + 1); + vsnprintf(message, len + 1, fmt, args); + if (errors == NULL) { + errors = concat(message, "\n", (char *) 0); + } else { + char *new_errors; + + new_errors = concat(errors, message, "\n", (char *) 0); + free(errors); + errors = new_errors; + } + free(message); +} + + +/* + * Turn on the capturing of errors. Errors will be stored in the global + * errors variable where they can be checked by the test suite. Capturing is + * turned off with errors_uncapture. + */ +void +errors_capture(void) +{ + if (errors != NULL) { + free(errors); + errors = NULL; + } + message_handlers_warn(1, message_log_buffer); + message_handlers_notice(1, message_log_buffer); +} + + +/* + * Turn off the capturing of errors again. + */ +void +errors_uncapture(void) +{ + message_handlers_warn(1, message_log_stderr); + message_handlers_notice(1, message_log_stdout); +} diff --git a/tests/tap/messages.h b/tests/tap/messages.h new file mode 100644 index 0000000..2b9a7db --- /dev/null +++ b/tests/tap/messages.h @@ -0,0 +1,35 @@ +/* + * Utility functions to test message handling. + * + * Copyright 2006, 2007, 2009 + * Board of Trustees, Leland Stanford Jr. University + * Copyright (c) 2004, 2005, 2006 + * by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002, 2003 by The Internet Software Consortium and Rich Salz + * + * See LICENSE for licensing terms. + */ + +#ifndef TAP_MESSAGES_H +#define TAP_MESSAGES_H 1 + +#include +#include + +/* A global buffer into which errors_capture stores errors. */ +extern char *errors; + +BEGIN_DECLS + +/* + * Turn on capturing of errors with errors_capture. Errors reported by warn + * will be stored in the global errors variable. Turn this off again with + * errors_uncapture. Caller is responsible for freeing errors when done. + */ +void errors_capture(void); +void errors_uncapture(void); + +END_DECLS + +#endif /* !TAP_MESSAGES_H */ diff --git a/tests/tap/process.c b/tests/tap/process.c new file mode 100644 index 0000000..16154c7 --- /dev/null +++ b/tests/tap/process.c @@ -0,0 +1,100 @@ +/* + * Utility functions for tests that use subprocesses. + * + * Provides utility functions for subprocess manipulation. Currently, only + * one utility function is provided: is_function_output, which runs a function + * in a subprocess and checks its output and exit status against expected + * values. + * + * Copyright 2009, 2010 Board of Trustees, Leland Stanford Jr. University + * Copyright (c) 2004, 2005, 2006 + * by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002, 2003 by The Internet Software Consortium and Rich Salz + * + * See LICENSE for licensing terms. + */ + +#include +#include + +#include + +#include +#include +#include + + +/* + * Given a function, an expected exit status, and expected output, runs that + * function in a subprocess, capturing stdout and stderr via a pipe, and + * compare the combination of stdout and stderr with the expected output and + * the exit status with the expected status. Expects the function to always + * exit (not die from a signal). + */ +void +is_function_output(test_function_type function, int status, const char *output, + const char *format, ...) +{ + int fds[2]; + pid_t child; + char *buf, *msg; + ssize_t count, ret, buflen; + int rval; + va_list args; + + /* Flush stdout before we start to avoid odd forking issues. */ + fflush(stdout); + + /* Set up the pipe and call the function, collecting its output. */ + if (pipe(fds) == -1) + sysbail("can't create pipe"); + child = fork(); + if (child == (pid_t) -1) { + sysbail("can't fork"); + } else if (child == 0) { + /* In child. Set up our stdout and stderr. */ + close(fds[0]); + if (dup2(fds[1], 1) == -1) + _exit(255); + if (dup2(fds[1], 2) == -1) + _exit(255); + + /* Now, run the function and exit successfully if it returns. */ + (*function)(); + fflush(stdout); + _exit(0); + } else { + /* + * In the parent; close the extra file descriptor, read the output if + * any, and then collect the exit status. + */ + close(fds[1]); + buflen = BUFSIZ; + buf = xmalloc(buflen); + count = 0; + do { + ret = read(fds[0], buf + count, buflen - count - 1); + if (ret > 0) + count += ret; + if (count >= buflen - 1) { + buflen += BUFSIZ; + buf = xrealloc(buf, buflen); + } + } while (ret > 0); + buf[count < 0 ? 0 : count] = '\0'; + if (waitpid(child, &rval, 0) == (pid_t) -1) + sysbail("waitpid failed"); + } + + /* Now, check the results against what we expected. */ + va_start(args, format); + if (xvasprintf(&msg, format, args) < 0) + bail("cannot format test description"); + va_end(args); + ok(WIFEXITED(rval), "%s (exited)", msg); + is_int(status, WEXITSTATUS(rval), "%s (status)", msg); + is_string(output, buf, "%s (output)", msg); + free(buf); + free(msg); +} diff --git a/tests/tap/process.h b/tests/tap/process.h new file mode 100644 index 0000000..b7d3b11 --- /dev/null +++ b/tests/tap/process.h @@ -0,0 +1,37 @@ +/* + * Utility functions for tests that use subprocesses. + * + * Copyright 2009, 2010 Board of Trustees, Leland Stanford Jr. University + * Copyright (c) 2004, 2005, 2006 + * by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002, 2003 by The Internet Software Consortium and Rich Salz + * + * See LICENSE for licensing terms. + */ + +#ifndef TAP_PROCESS_H +#define TAP_PROCESS_H 1 + +#include +#include + +BEGIN_DECLS + +/* + * Run a function in a subprocess and check the exit status and expected + * output (stdout and stderr combined) against the provided values. Expects + * the function to always exit (not die from a signal). + * + * This reports as three separate tests: whether the function exited rather + * than was killed, whether the exit status was correct, and whether the + * output was correct. + */ +typedef void (*test_function_type)(void); +void is_function_output(test_function_type, int status, const char *output, + const char *format, ...) + __attribute__((__format__(printf, 4, 5))); + +END_DECLS + +#endif /* TAP_PROCESS_H */ diff --git a/tests/tap/remctl.sh b/tests/tap/remctl.sh new file mode 100644 index 0000000..b9667ef --- /dev/null +++ b/tests/tap/remctl.sh @@ -0,0 +1,46 @@ +# Shell function library to start and stop remctld +# +# Written by Russ Allbery +# Copyright 2009 Board of Trustees, Leland Stanford Jr. University +# +# See LICENSE for licensing terms. + +# Start remctld. Takes the path to remctld, which may be found via configure, +# and the path to the configuration file. +remctld_start () { + local keytab principal + rm -f "$BUILD/data/remctld.pid" + keytab='' + for f in "$BUILD/data/test.keytab" "$SOURCE/data/test.keytab" ; do + if [ -r "$f" ] ; then + keytab="$f" + fi + done + principal='' + for f in "$BUILD/data/test.principal" "$SOURCE/data/test.principal" ; do + if [ -r "$f" ] ; then + principal=`cat "$BUILD/data/test.principal"` + fi + done + if [ -n "$VALGRIND" ] ; then + ( "$VALGRIND" --log-file=valgrind.%p --leak-check=full "$1" -m \ + -p 14373 -s "$principal" -P "$BUILD/data/remctld.pid" -f "$2" -d \ + -S -F -k "$keytab" &) + [ -f "$BUILD/data/remctld.pid" ] || sleep 5 + else + ( "$1" -m -p 14373 -s "$principal" -P "$BUILD/data/remctld.pid" \ + -f "$2" -d -S -F -k "$keytab" &) + fi + [ -f "$BUILD/data/remctld.pid" ] || sleep 1 + if [ ! -f "$BUILD/data/remctld.pid" ] ; then + bail 'remctld did not start' + fi +} + +# Stop remctld and clean up. +remctld_stop () { + if [ -f "$BUILD/data/remctld.pid" ] ; then + kill -TERM `cat "$BUILD/data/remctld.pid"` + rm -f "$BUILD/data/remctld.pid" + fi +} diff --git a/tests/util/concat-t.c b/tests/util/concat-t.c index 81824c8..ca7de2c 100644 --- a/tests/util/concat-t.c +++ b/tests/util/concat-t.c @@ -1,58 +1,46 @@ /* * concat test suite. * - * Copyright 2004, 2005, 2006 + * Written by Russ Allbery + * Copyright 2009 Board of Trustees, Leland Stanford Jr. University + * Copyright (c) 2004, 2005, 2006 * by Internet Systems Consortium, Inc. ("ISC") - * Copyright 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, - * 2003 by The Internet Software Consortium and Rich Salz + * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002, 2003 by The Internet Software Consortium and Rich Salz * - * This code is derived from software contributed to the Internet Software - * Consortium by Rich Salz. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. + * See LICENSE for licensing terms. */ #include #include -#include -#include +#include +#include #define END (char *) 0 - /* * Memory leaks everywhere! Whoo-hoo! */ int main(void) { - test_init(13); - - ok_string( 1, "a", concat("a", END)); - ok_string( 2, "ab", concat("a", "b", END)); - ok_string( 3, "ab", concat("ab", "", END)); - ok_string( 4, "ab", concat("", "ab", END)); - ok_string( 5, "", concat("", END)); - ok_string( 6, "abcde", concat("ab", "c", "", "de", END)); - ok_string( 7, "abcde", concat("abc", "de", END, "f", END)); - - ok_string( 8, "/foo", concatpath("/bar", "/foo")); - ok_string( 9, "/foo/bar", concatpath("/foo", "bar")); - ok_string(10, "./bar", concatpath("/foo", "./bar")); - ok_string(11, "/bar/baz/foo/bar", concatpath("/bar/baz", "foo/bar")); - ok_string(12, "./foo", concatpath(NULL, "foo")); - ok_string(13, "/foo/bar", concatpath(NULL, "/foo/bar")); + plan(13); + + is_string("a", concat("a", END), "concat 1"); + is_string("ab", concat("a", "b", END), "concat 2"); + is_string("ab", concat("ab", "", END), "concat 3"); + is_string("ab", concat("", "ab", END), "concat 4"); + is_string("", concat("", END), "concat 5"); + is_string("abcde", concat("ab", "c", "", "de", END), "concat 6"); + is_string("abcde", concat("abc", "de", END, "f", END), "concat 7"); + + is_string("/foo", concatpath("/bar", "/foo"), "path 1"); + is_string("/foo/bar", concatpath("/foo", "bar"), "path 2"); + is_string("./bar", concatpath("/foo", "./bar"), "path 3"); + is_string("/bar/baz/foo/bar", concatpath("/bar/baz", "foo/bar"), "path 4"); + is_string("./foo", concatpath(NULL, "foo"), "path 5"); + is_string("/foo/bar", concatpath(NULL, "/foo/bar"), "path 6"); return 0; } diff --git a/tests/util/messages-krb5-t.c b/tests/util/messages-krb5-t.c new file mode 100644 index 0000000..02d8f92 --- /dev/null +++ b/tests/util/messages-krb5-t.c @@ -0,0 +1,99 @@ +/* + * Test suite for Kerberos error handling routines. + * + * Written by Russ Allbery + * Copyright 2010 Board of Trustees, Leland Stanford Jr. University + * + * See LICENSE for licensing terms. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + + +/* + * Test functions. + */ +static void +test_warn(void) +{ + krb5_context ctx; + krb5_error_code code; + krb5_principal princ; + + code = krb5_init_context(&ctx); + if (code < 0) + die_krb5(ctx, code, "cannot create context"); + code = krb5_parse_name(ctx, "foo@bar@EXAMPLE.COM", &princ); + if (code < 0) + warn_krb5(ctx, code, "principal parse failed"); + else + die("unexpected success parsing principal"); + exit(0); +} + +static void +test_die(void) +{ + krb5_context ctx; + krb5_error_code code; + krb5_principal princ; + + code = krb5_init_context(&ctx); + if (code < 0) + die_krb5(ctx, code, "cannot create context"); + code = krb5_parse_name(ctx, "foo@bar@EXAMPLE.COM", &princ); + if (code < 0) + die_krb5(ctx, code, "principal parse failed"); + else + die("unexpected success parsing principal"); + exit(0); +} + + +/* + * Run the tests. + */ +int +main(void) +{ + krb5_context ctx; + krb5_error_code code; + krb5_principal princ; + const char *message; + char *wanted; + + plan(6 * 3); + + /* First, we have to get what the correct error message is. */ + code = krb5_init_context(&ctx); + if (code < 0) + bail("cannot create context"); + code = krb5_parse_name(ctx, "foo@bar@EXAMPLE.COM", &princ); + message = krb5_get_error_message(ctx, code); + + xasprintf(&wanted, "principal parse failed: %s\n", message); + is_function_output(test_warn, 0, wanted, "warn_krb5"); + is_function_output(test_die, 1, wanted, "die_krb5"); + free(wanted); + + message_program_name = "msg-test"; + xasprintf(&wanted, "msg-test: principal parse failed: %s\n", message); + is_function_output(test_warn, 0, wanted, "warn_krb5 with name"); + is_function_output(test_die, 1, wanted, "die_krb5 with name"); + free(wanted); + + message_handlers_warn(0); + is_function_output(test_warn, 0, "", "warn_krb5 with no handlers"); + message_handlers_die(0); + is_function_output(test_die, 1, "", "warn_krb5 with no handlers"); + + return 0; +} diff --git a/tests/util/messages-t.c b/tests/util/messages-t.c index 3f7860e..fb82a42 100644 --- a/tests/util/messages-t.c +++ b/tests/util/messages-t.c @@ -1,25 +1,14 @@ /* * Test suite for error handling routines. * - * Copyright 2004, 2005, 2006 + * Written by Russ Allbery + * Copyright 2009, 2010 Board of Trustees, Leland Stanford Jr. University + * Copyright (c) 2004, 2005, 2006 * by Internet Systems Consortium, Inc. ("ISC") - * Copyright 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, - * 2003 by The Internet Software Consortium and Rich Salz + * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002, 2003 by The Internet Software Consortium and Rich Salz * - * This code is derived from software contributed to the Internet Software - * Consortium by Rich Salz. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. + * See LICENSE for licensing terms. */ #include @@ -30,67 +19,11 @@ #include #include -#include -#include - -#define END (char *) 0 - -/* Test function type. */ -typedef void (*test_function_t)(void); - - -/* - * Fork and execute the provided function, connecting stdout and stderr to a - * pipe. Captures the output into the provided buffer and returns the exit - * status as a waitpid status value. - */ -static int -run_test(test_function_t function, char *buf, size_t buflen) -{ - int fds[2]; - pid_t child; - ssize_t count, status; - int rval; - - /* Flush stdout before we start to avoid odd forking issues. */ - fflush(stdout); - - /* Set up the pipe and call the function, collecting its output. */ - if (pipe(fds) == -1) - sysdie("can't create pipe"); - child = fork(); - if (child == (pid_t) -1) { - sysdie("can't fork"); - } else if (child == 0) { - /* In child. Set up our stdout and stderr. */ - close(fds[0]); - if (dup2(fds[1], 1) == -1) - _exit(255); - if (dup2(fds[1], 2) == -1) - _exit(255); - - /* Now, run the function and exit successfully if it returns. */ - (*function)(); - fflush(stdout); - _exit(0); - } else { - /* - * In the parent; close the extra file descriptor, read the output if - * any, and then collect the exit status. - */ - close(fds[1]); - count = 0; - do { - status = read(fds[0], buf + count, buflen - count - 1); - if (status > 0) - count += status; - } while (status > 0); - buf[count < 0 ? 0 : count] = '\0'; - if (waitpid(child, &rval, 0) == (pid_t) -1) - sysdie("waitpid failed"); - } - return rval; -} +#include +#include +#include +#include +#include /* @@ -203,43 +136,20 @@ static void test24(void) { /* - * Given the test number, intended exit status and message, and the function - * to run, print ok or not ok. - */ -static void -test_error(int n, int status, const char *output, test_function_t function) -{ - int real_status; - char buf[256]; - int succeeded = 1; - - real_status = run_test(function, buf, sizeof(buf)); - if (!WIFEXITED(real_status) || status != WEXITSTATUS(real_status)) { - printf(" unexpected exit status %d\n", real_status); - succeeded = 0; - } - if (strcmp(output, buf)) { - printf(" unexpected output: %s", buf); - printf(" expected output: %s", output); - succeeded = 0; - } - printf("%sok %d\n", succeeded ? "" : "not ", n); -} - - -/* - * Given the test number, intended status, intended message sans the appended - * strerror output, errno, and the function to run, print ok or not ok. + * Given the intended status, intended message sans the appended strerror + * output, errno, and the function to run, check the output. */ static void -test_strerror(int n, int status, const char *output, int error, - test_function_t function) +test_strerror(int status, const char *output, int error, + test_function_type function) { - char *full_output; + char *full_output, *name; - full_output = concat(output, ": ", strerror(error), "\n", END); - test_error(n, status, full_output, function); + full_output = concat(output, ": ", strerror(error), "\n", (char *) NULL); + xasprintf(&name, "strerror %d", testnum / 3 + 1); + is_function_output(function, status, full_output, name); free(full_output); + free(name); } @@ -250,46 +160,47 @@ int main(void) { char buff[32]; - - test_init(24); - - test_error(1, 0, "warning\n", test1); - test_error(2, 1, "fatal\n", test2); - test_strerror(3, 0, "permissions", EPERM, test3); - test_strerror(4, 1, "fatal access", EACCES, test4); - test_error(5, 0, "test5: warning\n", test5); - test_error(6, 1, "test6: fatal\n", test6); - test_strerror(7, 0, "test7: perms 7", EPERM, test7); - test_strerror(8, 1, "test8: fatal", EACCES, test8); - test_error(9, 10, "fatal\n", test9); - test_strerror(10, 10, "fatal perm", EPERM, test10); - test_strerror(11, 10, "1st test11: fatal", EPERM, test11); - test_error(12, 0, "7 0 warning\n", test12); - test_error(13, 1, "5 0 fatal\n", test13); + char *output; + + plan(24 * 3); + + is_function_output(test1, 0, "warning\n", "test1"); + is_function_output(test2, 1, "fatal\n", "test2"); + test_strerror(0, "permissions", EPERM, test3); + test_strerror(1, "fatal access", EACCES, test4); + is_function_output(test5, 0, "test5: warning\n", "test5"); + is_function_output(test6, 1, "test6: fatal\n", "test6"); + test_strerror(0, "test7: perms 7", EPERM, test7); + test_strerror(1, "test8: fatal", EACCES, test8); + is_function_output(test9, 10, "fatal\n", "test9"); + test_strerror(10, "fatal perm", EPERM, test10); + test_strerror(10, "1st test11: fatal", EPERM, test11); + is_function_output(test12, 0, "7 0 warning\n", "test12"); + is_function_output(test13, 1, "5 0 fatal\n", "test13"); sprintf(buff, "%d", EPERM); - test_error(14, 0, - concat("7 ", buff, " warning\n7 ", buff, " warning\n", END), - test14); - test_error(15, 10, - concat("5 ", buff, " fatal\n5 ", buff, " fatal\n", END), - test15); - test_error(16, 0, - concat("test16: warning: ", strerror(EPERM), "\n7 ", buff, - " warning\n", END), - test16); - - test_error(17, 0, "notice\n", test17); - test_error(18, 0, "test18: notice\n", test18); - test_error(19, 0, "", test19); - test_error(20, 0, "3 0 foo\n", test20); - test_error(21, 0, "test23: baz\n", test21); + xasprintf(&output, "7 %d warning\n7 %d warning\n", EPERM, EPERM); + is_function_output(test14, 0, output, "test14"); + free(output); + xasprintf(&output, "5 %d fatal\n5 %d fatal\n", EPERM, EPERM); + is_function_output(test15, 10, output, "test15"); + free(output); + xasprintf(&output, "test16: warning: %s\n7 %d warning\n", strerror(EPERM), + EPERM); + is_function_output(test16, 0, output, "test16"); + free(output); + + is_function_output(test17, 0, "notice\n", "test17"); + is_function_output(test18, 0, "test18: notice\n", "test18"); + is_function_output(test19, 0, "", "test19"); + is_function_output(test20, 0, "3 0 foo\n", "test20"); + is_function_output(test21, 0, "test23: baz\n", "test21"); /* Make sure that it's possible to turn off a message type entirely. */ - test_error(22, 1, "", test22); - test_error(23, 0, "", test23); - test_error(24, 0, "first\nthird\n", test24); + is_function_output(test22, 1, "", "test22"); + is_function_output(test23, 0, "", "test23"); + is_function_output(test24, 0, "first\nthird\n", "test24"); return 0; } diff --git a/tests/util/xmalloc-t b/tests/util/xmalloc-t new file mode 100755 index 0000000..02f54b5 --- /dev/null +++ b/tests/util/xmalloc-t @@ -0,0 +1,127 @@ +#! /bin/sh +# +# Test suite for xmalloc and friends. +# +# Written by Russ Allbery +# Copyright 2008, 2009, 2010 Board of Trustees, Leland Stanford Jr. University +# Copyright 2004, 2005, 2006 +# by Internet Systems Consortium, Inc. ("ISC") +# Copyright 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003 by The Internet Software Consortium and Rich Salz +# +# See LICENSE for licensing terms. + +. "$SOURCE/tap/libtap.sh" +cd "$BUILD/util" + +# Run an xmalloc test. Takes the description, the expectd exit status, the +# output, and the arguments. +ok_xmalloc () { + local desc w_status w_output output status + desc="$1" + shift + w_status="$1" + shift + w_output="$1" + shift + output=`./xmalloc "$@" 2>&1` + status=$? + if [ "$w_status" -ne 0 ] ; then + output=`echo "$output" | sed 's/:.*//'` + fi + if [ $status = $w_status ] && [ x"$output" = x"$w_output" ] ; then + ok "$desc" true + elif [ $status = 2 ] ; then + skip "no data limit support" + else + echo "# saw: ($status) $output" + echo "# not: ($w_status) $w_output" + ok "$desc" false + fi +} + +# Skip this test suite unless maintainer-mode tests are enabled. All of the +# failures in automated testing have been problems with the assumptions around +# memory allocation or problems with the test suite, not problems with the +# underlying xmalloc code. +if [ -z "$RRA_MAINTAINER_TESTS" ] ; then + skip_all 'xmalloc tests only run for maintainer' +fi + +# Total tests. +plan 36 + +# First run the tests expected to succeed. +ok_xmalloc "malloc small" 0 "" "m" "21" "0" +ok_xmalloc "malloc large" 0 "" "m" "3500000" "0" +ok_xmalloc "malloc zero" 0 "" "m" "0" "0" +ok_xmalloc "realloc small" 0 "" "r" "21" "0" +ok_xmalloc "realloc large" 0 "" "r" "3500000" "0" +ok_xmalloc "strdup small" 0 "" "s" "21" "0" +ok_xmalloc "strdup large" 0 "" "s" "3500000" "0" +ok_xmalloc "strndup small" 0 "" "n" "21" "0" +ok_xmalloc "strndup large" 0 "" "n" "3500000" "0" +ok_xmalloc "calloc small" 0 "" "c" "24" "0" +ok_xmalloc "calloc large" 0 "" "c" "3500000" "0" +ok_xmalloc "asprintf small" 0 "" "a" "24" "0" +ok_xmalloc "asprintf large" 0 "" "a" "3500000" "0" +ok_xmalloc "vasprintf small" 0 "" "v" "24" "0" +ok_xmalloc "vasprintf large" 0 "" "v" "3500000" "0" + +# Now limit our memory to 3.5MB and then try the large ones again, all of +# which should fail. +# +# The exact memory limits used here are essentially black magic. They need to +# be large enough to allow the program to be loaded and do small allocations, +# but not so large that we can't reasonably expect to allocate that much +# memory normally. 3.5MB seems to work reasonably well on both Solaris and +# Linux. +# +# We assume that there are enough miscellaneous allocations that an allocation +# exactly as large as the limit will always fail. +ok_xmalloc "malloc fail" 1 \ + "failed to malloc 3500000 bytes at xmalloc.c line 38" \ + "m" "3500000" "3500000" +ok_xmalloc "realloc fail" 1 \ + "failed to realloc 3500000 bytes at xmalloc.c line 66" \ + "r" "3500000" "3500000" +ok_xmalloc "strdup fail" 1 \ + "failed to strdup 3500000 bytes at xmalloc.c line 97" \ + "s" "3500000" "3500000" +ok_xmalloc "strndup fail" 1 \ + "failed to strndup 3500000 bytes at xmalloc.c line 124" \ + "n" "3500000" "3500000" +ok_xmalloc "calloc fail" 1 \ + "failed to calloc 3500000 bytes at xmalloc.c line 148" \ + "c" "3500000" "3500000" +ok_xmalloc "asprintf fail" 1 \ + "failed to asprintf 3500000 bytes at xmalloc.c line 173" \ + "a" "3500000" "3500000" +ok_xmalloc "vasprintf fail" 1 \ + "failed to vasprintf 3500000 bytes at xmalloc.c line 193" \ + "v" "3500000" "3500000" + +# Check our custom error handler. +ok_xmalloc "malloc custom" 1 "malloc 3500000 xmalloc.c 38" \ + "M" "3500000" "3500000" +ok_xmalloc "realloc custom" 1 "realloc 3500000 xmalloc.c 66" \ + "R" "3500000" "3500000" +ok_xmalloc "strdup custom" 1 "strdup 3500000 xmalloc.c 97" \ + "S" "3500000" "3500000" +ok_xmalloc "strndup custom" 1 "strndup 3500000 xmalloc.c 124" \ + "N" "3500000" "3500000" +ok_xmalloc "calloc custom" 1 "calloc 3500000 xmalloc.c 148" \ + "C" "3500000" "3500000" +ok_xmalloc "asprintf custom" 1 "asprintf 3500000 xmalloc.c 173" \ + "A" "3500000" "3500000" +ok_xmalloc "vasprintf custom" 1 "vasprintf 3500000 xmalloc.c 193" \ + "V" "3500000" "3500000" + +# Check the smaller ones again just for grins. +ok_xmalloc "malloc retry" 0 "" "m" "21" "3500000" +ok_xmalloc "realloc retry" 0 "" "r" "32" "3500000" +ok_xmalloc "strdup retry" 0 "" "s" "64" "3500000" +ok_xmalloc "strndup retry" 0 "" "n" "20" "3500000" +ok_xmalloc "calloc retry" 0 "" "c" "24" "3500000" +ok_xmalloc "asprintf retry" 0 "" "a" "30" "3500000" +ok_xmalloc "vasprintf retry" 0 "" "v" "35" "3500000" diff --git a/tests/util/xmalloc-t.in b/tests/util/xmalloc-t.in deleted file mode 100644 index 5c18512..0000000 --- a/tests/util/xmalloc-t.in +++ /dev/null @@ -1,126 +0,0 @@ -#! /bin/sh -# -# Test suite for xmalloc and friends. -# -# Copyright 2004, 2005, 2006 -# by Internet Systems Consortium, Inc. ("ISC") -# Copyright 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003 by The Internet Software Consortium and Rich Salz -# -# This code is derived from software contributed to the Internet Software -# Consortium by Rich Salz. -# -# Permission to use, copy, modify, and distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH -# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, -# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE -# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -# PERFORMANCE OF THIS SOFTWARE. - -# The count starts at 1 and is updated each time ok is printed. printcount -# takes "ok" or "not ok". -count=1 -printcount () { - echo "$1 $count $2" - count=`expr $count + 1` -} - -# Run a program expected to succeed, and print ok if it does. -runsuccess () { - output=`$xmalloc "$1" "$2" "$3" 2>&1 >/dev/null` - status=$? - if test $status = 0 && test -z "$output" ; then - printcount "ok" - else - if test $status = 2 ; then - printcount "ok" "# skip - no data limit support" - else - printcount "not ok" - echo " $output" - fi - fi -} - -# Run a program expected to fail and make sure it fails with an exit status -# of 2 and the right failure message. Strip the colon and everything after -# it off the error message since it's system-specific. -runfailure () { - output=`$xmalloc "$1" "$2" "$3" 2>&1 >/dev/null` - status=$? - output=`echo "$output" | sed 's/:.*//' \ - | sed 's% [^ ]*/xmalloc.c% xmalloc.c%'` - if test $status = 1 && test x"$output" = x"$4" ; then - printcount "ok" - else - if test $status = 2 ; then - printcount "ok" "# skip - no data limit support" - else - printcount "not ok" - echo " saw: $output" - echo " not: $4" - fi - fi -} - -# Find where the helper program is. -xmalloc="@abs_top_builddir@/tests/util/xmalloc" - -# Total tests. -echo 36 - -# First run the tests expected to succeed. -runsuccess "m" "21" "0" -runsuccess "m" "128000" "0" -runsuccess "m" "0" "0" -runsuccess "r" "21" "0" -runsuccess "r" "128000" "0" -runsuccess "s" "21" "0" -runsuccess "s" "128000" "0" -runsuccess "n" "21" "0" -runsuccess "n" "128000" "0" -runsuccess "c" "24" "0" -runsuccess "c" "128000" "0" -runsuccess "a" "24" "0" -runsuccess "a" "128000" "0" -runsuccess "v" "24" "0" -runsuccess "v" "128000" "0" - -# Now limit our memory to 120KB and then try the large ones again, all of -# which should fail. -runfailure "m" "128000" "120000" \ - "failed to malloc 128000 bytes at xmalloc.c line 61" -runfailure "r" "128000" "120000" \ - "failed to realloc 128000 bytes at xmalloc.c line 90" -runfailure "s" "64000" "120000" \ - "failed to strdup 64000 bytes at xmalloc.c line 121" -runfailure "n" "64000" "120000" \ - "failed to strndup 64000 bytes at xmalloc.c line 148" -runfailure "c" "128000" "120000" \ - "failed to calloc 128000 bytes at xmalloc.c line 172" -runfailure "a" "64000" "120000" \ - "failed to asprintf 64000 bytes at xmalloc.c line 241" -runfailure "v" "64000" "120000" \ - "failed to vasprintf 64000 bytes at xmalloc.c line 217" - -# Check our custom error handler. -runfailure "M" "128000" "120000" "malloc 128000 xmalloc.c 61" -runfailure "R" "128000" "120000" "realloc 128000 xmalloc.c 90" -runfailure "S" "64000" "120000" "strdup 64000 xmalloc.c 121" -runfailure "N" "64000" "120000" "strndup 64000 xmalloc.c 148" -runfailure "C" "128000" "120000" "calloc 128000 xmalloc.c 172" -runfailure "A" "64000" "120000" "asprintf 64000 xmalloc.c 241" -runfailure "V" "64000" "120000" "vasprintf 64000 xmalloc.c 217" - -# Check the smaller ones again just for grins. -runsuccess "m" "21" "96000" -runsuccess "r" "32" "96000" -runsuccess "s" "64" "96000" -runsuccess "n" "20" "96000" -runsuccess "c" "24" "96000" -runsuccess "a" "30" "96000" -runsuccess "v" "35" "96000" diff --git a/tests/util/xmalloc.c b/tests/util/xmalloc.c index bd0ab62..3bd5588 100644 --- a/tests/util/xmalloc.c +++ b/tests/util/xmalloc.c @@ -1,27 +1,17 @@ /* * Test suite for xmalloc and family. * + * Copyright 2008 Board of Trustees, Leland Stanford Jr. University * Copyright 2004, 2005, 2006 * by Internet Systems Consortium, Inc. ("ISC") * Copyright 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, * 2003 by The Internet Software Consortium and Rich Salz * - * This code is derived from software contributed to the Internet Software - * Consortium by Rich Salz. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. + * See LICENSE for licensing terms. */ +#line 1 "xmalloc.c" + #include #include @@ -32,7 +22,8 @@ /* Linux requires sys/time.h be included before sys/resource.h. */ #include -#include +#include +#include /* @@ -81,20 +72,19 @@ test_realloc(size_t size) char *buffer; size_t i; - buffer = xmalloc(size / 2); + buffer = xmalloc(10); if (buffer == NULL) return 0; - if (size / 2 > 0) - memset(buffer, 1, size / 2); + memset(buffer, 1, 10); buffer = xrealloc(buffer, size); if (buffer == NULL) return 0; if (size > 0) - memset(buffer + size / 2, 2, size - size / 2); - for (i = 0; i < size / 2; i++) + memset(buffer + 10, 2, size - 10); + for (i = 0; i < 10; i++) if (buffer[i] != 1) return 0; - for (i = size / 2; i < size; i++) + for (i = 10; i < size; i++) if (buffer[i] != 2) return 0; free(buffer); @@ -257,6 +247,7 @@ main(int argc, char *argv[]) int willfail = 0; unsigned char code; struct rlimit rl; + void *tmp; if (argc < 3) die("Usage error. Type, size, and limit must be given."); @@ -269,6 +260,27 @@ main(int argc, char *argv[]) if (limit == 0 && errno != 0) sysdie("Invalid limit"); + /* If the code is capitalized, install our customized error handler. */ + code = argv[1][0]; + if (isupper(code)) { + xmalloc_error_handler = test_handler; + code = tolower(code); + } + + /* + * Decide if the allocation should fail. If it should, set willfail to 2, + * so that if it unexpectedly succeeds, we exit with a status indicating + * that the test should be skipped. + */ + max = size; + if (code == 's' || code == 'n' || code == 'a' || code == 'v') { + max += size; + if (limit > 0) + limit += size; + } + if (limit > 0 && max > limit) + willfail = 2; + /* * If a memory limit was given and we can set memory limits, set it. * Otherwise, exit 2, signalling to the driver that the test should be @@ -277,37 +289,28 @@ main(int argc, char *argv[]) * the shell to die). */ if (limit > 0) { -#if HAVE_SETRLIMIT && defined(RLIMIT_DATA) +#if HAVE_SETRLIMIT && defined(RLIMIT_AS) rl.rlim_cur = limit; rl.rlim_max = limit; - if (setrlimit(RLIMIT_DATA, &rl) < 0) { + if (setrlimit(RLIMIT_AS, &rl) < 0) { syswarn("Can't set data limit to %lu", (unsigned long) limit); exit(2); } + if (size < limit || code == 'r') { + tmp = malloc(code == 'r' ? 10 : size); + if (tmp == NULL) { + syswarn("Can't allocate initial memory of %lu", + (unsigned long) size); + exit(2); + } + free(tmp); + } #else warn("Data limits aren't supported."); exit(2); #endif } - /* If the code is capitalized, install our customized error handler. */ - code = argv[1][0]; - if (isupper(code)) { - xmalloc_error_handler = test_handler; - code = tolower(code); - } - - /* - * Decide if the allocation should fail. If it should, set willfail to 2, - * so that if it unexpectedly succeeds, we exit with a status indicating - * that the test should be skipped. - */ - max = size; - if (code == 's' || code == 'n' || code == 'a' || code == 'v') - max *= 2; - if (limit > 0 && max > limit) - willfail = 2; - switch (code) { case 'c': exit(test_calloc(size) ? willfail : 1); case 'm': exit(test_malloc(size) ? willfail : 1); -- cgit v1.2.3 From 77d967fb11a4e63967ad1e80929b7096f9d58c05 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Tue, 9 Feb 2010 19:43:15 -0800 Subject: Enable silent rule support and use it for make warnings --- Makefile.am | 4 ++-- README | 13 +++++++++---- configure.ac | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) (limited to 'configure.ac') diff --git a/Makefile.am b/Makefile.am index 056229b..77514a7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -70,8 +70,8 @@ WARNINGS = -g -O -DDEBUG=1 -Wall -W -Wendif-labels -Wpointer-arith \ -Wmissing-prototypes -Wnested-externs -Werror warnings: - $(MAKE) CFLAGS='$(WARNINGS)' - $(MAKE) CFLAGS='$(WARNINGS)' $(check_PROGRAMS) + $(MAKE) V=0 CFLAGS='$(WARNINGS)' + $(MAKE) V=0 CFLAGS='$(WARNINGS)' $(check_PROGRAMS) # Remove some additional files. DISTCLEANFILES = perl/Makefile diff --git a/README b/README index eb9b39c..abc02fb 100644 --- a/README +++ b/README @@ -127,10 +127,11 @@ REQUIREMENTS srvtab with ADMIN access to a test AFS kaserver, and some additional configuration. - If you change the Automake files and need to regenerate Makefile.in, you - will need Automake 1.10 or later. If you change configure.ac or any of - the m4 files it includes and need to regenerate configure or - config.h.in, you will need Autoconf 2.61 or later. + To bootstrap from a Git checkout, or if you change the Automake files + and need to regenerate Makefile.in, you will need Automake 1.11 or + later. For bootstrap or if you change configure.ac or any of the m4 + files it includes and need to regenerate configure or config.h.in, you + will need Autoconf 2.64 or later. BUILD AND INSTALLATION @@ -140,6 +141,10 @@ BUILD AND INSTALLATION make make install + Pass --enable-silent-rules to configure for a quieter build (similar to + the Linux kernel). Use make warnings instead of make to build with full + GCC compiler warnings (requires a relatively current version of GCC). + The last step will probably have to be done as root. Currently, this always installs both the client and the server. diff --git a/configure.ac b/configure.ac index 0330aa9..0597859 100644 --- a/configure.ac +++ b/configure.ac @@ -13,7 +13,7 @@ AC_INIT([wallet], [0.9], [rra@stanford.edu]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_LIBOBJ_DIR([portable]) AC_CONFIG_MACRO_DIR([m4]) -AM_INIT_AUTOMAKE([1.11 check-news]) +AM_INIT_AUTOMAKE([1.11 check-news silent-rules]) AM_MAINTAINER_MODE AC_PROG_CC -- cgit v1.2.3 From 99448954f4f9504796226bf05e76df22231d51ca Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Tue, 9 Feb 2010 19:43:27 -0800 Subject: Add additional Kerberos portability needed for the test suite --- configure.ac | 3 ++- portable/krb5-extra.c | 17 +++++++++++++++++ portable/krb5.h | 9 +++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) (limited to 'configure.ac') diff --git a/configure.ac b/configure.ac index 0597859..c897775 100644 --- a/configure.ac +++ b/configure.ac @@ -27,7 +27,8 @@ RRA_LIB_KRB5 RRA_LIB_KRB5_SWITCH AC_CHECK_FUNCS([krb5_get_init_creds_opt_alloc \ krb5_get_init_creds_opt_set_default_flags \ - krb5_kt_free_entry]) + krb5_kt_free_entry \ + krb5_principal_get_realm]) AC_CHECK_MEMBERS([krb5_keytab_entry.keyblock], , , [#include ]) RRA_LIB_KRB5_RESTORE diff --git a/portable/krb5-extra.c b/portable/krb5-extra.c index 09a717b..afd00e8 100644 --- a/portable/krb5-extra.c +++ b/portable/krb5-extra.c @@ -106,3 +106,20 @@ krb5_get_init_creds_opt_alloc(krb5_context ctx, krb5_get_init_creds_opt **opts) return 0; } #endif /* !HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC */ + + +#ifndef HAVE_KRB5_PRINCIPAL_GET_REALM +/* + * Return the realm of a principal as a const char *. + */ +const char * +krb5_principal_get_realm(krb5_context ctx UNUSED, krb5_const_principal princ) +{ + const krb5_data *data; + + data = krb5_princ_realm(ctx, princ); + if (data == NULL || data->data == NULL) + return NULL; + return data->data; +} +#endif /* !HAVE_KRB5_PRINCIPAL_GET_REALM */ diff --git a/portable/krb5.h b/portable/krb5.h index 117f5ce..d9ef283 100644 --- a/portable/krb5.h +++ b/portable/krb5.h @@ -68,6 +68,15 @@ krb5_error_code krb5_get_init_creds_opt_alloc(krb5_context, # define krb5_kt_free_entry(c, e) krb5_free_keytab_entry_contents((c), (e)) #endif +/* + * Heimdal provides a nice function that just returns a const char *. On MIT, + * there's an accessor macro that returns the krb5_data pointer, wihch + * requires more work to get at the underlying char *. + */ +#ifndef HAVE_KRB5_PRINCIPAL_GET_REALM +const char *krb5_principal_get_realm(krb5_context, krb5_const_principal); +#endif + /* Undo default visibility change. */ #pragma GCC visibility pop -- cgit v1.2.3 From a556c732806da87d06bb787565e12240ea39b553 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Tue, 9 Feb 2010 21:01:33 -0800 Subject: Stop doing Autoconf substitution on some test suite code Anything that only was using substitution for the paths to the build tree now uses $SOURCE and $BUILD instead. Stop doing substitution. Also fix tests/data/cmd-wrapper to use the environment variables. --- .gitignore | 7 - configure.ac | 6 - tests/client/pod-t | 22 ++ tests/client/pod-t.in | 22 -- tests/data/cmd-wrapper | 8 + tests/data/cmd-wrapper.in | 9 - tests/server/admin-t | 241 ++++++++++++++++++++++ tests/server/admin-t.in | 241 ---------------------- tests/server/backend-t | 502 ++++++++++++++++++++++++++++++++++++++++++++++ tests/server/backend-t.in | 502 ---------------------------------------------- tests/server/keytab-t | 88 ++++++++ tests/server/keytab-t.in | 88 -------- tests/server/pod-t | 22 ++ tests/server/pod-t.in | 22 -- 14 files changed, 883 insertions(+), 897 deletions(-) create mode 100755 tests/client/pod-t delete mode 100644 tests/client/pod-t.in create mode 100755 tests/data/cmd-wrapper delete mode 100644 tests/data/cmd-wrapper.in create mode 100755 tests/server/admin-t delete mode 100644 tests/server/admin-t.in create mode 100755 tests/server/backend-t delete mode 100644 tests/server/backend-t.in create mode 100755 tests/server/keytab-t delete mode 100644 tests/server/keytab-t.in create mode 100755 tests/server/pod-t delete mode 100644 tests/server/pod-t.in (limited to 'configure.ac') diff --git a/.gitignore b/.gitignore index 09ae109..b0a49df 100644 --- a/.gitignore +++ b/.gitignore @@ -18,15 +18,12 @@ /perl/t/data/test.krbtype /tests/client/basic-t /tests/client/full-t -/tests/client/pod-t /tests/client/prompt-t -/tests/data/cmd-wrapper /tests/data/full.conf /tests/data/test.keytab /tests/data/test.password /tests/data/test.principal /tests/data/test.krbtype -/tests/kasetkey/basic-t /tests/portable/asprintf-t /tests/portable/mkstemp-t /tests/portable/setenv-t @@ -34,10 +31,6 @@ /tests/portable/strlcat-t /tests/portable/strlcpy-t /tests/runtests -/tests/server/admin-t -/tests/server/backend-t -/tests/server/keytab-t -/tests/server/pod-t /tests/util/concat-t /tests/util/messages-krb5-t /tests/util/messages-t diff --git a/configure.ac b/configure.ac index c897775..664c6f7 100644 --- a/configure.ac +++ b/configure.ac @@ -64,11 +64,5 @@ AC_CONFIG_HEADER([config.h]) AC_CONFIG_FILES([Makefile perl/Makefile.PL tests/data/full.conf]) AC_CONFIG_FILES([tests/client/basic-t], [chmod +x tests/client/basic-t]) AC_CONFIG_FILES([tests/client/full-t], [chmod +x tests/client/full-t]) -AC_CONFIG_FILES([tests/client/pod-t], [chmod +x tests/client/pod-t]) AC_CONFIG_FILES([tests/client/prompt-t], [chmod +x tests/client/prompt-t]) -AC_CONFIG_FILES([tests/data/cmd-wrapper], [chmod +x tests/data/cmd-wrapper]) -AC_CONFIG_FILES([tests/server/admin-t], [chmod +x tests/server/admin-t]) -AC_CONFIG_FILES([tests/server/backend-t], [chmod +x tests/server/backend-t]) -AC_CONFIG_FILES([tests/server/keytab-t], [chmod +x tests/server/keytab-t]) -AC_CONFIG_FILES([tests/server/pod-t], [chmod +x tests/server/pod-t]) AC_OUTPUT diff --git a/tests/client/pod-t b/tests/client/pod-t new file mode 100755 index 0000000..9963567 --- /dev/null +++ b/tests/client/pod-t @@ -0,0 +1,22 @@ +#!/usr/bin/perl +# +# Test POD formatting for client documentation. +# +# Written by Russ Allbery +# Copyright 2008, 2010 Board of Trustees, Leland Stanford Jr. University +# +# See LICENSE for licensing terms. + +use Test::More; + +my @files = qw(wallet.pod); +my $total = scalar (@files); +plan tests => $total; + +eval 'use Test::Pod 1.00'; +SKIP: { + skip $total, 'Test::Pod 1.00 required for testing POD' if $@; + for my $file (@files) { + pod_file_ok ("$ENV{SOURCE}/../client/$file", "client/$file"); + } +} diff --git a/tests/client/pod-t.in b/tests/client/pod-t.in deleted file mode 100644 index 9963567..0000000 --- a/tests/client/pod-t.in +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/perl -# -# Test POD formatting for client documentation. -# -# Written by Russ Allbery -# Copyright 2008, 2010 Board of Trustees, Leland Stanford Jr. University -# -# See LICENSE for licensing terms. - -use Test::More; - -my @files = qw(wallet.pod); -my $total = scalar (@files); -plan tests => $total; - -eval 'use Test::Pod 1.00'; -SKIP: { - skip $total, 'Test::Pod 1.00 required for testing POD' if $@; - for my $file (@files) { - pod_file_ok ("$ENV{SOURCE}/../client/$file", "client/$file"); - } -} diff --git a/tests/data/cmd-wrapper b/tests/data/cmd-wrapper new file mode 100755 index 0000000..79b1943 --- /dev/null +++ b/tests/data/cmd-wrapper @@ -0,0 +1,8 @@ +#!/bin/sh +# +# Wrapper around the standard wallet-backend script that sets the Perl INC +# path and the WALLET_CONFIG environment variable appropriately. + +WALLET_CONFIG="$SOURCE/data/wallet.conf" +export WALLET_CONFIG +exec perl -I"$SOURCE/../perl" "$SOURCE/../server/wallet-backend" -q "$@" diff --git a/tests/data/cmd-wrapper.in b/tests/data/cmd-wrapper.in deleted file mode 100644 index 7c7b342..0000000 --- a/tests/data/cmd-wrapper.in +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -# -# Wrapper around the standard wallet-backend script that sets the Perl INC -# path and the WALLET_CONFIG environment variable appropriately. - -WALLET_CONFIG='@abs_top_srcdir@/tests/data/wallet.conf' -export WALLET_CONFIG -exec perl -I'@abs_top_srcdir@/perl' '@abs_top_srcdir@/server/wallet-backend' \ - -q "$@" diff --git a/tests/server/admin-t b/tests/server/admin-t new file mode 100755 index 0000000..570dc52 --- /dev/null +++ b/tests/server/admin-t @@ -0,0 +1,241 @@ +#!/usr/bin/perl -w +# +# Tests for the wallet-admin dispatch code. +# +# Written by Russ Allbery +# Copyright 2008, 2009, 2010 Board of Trustees, Leland Stanford Jr. University +# +# See LICENSE for licensing terms. + +use strict; +use Test::More tests => 64; + +# Create a dummy class for Wallet::Admin that prints what method was called +# with its arguments and returns data for testing. +package Wallet::Admin; + +use vars qw($empty $error); +$error = 0; +$empty = 0; + +sub error { + if ($error) { + return "some error"; + } else { + return; + } +} + +sub new { + print "new\n"; + return bless ({}, 'Wallet::Admin'); +} + +sub destroy { + print "destroy\n"; + return if $error; + return 1; +} + +sub initialize { + shift; + print "initialize @_\n"; + return if $error; + return 1; +} + +sub list_objects { + print "list_objects\n"; + return if ($error or $empty); + return ([ keytab => 'host/windlord.stanford.edu' ], + [ file => 'unix-wallet-password' ]); +} + +sub list_acls { + print "list_acls\n"; + return if ($error or $empty); + return ([ 1, 'ADMIN' ], [ 2, 'group/admins' ], [ 4, 'group/users' ]); +} + +sub register_object { + shift; + print "register_object @_\n"; + return if $error; + return 1; +} + +sub register_verifier { + shift; + print "register_verifier @_\n"; + return if $error; + return 1; +} + +sub report_owners { + shift; + print "report_owners @_\n"; + return if ($error or $empty); + return ([ krb5 => 'admin@EXAMPLE.COM' ]); +} + +# Back to the main package and the actual test suite. Lie about whether the +# Wallet::Admin package has already been loaded. +package main; +$INC{'Wallet/Admin.pm'} = 'FAKE'; +eval { do "$ENV{SOURCE}/../server/wallet-admin" }; + +# Run the wallet admin client. This fun hack takes advantage of the fact that +# the wallet admin client is written in Perl so that we can substitute our own +# Wallet::Admin class. +sub run_admin { + my (@args) = @_; + my $result = ''; + open (OUTPUT, '>', \$result) or die "cannot create output string: $!\n"; + select OUTPUT; + local $| = 1; + eval { command (@args) }; + my $error = $@; + select STDOUT; + return ($result, $error); +} + +# Now for the actual tests. First check for unknown commands. +my ($out, $err) = run_admin ('foo'); +is ($err, "unknown command foo\n", 'Unknown command'); +is ($out, "new\n", ' and nothing ran'); + +# Check too few and too many arguments for every command. +my %commands = (destroy => [0, 0], + initialize => [1, 1], + list => [1, 4], + register => [3, 3], + report => [1, -1]); +for my $command (sort keys %commands) { + my ($min, $max) = @{ $commands{$command} }; + if ($min > 0) { + ($out, $err) = run_admin ($command, ('foo') x ($min - 1)); + is ($err, "too few arguments to $command\n", + "Too few arguments for $command"); + is ($out, "new\n", ' and nothing ran'); + } + if ($max >= 0) { + ($out, $err) = run_admin ($command, ('foo') x ($max + 1)); + is ($err, "too many arguments to $command\n", + "Too many arguments for $command"); + is ($out, "new\n", ' and nothing ran'); + } +} + +# Test destroy. +my $answer = ''; +close STDIN; +open (STDIN, '<', \$answer) or die "cannot reopen standard input: $!\n"; +($out, $err) = run_admin ('destroy'); +is ($err, "Aborted\n", 'Destroy with no answer aborts'); +is ($out, "new\n" . + 'This will delete all data in the wallet database. Are you sure (N/y)? ', + ' and prints the right prompt'); +seek (STDIN, 0, 0); +$answer = 'n'; +($out, $err) = run_admin ('destroy'); +is ($err, "Aborted\n", 'Destroy with negative answer answer aborts'); +is ($out, "new\n" . + 'This will delete all data in the wallet database. Are you sure (N/y)? ', + ' and prints the right prompt'); +seek (STDIN, 0, 0); +$answer = 'y'; +($out, $err) = run_admin ('destroy'); +is ($err, '', 'Destroy succeeds with a positive answer'); +is ($out, "new\n" + . 'This will delete all data in the wallet database.' + . ' Are you sure (N/y)? ' . "destroy\n", ' and destroy was run'); +seek (STDIN, 0, 0); + +# Test initialize. +($out, $err) = run_admin ('initialize', 'rra'); +is ($err, "invalid admin principal rra\n", 'Initialize requires a principal'); +is ($out, "new\n", ' and nothing was run'); +($out, $err) = run_admin ('initialize', 'rra@stanford.edu'); +is ($err, '', 'Initialize succeeds with a principal'); +is ($out, "new\ninitialize rra\@stanford.edu\n", ' and runs the right code'); + +# Test list. +($out, $err) = run_admin ('list', 'foo'); +is ($err, "only objects or acls are supported for list\n", + 'List requires a known object'); +is ($out, "new\n", ' and nothing was run'); +($out, $err) = run_admin ('list', 'objects'); +is ($err, '', 'List succeeds for objects'); +is ($out, "new\nlist_objects\n" + . "keytab host/windlord.stanford.edu\nfile unix-wallet-password\n", + ' and returns the right output'); +($out, $err) = run_admin ('list', 'acls'); +is ($err, '', 'List succeeds for ACLs'); +is ($out, "new\nlist_acls\n" + . "ADMIN (ACL ID: 1)\ngroup/admins (ACL ID: 2)\ngroup/users (ACL ID: 4)\n", + ' and returns the right output'); + +# Test register. +($out, $err) = run_admin ('register', 'foo', 'foo', 'Foo::Bar'); +is ($err, "only object or verifier is supported for register\n", + 'Register requires object or verifier'); +is ($out, "new\n", ' and nothing was run'); +($out, $err) = run_admin ('register', 'object', 'foo', 'Foo::Object'); +is ($err, '', 'Register succeeds for object'); +is ($out, "new\nregister_object foo Foo::Object\n", + ' and returns the right outout'); +($out, $err) = run_admin ('register', 'verifier', 'foo', 'Foo::Verifier'); +is ($err, '', 'Register succeeds for verifier'); +is ($out, "new\nregister_verifier foo Foo::Verifier\n", + ' and returns the right outout'); + +# Test report. +($out, $err) = run_admin ('report', 'foo'); +is ($err, "unknown report type foo\n", 'Report requires a known report'); +is ($out, "new\n", ' and nothing was run'); +($out, $err) = run_admin ('report', 'owners', '%', '%'); +is ($err, '', 'Report succeeds for owners'); +is ($out, "new\nreport_owners % %\nkrb5 admin\@EXAMPLE.COM\n", + ' and returns the right output'); + +# Test error handling. +$Wallet::Admin::error = 1; +($out, $err) = run_admin ('destroy'); +is ($err, "some error\n", 'Error handling succeeds for destroy'); +is ($out, "new\n" + . 'This will delete all data in the wallet database.' + . ' Are you sure (N/y)? ' . "destroy\n", ' and calls the right methods'); +($out, $err) = run_admin ('initialize', 'rra@stanford.edu'); +is ($err, "some error\n", 'Error handling succeeds for initialize'); +is ($out, "new\ninitialize rra\@stanford.edu\n", + ' and calls the right methods'); +($out, $err) = run_admin ('list', 'objects'); +is ($err, "some error\n", 'Error handling succeeds for list objects'); +is ($out, "new\nlist_objects\n", ' and calls the right methods'); +($out, $err) = run_admin ('list', 'acls'); +is ($err, "some error\n", 'Error handling succeeds for list acls'); +is ($out, "new\nlist_acls\n", ' and calls the right methods'); +($out, $err) = run_admin ('register', 'object', 'foo', 'Foo::Object'); +is ($err, "some error\n", 'Error handling succeeds for register object'); +is ($out, "new\nregister_object foo Foo::Object\n", + ' and calls the right methods'); +($out, $err) = run_admin ('register', 'verifier', 'foo', 'Foo::Verifier'); +is ($err, "some error\n", 'Error handling succeeds for register verifier'); +is ($out, "new\nregister_verifier foo Foo::Verifier\n", + ' and calls the right methods'); +($out, $err) = run_admin ('report', 'owners', 'foo', 'bar'); +is ($err, "some error\n", 'Error handling succeeds for report owners'); +is ($out, "new\nreport_owners foo bar\n", ' and calls the right methods'); + +# Test empty lists. +$Wallet::Admin::error = 0; +$Wallet::Admin::empty = 1; +($out, $err) = run_admin ('list', 'objects'); +is ($err, '', 'list objects runs with an empty list with no errors'); +is ($out, "new\nlist_objects\n", ' and calls the right methods'); +($out, $err) = run_admin ('list', 'acls'); +is ($err, '', 'list acls runs with an empty list and no errors'); +is ($out, "new\nlist_acls\n", ' and calls the right methods'); +($out, $err) = run_admin ('report', 'owners', 'foo', 'bar'); +is ($err, '', 'report owners runs with an empty list and no errors'); +is ($out, "new\nreport_owners foo bar\n", ' and calls the right methods'); diff --git a/tests/server/admin-t.in b/tests/server/admin-t.in deleted file mode 100644 index 570dc52..0000000 --- a/tests/server/admin-t.in +++ /dev/null @@ -1,241 +0,0 @@ -#!/usr/bin/perl -w -# -# Tests for the wallet-admin dispatch code. -# -# Written by Russ Allbery -# Copyright 2008, 2009, 2010 Board of Trustees, Leland Stanford Jr. University -# -# See LICENSE for licensing terms. - -use strict; -use Test::More tests => 64; - -# Create a dummy class for Wallet::Admin that prints what method was called -# with its arguments and returns data for testing. -package Wallet::Admin; - -use vars qw($empty $error); -$error = 0; -$empty = 0; - -sub error { - if ($error) { - return "some error"; - } else { - return; - } -} - -sub new { - print "new\n"; - return bless ({}, 'Wallet::Admin'); -} - -sub destroy { - print "destroy\n"; - return if $error; - return 1; -} - -sub initialize { - shift; - print "initialize @_\n"; - return if $error; - return 1; -} - -sub list_objects { - print "list_objects\n"; - return if ($error or $empty); - return ([ keytab => 'host/windlord.stanford.edu' ], - [ file => 'unix-wallet-password' ]); -} - -sub list_acls { - print "list_acls\n"; - return if ($error or $empty); - return ([ 1, 'ADMIN' ], [ 2, 'group/admins' ], [ 4, 'group/users' ]); -} - -sub register_object { - shift; - print "register_object @_\n"; - return if $error; - return 1; -} - -sub register_verifier { - shift; - print "register_verifier @_\n"; - return if $error; - return 1; -} - -sub report_owners { - shift; - print "report_owners @_\n"; - return if ($error or $empty); - return ([ krb5 => 'admin@EXAMPLE.COM' ]); -} - -# Back to the main package and the actual test suite. Lie about whether the -# Wallet::Admin package has already been loaded. -package main; -$INC{'Wallet/Admin.pm'} = 'FAKE'; -eval { do "$ENV{SOURCE}/../server/wallet-admin" }; - -# Run the wallet admin client. This fun hack takes advantage of the fact that -# the wallet admin client is written in Perl so that we can substitute our own -# Wallet::Admin class. -sub run_admin { - my (@args) = @_; - my $result = ''; - open (OUTPUT, '>', \$result) or die "cannot create output string: $!\n"; - select OUTPUT; - local $| = 1; - eval { command (@args) }; - my $error = $@; - select STDOUT; - return ($result, $error); -} - -# Now for the actual tests. First check for unknown commands. -my ($out, $err) = run_admin ('foo'); -is ($err, "unknown command foo\n", 'Unknown command'); -is ($out, "new\n", ' and nothing ran'); - -# Check too few and too many arguments for every command. -my %commands = (destroy => [0, 0], - initialize => [1, 1], - list => [1, 4], - register => [3, 3], - report => [1, -1]); -for my $command (sort keys %commands) { - my ($min, $max) = @{ $commands{$command} }; - if ($min > 0) { - ($out, $err) = run_admin ($command, ('foo') x ($min - 1)); - is ($err, "too few arguments to $command\n", - "Too few arguments for $command"); - is ($out, "new\n", ' and nothing ran'); - } - if ($max >= 0) { - ($out, $err) = run_admin ($command, ('foo') x ($max + 1)); - is ($err, "too many arguments to $command\n", - "Too many arguments for $command"); - is ($out, "new\n", ' and nothing ran'); - } -} - -# Test destroy. -my $answer = ''; -close STDIN; -open (STDIN, '<', \$answer) or die "cannot reopen standard input: $!\n"; -($out, $err) = run_admin ('destroy'); -is ($err, "Aborted\n", 'Destroy with no answer aborts'); -is ($out, "new\n" . - 'This will delete all data in the wallet database. Are you sure (N/y)? ', - ' and prints the right prompt'); -seek (STDIN, 0, 0); -$answer = 'n'; -($out, $err) = run_admin ('destroy'); -is ($err, "Aborted\n", 'Destroy with negative answer answer aborts'); -is ($out, "new\n" . - 'This will delete all data in the wallet database. Are you sure (N/y)? ', - ' and prints the right prompt'); -seek (STDIN, 0, 0); -$answer = 'y'; -($out, $err) = run_admin ('destroy'); -is ($err, '', 'Destroy succeeds with a positive answer'); -is ($out, "new\n" - . 'This will delete all data in the wallet database.' - . ' Are you sure (N/y)? ' . "destroy\n", ' and destroy was run'); -seek (STDIN, 0, 0); - -# Test initialize. -($out, $err) = run_admin ('initialize', 'rra'); -is ($err, "invalid admin principal rra\n", 'Initialize requires a principal'); -is ($out, "new\n", ' and nothing was run'); -($out, $err) = run_admin ('initialize', 'rra@stanford.edu'); -is ($err, '', 'Initialize succeeds with a principal'); -is ($out, "new\ninitialize rra\@stanford.edu\n", ' and runs the right code'); - -# Test list. -($out, $err) = run_admin ('list', 'foo'); -is ($err, "only objects or acls are supported for list\n", - 'List requires a known object'); -is ($out, "new\n", ' and nothing was run'); -($out, $err) = run_admin ('list', 'objects'); -is ($err, '', 'List succeeds for objects'); -is ($out, "new\nlist_objects\n" - . "keytab host/windlord.stanford.edu\nfile unix-wallet-password\n", - ' and returns the right output'); -($out, $err) = run_admin ('list', 'acls'); -is ($err, '', 'List succeeds for ACLs'); -is ($out, "new\nlist_acls\n" - . "ADMIN (ACL ID: 1)\ngroup/admins (ACL ID: 2)\ngroup/users (ACL ID: 4)\n", - ' and returns the right output'); - -# Test register. -($out, $err) = run_admin ('register', 'foo', 'foo', 'Foo::Bar'); -is ($err, "only object or verifier is supported for register\n", - 'Register requires object or verifier'); -is ($out, "new\n", ' and nothing was run'); -($out, $err) = run_admin ('register', 'object', 'foo', 'Foo::Object'); -is ($err, '', 'Register succeeds for object'); -is ($out, "new\nregister_object foo Foo::Object\n", - ' and returns the right outout'); -($out, $err) = run_admin ('register', 'verifier', 'foo', 'Foo::Verifier'); -is ($err, '', 'Register succeeds for verifier'); -is ($out, "new\nregister_verifier foo Foo::Verifier\n", - ' and returns the right outout'); - -# Test report. -($out, $err) = run_admin ('report', 'foo'); -is ($err, "unknown report type foo\n", 'Report requires a known report'); -is ($out, "new\n", ' and nothing was run'); -($out, $err) = run_admin ('report', 'owners', '%', '%'); -is ($err, '', 'Report succeeds for owners'); -is ($out, "new\nreport_owners % %\nkrb5 admin\@EXAMPLE.COM\n", - ' and returns the right output'); - -# Test error handling. -$Wallet::Admin::error = 1; -($out, $err) = run_admin ('destroy'); -is ($err, "some error\n", 'Error handling succeeds for destroy'); -is ($out, "new\n" - . 'This will delete all data in the wallet database.' - . ' Are you sure (N/y)? ' . "destroy\n", ' and calls the right methods'); -($out, $err) = run_admin ('initialize', 'rra@stanford.edu'); -is ($err, "some error\n", 'Error handling succeeds for initialize'); -is ($out, "new\ninitialize rra\@stanford.edu\n", - ' and calls the right methods'); -($out, $err) = run_admin ('list', 'objects'); -is ($err, "some error\n", 'Error handling succeeds for list objects'); -is ($out, "new\nlist_objects\n", ' and calls the right methods'); -($out, $err) = run_admin ('list', 'acls'); -is ($err, "some error\n", 'Error handling succeeds for list acls'); -is ($out, "new\nlist_acls\n", ' and calls the right methods'); -($out, $err) = run_admin ('register', 'object', 'foo', 'Foo::Object'); -is ($err, "some error\n", 'Error handling succeeds for register object'); -is ($out, "new\nregister_object foo Foo::Object\n", - ' and calls the right methods'); -($out, $err) = run_admin ('register', 'verifier', 'foo', 'Foo::Verifier'); -is ($err, "some error\n", 'Error handling succeeds for register verifier'); -is ($out, "new\nregister_verifier foo Foo::Verifier\n", - ' and calls the right methods'); -($out, $err) = run_admin ('report', 'owners', 'foo', 'bar'); -is ($err, "some error\n", 'Error handling succeeds for report owners'); -is ($out, "new\nreport_owners foo bar\n", ' and calls the right methods'); - -# Test empty lists. -$Wallet::Admin::error = 0; -$Wallet::Admin::empty = 1; -($out, $err) = run_admin ('list', 'objects'); -is ($err, '', 'list objects runs with an empty list with no errors'); -is ($out, "new\nlist_objects\n", ' and calls the right methods'); -($out, $err) = run_admin ('list', 'acls'); -is ($err, '', 'list acls runs with an empty list and no errors'); -is ($out, "new\nlist_acls\n", ' and calls the right methods'); -($out, $err) = run_admin ('report', 'owners', 'foo', 'bar'); -is ($err, '', 'report owners runs with an empty list and no errors'); -is ($out, "new\nreport_owners foo bar\n", ' and calls the right methods'); diff --git a/tests/server/backend-t b/tests/server/backend-t new file mode 100755 index 0000000..2fc6a53 --- /dev/null +++ b/tests/server/backend-t @@ -0,0 +1,502 @@ +#!/usr/bin/perl -w +# +# Tests for the wallet-backend dispatch code. +# +# Written by Russ Allbery +# Copyright 2006, 2007, 2008, 2009, 2010 +# Board of Trustees, Leland Stanford Jr. University +# +# See LICENSE for licensing terms. + +use strict; +use Test::More tests => 1263; + +# Create a dummy class for Wallet::Server that prints what method was called +# with its arguments and returns data for testing. +package Wallet::Server; + +use vars qw($error $okay); +$error = 0; +$okay = 0; + +sub error { + if ($okay) { + $okay = 0; + return; + } else { + $error++; + return "error count $error"; + } +} + +sub new { shift; print "new @_\n"; return bless ({}, 'Wallet::Server') } +sub create { shift; print "create @_\n"; ($_[0] eq 'error') ? undef : 1 } +sub destroy { shift; print "destroy @_\n"; ($_[0] eq 'error') ? undef : 1 } +sub store { shift; print "store @_\n"; ($_[0] eq 'error') ? undef : 1 } + +sub acl_add + { shift; print "acl_add @_\n"; ($_[0] eq 'error') ? undef : 1 } +sub acl_create + { shift; print "acl_create @_\n"; ($_[0] eq 'error') ? undef : 1 } +sub acl_destroy + { shift; print "acl_destroy @_\n"; ($_[0] eq 'error') ? undef : 1 } +sub acl_remove + { shift; print "acl_remove @_\n"; ($_[0] eq 'error') ? undef : 1 } +sub acl_rename + { shift; print "acl_rename @_\n"; ($_[0] eq 'error') ? undef : 1 } + +sub acl_history { + shift; + print "acl_history @_\n"; + return if $_[0] eq 'error'; + return 'acl_history'; +} + +sub acl_show { + shift; + print "acl_show @_\n"; + return if $_[0] eq 'error'; + return 'acl_show'; +} + +sub flag_clear + { shift; print "flag_clear @_\n"; ($_[0] eq 'error') ? undef : 1 } +sub flag_set + { shift; print "flag_set @_\n"; ($_[0] eq 'error') ? undef : 1 } + +sub acl { + shift; + print "acl @_\n"; + if ($_[0] eq 'error') { + return; + } elsif ($_[1] eq 'empty') { + $okay = 1; + return; + } else { + return 'acl'; + } +} + +sub attr { + shift; + print "attr @_\n"; + if ($_[0] eq 'error') { + return; + } elsif ($_[1] eq 'empty') { + $okay = 1; + return; + } elsif (@_ == 3) { + return ('attr1', 'attr2'); + } else { + return 'attr'; + } +} + +sub autocreate { + shift; + print "autocreate @_\n"; + return ($_[0] eq 'error') ? undef : 1 +} + +sub check { + shift; + print "check @_\n"; + if ($_[0] eq 'error') { + return; + } elsif ($_[1] eq 'empty') { + return 0; + } else { + return 1; + } +} + +sub expires { + shift; + print "expires @_\n"; + if ($_[0] eq 'error') { + return; + } elsif ($_[1] eq 'empty') { + $okay = 1; + return; + } else { + return 'expires'; + } +} + +sub get { + shift; + print "get @_\n"; + return if $_[0] eq 'error'; + return 'get'; +} + +sub history { + shift; + print "history @_\n"; + return if $_[0] eq 'error'; + return 'history'; +} + +sub owner { + shift; + print "owner @_\n"; + if ($_[0] eq 'error') { + return; + } elsif ($_[1] eq 'empty') { + $okay = 1; + return; + } else { + return 'owner'; + } +} + +sub show { + shift; + print "show @_\n"; + return if $_[0] eq 'error'; + return 'show'; +} + +# Back to the main package and the actual test suite. Lie about whether the +# Wallet::Server package has already been loaded. +package main; +$INC{'Wallet/Server.pm'} = 'FAKE'; +my $OUTPUT; +our $SYSLOG = \$OUTPUT; +eval { do "$ENV{SOURCE}/../server/wallet-backend" }; + +# Run the wallet backend. This fun hack takes advantage of the fact that the +# wallet backend is written in Perl so that we can substitute our own +# Wallet::Server class. +sub run_backend { + my (@args) = @_; + my $result = ''; + open (OUTPUT, '>', \$result) or die "cannot create output string: $!\n"; + select OUTPUT; + local $| = 1; + eval { command (@args) }; + my $error = $@; + select STDOUT; + return ($result, $error); +} + +# Now for the actual tests. First, check for lack of trace information. +my ($out, $err) = run_backend; +is ($err, "REMOTE_USER not set\n", 'REMOTE_USER required'); +is ($OUTPUT, "error: REMOTE_USER not set\n", ' and syslog correct'); +$ENV{REMOTE_USER} = 'admin'; +($out, $err) = run_backend; +is ($err, "neither REMOTE_HOST nor REMOTE_ADDR set\n", + 'REMOTE_HOST or _ADDR required'); +is ($OUTPUT, "error for admin: neither REMOTE_HOST nor REMOTE_ADDR set\n", + ' and syslog correct'); +$ENV{REMOTE_ADDR} = '1.2.3.4'; +my $new = 'new admin 1.2.3.4'; + +# Check unknown commands. +($out, $err) = run_backend ('foo'); +is ($err, "unknown command foo\n", 'Unknown command'); +is ($OUTPUT, "error for admin (1.2.3.4): unknown command foo\n", + ' and syslog correct'); +is ($out, "$new\n", ' and nothing ran'); +($out, $err) = run_backend ('acl', 'foo'); +is ($err, "unknown command acl foo\n", 'Unknown ACL command'); +is ($OUTPUT, "error for admin (1.2.3.4): unknown command acl foo\n", + ' and syslog correct'); +is ($out, "$new\n", ' and nothing ran'); +($out, $err) = run_backend ('flag', 'foo', 'service', 'foo', 'foo'); +is ($err, "unknown command flag foo\n", 'Unknown flag command'); +is ($OUTPUT, "error for admin (1.2.3.4): unknown command flag foo\n", + ' and syslog correct'); +is ($out, "$new\n", ' and nothing ran'); + +# Check too few, too many, and bad arguments for every command. +my %commands = (autocreate => [2, 2], + check => [2, 2], + create => [2, 2], + destroy => [2, 2], + expires => [2, 4], + get => [2, 2], + getacl => [3, 3], + getattr => [3, 3], + history => [2, 2], + owner => [2, 3], + setacl => [4, 4], + setattr => [4, 9], + show => [2, 2], + store => [3, 3]); +my %acl_commands = (add => [3, 3], + create => [1, 1], + destroy => [1, 1], + history => [1, 1], + remove => [3, 3], + rename => [2, 2], + show => [1, 1]); +my %flag_commands = (clear => [3, 3], + set => [3, 3]); +for my $command (sort keys %commands) { + my ($min, $max) = @{ $commands{$command} }; + ($out, $err) = run_backend ($command, ('foo') x ($min - 1)); + is ($err, "insufficient arguments\n", "Too few arguments for $command"); + is ($OUTPUT, "error for admin (1.2.3.4): insufficient arguments\n", + ' and syslog correct'); + is ($out, "$new\n", ' and nothing ran'); + unless ($max >= 9) { + ($out, $err) = run_backend ($command, ('foo') x ($max + 1)); + is ($err, "too many arguments\n", "Too many arguments for $command"); + is ($OUTPUT, "error for admin (1.2.3.4): too many arguments\n", + ' and syslog correct'); + is ($out, "$new\n", ' and nothing ran'); + } + my @base = ('foobar') x $max; + for my $arg (0 .. ($max - 1)) { + my @args = @base; + $args[$arg] = 'foo;bar'; + ($out, $err) = run_backend ($command, @args); + if ($command eq 'store' and $arg == 2) { + is ($err, '', 'Store allows any characters'); + is ($OUTPUT, "command $command @args[0,1] from admin (1.2.3.4)" + . " succeeded\n", ' and success logged'); + is ($out, "$new\nstore foobar foobar foo;bar\n", + ' and calls the right method'); + } else { + is ($err, "invalid characters in argument: foo;bar\n", + "Invalid arguments for $command $arg"); + is ($OUTPUT, "error for admin (1.2.3.4): invalid characters in" + . " argument: foo;bar\n", ' and syslog correct'); + is ($out, "$new\n", ' and nothing ran'); + } + } +} +for my $command (sort keys %acl_commands) { + my ($min, $max) = @{ $acl_commands{$command} }; + ($out, $err) = run_backend ('acl', $command, ('foo') x ($min - 1)); + is ($err, "insufficient arguments\n", + "Too few arguments for acl $command"); + is ($OUTPUT, "error for admin (1.2.3.4): insufficient arguments\n", + ' and syslog correct'); + is ($out, "$new\n", ' and nothing ran'); + ($out, $err) = run_backend ('acl', $command, ('foo') x ($max + 1)); + is ($err, "too many arguments\n", "Too many arguments for acl $command"); + is ($OUTPUT, "error for admin (1.2.3.4): too many arguments\n", + ' and syslog correct'); + is ($out, "$new\n", ' and nothing ran'); + my @base = ('foobar') x $max; + for my $arg (0 .. ($max - 1)) { + my @args = @base; + $args[$arg] = 'foo;bar'; + ($out, $err) = run_backend ('acl', $command, @args); + is ($err, "invalid characters in argument: foo;bar\n", + "Invalid arguments for acl $command $arg"); + is ($OUTPUT, "error for admin (1.2.3.4): invalid characters in" + . " argument: foo;bar\n", ' and syslog correct'); + is ($out, "$new\n", ' and nothing ran'); + } +} +for my $command (sort keys %flag_commands) { + my ($min, $max) = @{ $flag_commands{$command} }; + ($out, $err) = run_backend ('flag', $command, ('foo') x ($min - 1)); + is ($err, "insufficient arguments\n", + "Too few arguments for flag $command"); + is ($OUTPUT, "error for admin (1.2.3.4): insufficient arguments\n", + ' and syslog correct'); + is ($out, "$new\n", ' and nothing ran'); + ($out, $err) = run_backend ('flag', $command, ('foo') x ($max + 1)); + is ($err, "too many arguments\n", "Too many arguments for flag $command"); + is ($OUTPUT, "error for admin (1.2.3.4): too many arguments\n", + ' and syslog correct'); + is ($out, "$new\n", ' and nothing ran'); + my @base = ('foobar') x $max; + for my $arg (0 .. ($max - 1)) { + my @args = @base; + $args[$arg] = 'foo;bar'; + ($out, $err) = run_backend ('flag', $command, @args); + is ($err, "invalid characters in argument: foo;bar\n", + "Invalid arguments for flag $command $arg"); + is ($OUTPUT, "error for admin (1.2.3.4): invalid characters in" + . " argument: foo;bar\n", ' and syslog correct'); + is ($out, "$new\n", ' and nothing ran'); + } +} + +# Now, test that we ran the right functions and passed the correct arguments. +my $error = 1; +for my $command (qw/autocreate create destroy setacl setattr store/) { + my $method = { setacl => 'acl', setattr => 'attr' }->{$command}; + $method ||= $command; + my @extra = ('foo') x ($commands{$command}[0] - 2); + my $extra = @extra ? join (' ', '', @extra) : ''; + ($out, $err) = run_backend ($command, 'type', 'name', @extra); + my $ran; + if ($command eq 'store') { + $ran = "$command type name"; + } else { + $ran = "$command type name" . (@extra ? " @extra" : ''); + } + is ($err, '', "Command $command ran with no errors"); + is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", + ' and success logged'); + is ($out, "$new\n$method type name$extra\n", + ' and ran the right method'); + ($out, $err) = run_backend ($command, 'error', 'name', @extra); + if ($command eq 'store') { + $ran = "$command error name"; + } else { + $ran = "$command error name" . (@extra ? " @extra" : ''); + } + is ($err, "error count $error\n", "Command $command ran with errors"); + is ($OUTPUT, "command $ran from admin (1.2.3.4) failed: error count" + . " $error\n", ' and syslog correct'); + is ($out, "$new\n$method error name$extra\n", + ' and ran the right method'); + $error++; +} +for my $command (qw/check expires get getacl getattr history owner show/) { + my $method = { getacl => 'acl', getattr => 'attr' }->{$command}; + $method ||= $command; + my @extra = ('foo') x ($commands{$command}[0] - 2); + my $extra = @extra ? join (' ', '', @extra) : ''; + ($out, $err) = run_backend ($command, 'type', 'name', @extra); + my $ran = "$command type name" . (@extra ? " @extra" : ''); + is ($err, '', "Command $command ran with no errors"); + is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", + ' and success logged'); + if ($command eq 'getattr') { + is ($out, "$new\n$method type name$extra\nattr1\nattr2\n", + ' and ran the right method with output'); + } elsif ($command eq 'check') { + is ($out, "$new\n$method type name$extra\nyes\n", + ' and ran the right method with output'); + } else { + my $newline = ($command =~ /^(get|history|show)\z/) ? '' : "\n"; + is ($out, "$new\n$method type name$extra\n$method$newline", + ' and ran the right method with output'); + } + if ($command eq 'expires' or $command eq 'owner') { + ($out, $err) = run_backend ($command, 'type', 'name', @extra, 'foo'); + my $ran = "$command type name" . (@extra ? " @extra" : '') . ' foo'; + is ($err, '', "Command $command ran with no errors (setting)"); + is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", + ' and success logged'); + is ($out, "$new\n$method type name$extra foo\n", + ' and ran the right method'); + } + if ($command eq 'expires' or $command eq 'getacl' or $command eq 'owner') { + ($out, $err) = run_backend ($command, 'type', 'empty', @extra); + my $ran = "$command type empty" . (@extra ? " @extra" : ''); + is ($err, '', "Command $command ran with no errors (empty)"); + is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", + ' and success logged'); + my $desc; + if ($command eq 'expires') { $desc = 'expiration' } + elsif ($command eq 'getacl') { $desc = 'ACL' } + elsif ($command eq 'owner') { $desc = 'owner' } + is ($out, "$new\n$method type empty$extra\nNo $desc set\n", + ' and ran the right method with output'); + $error++; + } elsif ($command eq 'getattr') { + ($out, $err) = run_backend ($command, 'type', 'empty', @extra); + my $ran = "$command type empty" . (@extra ? " @extra" : ''); + is ($err, '', "Command $command ran with no errors (empty)"); + is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", + ' and success logged'); + is ($out, "$new\n$method type empty$extra\n", + ' and ran the right method with output'); + $error++; + } elsif ($command eq 'check') { + ($out, $err) = run_backend ($command, 'type', 'empty', @extra); + my $ran = "$command type empty" . (@extra ? " @extra" : ''); + is ($err, '', "Command $command ran with no errors (empty)"); + is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", + ' and success logged'); + is ($out, "$new\n$method type empty$extra\nno\n", + ' and ran the right method with output'); + } + ($out, $err) = run_backend ($command, 'error', 'name', @extra); + my $ran = "$command error name" . (@extra ? " @extra" : ''); + is ($err, "error count $error\n", "Command $command ran with errors"); + is ($OUTPUT, "command $ran from admin (1.2.3.4) failed: error count" + . " $error\n", ' and syslog correct'); + is ($out, "$new\n$method error name$extra\n", + ' and ran the right method'); + $error++; +} +for my $command (sort keys %acl_commands) { + my @extra = ('foo') x ($acl_commands{$command}[0] - 1); + my $extra = @extra ? join (' ', '', @extra) : ''; + ($out, $err) = run_backend ('acl', $command, 'name', @extra); + my $ran = "acl $command name" . (@extra ? " @extra" : ''); + is ($err, '', "Command acl $command ran with no errors"); + is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", + ' and success logged'); + my $expected; + if ($command eq 'show') { + $expected = "$new\nacl_$command name$extra\nacl_show"; + } elsif ($command eq 'history') { + $expected = "$new\nacl_$command name$extra\nacl_history"; + } else { + $expected = "$new\nacl_$command name$extra\n"; + } + is ($out, $expected, ' and ran the right method'); + ($out, $err) = run_backend ('acl', $command, 'error', @extra); + $ran = "acl $command error" . (@extra ? " @extra" : ''); + is ($err, "error count $error\n", "Command acl $command ran with errors"); + is ($OUTPUT, "command $ran from admin (1.2.3.4) failed: error count" + . " $error\n", ' and syslog correct'); + is ($out, "$new\nacl_$command error$extra\n", + ' and ran the right method'); + $error++; +} +for my $command (sort keys %flag_commands) { + my @extra = ('foo') x ($flag_commands{$command}[0] - 2); + my $extra = @extra ? join (' ', '', @extra) : ''; + ($out, $err) = run_backend ('flag', $command, 'type', 'name', @extra); + my $ran = "flag $command type name" . (@extra ? " @extra" : ''); + is ($err, '', "Command flag $command ran with no errors"); + is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", + ' and success logged'); + is ($out, "$new\nflag_$command type name$extra\n", + ' and ran the right method'); + ($out, $err) = run_backend ('flag', $command, 'error', 'name', @extra); + $ran = "flag $command error name" . (@extra ? " @extra" : ''); + is ($err, "error count $error\n", + "Command flag $command ran with errors"); + is ($OUTPUT, "command $ran from admin (1.2.3.4) failed: error count" + . " $error\n", ' and syslog correct'); + is ($out, "$new\nflag_$command error name$extra\n", + ' and ran the right method'); + $error++; +} + +# Almost done. All that remains is to test the robustness of the bad +# character checks against every possible character and test permitting the +# empty argument. +($out, $err) = run_backend ('show', 'type', ''); +is ($err, '', 'Allowed the empty argument'); +is ($OUTPUT, "command show type from admin (1.2.3.4) succeeded\n", + ' and success logged'); +my $ok = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_/.@-'; +($out, $err) = run_backend ('show', 'type', $ok); +is ($err, '', 'Allowed all valid characters'); +is ($OUTPUT, "command show type $ok from admin (1.2.3.4) succeeded\n", + ' and success logged'); +is ($out, "$new\nshow type $ok\nshow", ' and returned the right output'); +for my $n (0 .. 255) { + my $c = chr ($n); + my $name = $ok . $c; + ($out, $err) = run_backend ('show', 'type', $name); + if (index ($ok, $c) == -1) { + is ($err, "invalid characters in argument: $name\n", + "Rejected invalid character $n"); + my $stripped = $name; + $stripped =~ s/[^\x20-\x7e]/_/g; + is ($OUTPUT, "error for admin (1.2.3.4): invalid characters in" + . " argument: $stripped\n", ' and syslog correct'); + is ($out, "$new\n", ' and did nothing'); + } else { + is ($err, '', "Accepted valid character $n"); + is ($OUTPUT, "command show type $name from admin (1.2.3.4)" + . " succeeded\n", ' and success logged'); + is ($out, "$new\nshow type $name\nshow", ' and ran the method'); + } +} diff --git a/tests/server/backend-t.in b/tests/server/backend-t.in deleted file mode 100644 index 2fc6a53..0000000 --- a/tests/server/backend-t.in +++ /dev/null @@ -1,502 +0,0 @@ -#!/usr/bin/perl -w -# -# Tests for the wallet-backend dispatch code. -# -# Written by Russ Allbery -# Copyright 2006, 2007, 2008, 2009, 2010 -# Board of Trustees, Leland Stanford Jr. University -# -# See LICENSE for licensing terms. - -use strict; -use Test::More tests => 1263; - -# Create a dummy class for Wallet::Server that prints what method was called -# with its arguments and returns data for testing. -package Wallet::Server; - -use vars qw($error $okay); -$error = 0; -$okay = 0; - -sub error { - if ($okay) { - $okay = 0; - return; - } else { - $error++; - return "error count $error"; - } -} - -sub new { shift; print "new @_\n"; return bless ({}, 'Wallet::Server') } -sub create { shift; print "create @_\n"; ($_[0] eq 'error') ? undef : 1 } -sub destroy { shift; print "destroy @_\n"; ($_[0] eq 'error') ? undef : 1 } -sub store { shift; print "store @_\n"; ($_[0] eq 'error') ? undef : 1 } - -sub acl_add - { shift; print "acl_add @_\n"; ($_[0] eq 'error') ? undef : 1 } -sub acl_create - { shift; print "acl_create @_\n"; ($_[0] eq 'error') ? undef : 1 } -sub acl_destroy - { shift; print "acl_destroy @_\n"; ($_[0] eq 'error') ? undef : 1 } -sub acl_remove - { shift; print "acl_remove @_\n"; ($_[0] eq 'error') ? undef : 1 } -sub acl_rename - { shift; print "acl_rename @_\n"; ($_[0] eq 'error') ? undef : 1 } - -sub acl_history { - shift; - print "acl_history @_\n"; - return if $_[0] eq 'error'; - return 'acl_history'; -} - -sub acl_show { - shift; - print "acl_show @_\n"; - return if $_[0] eq 'error'; - return 'acl_show'; -} - -sub flag_clear - { shift; print "flag_clear @_\n"; ($_[0] eq 'error') ? undef : 1 } -sub flag_set - { shift; print "flag_set @_\n"; ($_[0] eq 'error') ? undef : 1 } - -sub acl { - shift; - print "acl @_\n"; - if ($_[0] eq 'error') { - return; - } elsif ($_[1] eq 'empty') { - $okay = 1; - return; - } else { - return 'acl'; - } -} - -sub attr { - shift; - print "attr @_\n"; - if ($_[0] eq 'error') { - return; - } elsif ($_[1] eq 'empty') { - $okay = 1; - return; - } elsif (@_ == 3) { - return ('attr1', 'attr2'); - } else { - return 'attr'; - } -} - -sub autocreate { - shift; - print "autocreate @_\n"; - return ($_[0] eq 'error') ? undef : 1 -} - -sub check { - shift; - print "check @_\n"; - if ($_[0] eq 'error') { - return; - } elsif ($_[1] eq 'empty') { - return 0; - } else { - return 1; - } -} - -sub expires { - shift; - print "expires @_\n"; - if ($_[0] eq 'error') { - return; - } elsif ($_[1] eq 'empty') { - $okay = 1; - return; - } else { - return 'expires'; - } -} - -sub get { - shift; - print "get @_\n"; - return if $_[0] eq 'error'; - return 'get'; -} - -sub history { - shift; - print "history @_\n"; - return if $_[0] eq 'error'; - return 'history'; -} - -sub owner { - shift; - print "owner @_\n"; - if ($_[0] eq 'error') { - return; - } elsif ($_[1] eq 'empty') { - $okay = 1; - return; - } else { - return 'owner'; - } -} - -sub show { - shift; - print "show @_\n"; - return if $_[0] eq 'error'; - return 'show'; -} - -# Back to the main package and the actual test suite. Lie about whether the -# Wallet::Server package has already been loaded. -package main; -$INC{'Wallet/Server.pm'} = 'FAKE'; -my $OUTPUT; -our $SYSLOG = \$OUTPUT; -eval { do "$ENV{SOURCE}/../server/wallet-backend" }; - -# Run the wallet backend. This fun hack takes advantage of the fact that the -# wallet backend is written in Perl so that we can substitute our own -# Wallet::Server class. -sub run_backend { - my (@args) = @_; - my $result = ''; - open (OUTPUT, '>', \$result) or die "cannot create output string: $!\n"; - select OUTPUT; - local $| = 1; - eval { command (@args) }; - my $error = $@; - select STDOUT; - return ($result, $error); -} - -# Now for the actual tests. First, check for lack of trace information. -my ($out, $err) = run_backend; -is ($err, "REMOTE_USER not set\n", 'REMOTE_USER required'); -is ($OUTPUT, "error: REMOTE_USER not set\n", ' and syslog correct'); -$ENV{REMOTE_USER} = 'admin'; -($out, $err) = run_backend; -is ($err, "neither REMOTE_HOST nor REMOTE_ADDR set\n", - 'REMOTE_HOST or _ADDR required'); -is ($OUTPUT, "error for admin: neither REMOTE_HOST nor REMOTE_ADDR set\n", - ' and syslog correct'); -$ENV{REMOTE_ADDR} = '1.2.3.4'; -my $new = 'new admin 1.2.3.4'; - -# Check unknown commands. -($out, $err) = run_backend ('foo'); -is ($err, "unknown command foo\n", 'Unknown command'); -is ($OUTPUT, "error for admin (1.2.3.4): unknown command foo\n", - ' and syslog correct'); -is ($out, "$new\n", ' and nothing ran'); -($out, $err) = run_backend ('acl', 'foo'); -is ($err, "unknown command acl foo\n", 'Unknown ACL command'); -is ($OUTPUT, "error for admin (1.2.3.4): unknown command acl foo\n", - ' and syslog correct'); -is ($out, "$new\n", ' and nothing ran'); -($out, $err) = run_backend ('flag', 'foo', 'service', 'foo', 'foo'); -is ($err, "unknown command flag foo\n", 'Unknown flag command'); -is ($OUTPUT, "error for admin (1.2.3.4): unknown command flag foo\n", - ' and syslog correct'); -is ($out, "$new\n", ' and nothing ran'); - -# Check too few, too many, and bad arguments for every command. -my %commands = (autocreate => [2, 2], - check => [2, 2], - create => [2, 2], - destroy => [2, 2], - expires => [2, 4], - get => [2, 2], - getacl => [3, 3], - getattr => [3, 3], - history => [2, 2], - owner => [2, 3], - setacl => [4, 4], - setattr => [4, 9], - show => [2, 2], - store => [3, 3]); -my %acl_commands = (add => [3, 3], - create => [1, 1], - destroy => [1, 1], - history => [1, 1], - remove => [3, 3], - rename => [2, 2], - show => [1, 1]); -my %flag_commands = (clear => [3, 3], - set => [3, 3]); -for my $command (sort keys %commands) { - my ($min, $max) = @{ $commands{$command} }; - ($out, $err) = run_backend ($command, ('foo') x ($min - 1)); - is ($err, "insufficient arguments\n", "Too few arguments for $command"); - is ($OUTPUT, "error for admin (1.2.3.4): insufficient arguments\n", - ' and syslog correct'); - is ($out, "$new\n", ' and nothing ran'); - unless ($max >= 9) { - ($out, $err) = run_backend ($command, ('foo') x ($max + 1)); - is ($err, "too many arguments\n", "Too many arguments for $command"); - is ($OUTPUT, "error for admin (1.2.3.4): too many arguments\n", - ' and syslog correct'); - is ($out, "$new\n", ' and nothing ran'); - } - my @base = ('foobar') x $max; - for my $arg (0 .. ($max - 1)) { - my @args = @base; - $args[$arg] = 'foo;bar'; - ($out, $err) = run_backend ($command, @args); - if ($command eq 'store' and $arg == 2) { - is ($err, '', 'Store allows any characters'); - is ($OUTPUT, "command $command @args[0,1] from admin (1.2.3.4)" - . " succeeded\n", ' and success logged'); - is ($out, "$new\nstore foobar foobar foo;bar\n", - ' and calls the right method'); - } else { - is ($err, "invalid characters in argument: foo;bar\n", - "Invalid arguments for $command $arg"); - is ($OUTPUT, "error for admin (1.2.3.4): invalid characters in" - . " argument: foo;bar\n", ' and syslog correct'); - is ($out, "$new\n", ' and nothing ran'); - } - } -} -for my $command (sort keys %acl_commands) { - my ($min, $max) = @{ $acl_commands{$command} }; - ($out, $err) = run_backend ('acl', $command, ('foo') x ($min - 1)); - is ($err, "insufficient arguments\n", - "Too few arguments for acl $command"); - is ($OUTPUT, "error for admin (1.2.3.4): insufficient arguments\n", - ' and syslog correct'); - is ($out, "$new\n", ' and nothing ran'); - ($out, $err) = run_backend ('acl', $command, ('foo') x ($max + 1)); - is ($err, "too many arguments\n", "Too many arguments for acl $command"); - is ($OUTPUT, "error for admin (1.2.3.4): too many arguments\n", - ' and syslog correct'); - is ($out, "$new\n", ' and nothing ran'); - my @base = ('foobar') x $max; - for my $arg (0 .. ($max - 1)) { - my @args = @base; - $args[$arg] = 'foo;bar'; - ($out, $err) = run_backend ('acl', $command, @args); - is ($err, "invalid characters in argument: foo;bar\n", - "Invalid arguments for acl $command $arg"); - is ($OUTPUT, "error for admin (1.2.3.4): invalid characters in" - . " argument: foo;bar\n", ' and syslog correct'); - is ($out, "$new\n", ' and nothing ran'); - } -} -for my $command (sort keys %flag_commands) { - my ($min, $max) = @{ $flag_commands{$command} }; - ($out, $err) = run_backend ('flag', $command, ('foo') x ($min - 1)); - is ($err, "insufficient arguments\n", - "Too few arguments for flag $command"); - is ($OUTPUT, "error for admin (1.2.3.4): insufficient arguments\n", - ' and syslog correct'); - is ($out, "$new\n", ' and nothing ran'); - ($out, $err) = run_backend ('flag', $command, ('foo') x ($max + 1)); - is ($err, "too many arguments\n", "Too many arguments for flag $command"); - is ($OUTPUT, "error for admin (1.2.3.4): too many arguments\n", - ' and syslog correct'); - is ($out, "$new\n", ' and nothing ran'); - my @base = ('foobar') x $max; - for my $arg (0 .. ($max - 1)) { - my @args = @base; - $args[$arg] = 'foo;bar'; - ($out, $err) = run_backend ('flag', $command, @args); - is ($err, "invalid characters in argument: foo;bar\n", - "Invalid arguments for flag $command $arg"); - is ($OUTPUT, "error for admin (1.2.3.4): invalid characters in" - . " argument: foo;bar\n", ' and syslog correct'); - is ($out, "$new\n", ' and nothing ran'); - } -} - -# Now, test that we ran the right functions and passed the correct arguments. -my $error = 1; -for my $command (qw/autocreate create destroy setacl setattr store/) { - my $method = { setacl => 'acl', setattr => 'attr' }->{$command}; - $method ||= $command; - my @extra = ('foo') x ($commands{$command}[0] - 2); - my $extra = @extra ? join (' ', '', @extra) : ''; - ($out, $err) = run_backend ($command, 'type', 'name', @extra); - my $ran; - if ($command eq 'store') { - $ran = "$command type name"; - } else { - $ran = "$command type name" . (@extra ? " @extra" : ''); - } - is ($err, '', "Command $command ran with no errors"); - is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", - ' and success logged'); - is ($out, "$new\n$method type name$extra\n", - ' and ran the right method'); - ($out, $err) = run_backend ($command, 'error', 'name', @extra); - if ($command eq 'store') { - $ran = "$command error name"; - } else { - $ran = "$command error name" . (@extra ? " @extra" : ''); - } - is ($err, "error count $error\n", "Command $command ran with errors"); - is ($OUTPUT, "command $ran from admin (1.2.3.4) failed: error count" - . " $error\n", ' and syslog correct'); - is ($out, "$new\n$method error name$extra\n", - ' and ran the right method'); - $error++; -} -for my $command (qw/check expires get getacl getattr history owner show/) { - my $method = { getacl => 'acl', getattr => 'attr' }->{$command}; - $method ||= $command; - my @extra = ('foo') x ($commands{$command}[0] - 2); - my $extra = @extra ? join (' ', '', @extra) : ''; - ($out, $err) = run_backend ($command, 'type', 'name', @extra); - my $ran = "$command type name" . (@extra ? " @extra" : ''); - is ($err, '', "Command $command ran with no errors"); - is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", - ' and success logged'); - if ($command eq 'getattr') { - is ($out, "$new\n$method type name$extra\nattr1\nattr2\n", - ' and ran the right method with output'); - } elsif ($command eq 'check') { - is ($out, "$new\n$method type name$extra\nyes\n", - ' and ran the right method with output'); - } else { - my $newline = ($command =~ /^(get|history|show)\z/) ? '' : "\n"; - is ($out, "$new\n$method type name$extra\n$method$newline", - ' and ran the right method with output'); - } - if ($command eq 'expires' or $command eq 'owner') { - ($out, $err) = run_backend ($command, 'type', 'name', @extra, 'foo'); - my $ran = "$command type name" . (@extra ? " @extra" : '') . ' foo'; - is ($err, '', "Command $command ran with no errors (setting)"); - is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", - ' and success logged'); - is ($out, "$new\n$method type name$extra foo\n", - ' and ran the right method'); - } - if ($command eq 'expires' or $command eq 'getacl' or $command eq 'owner') { - ($out, $err) = run_backend ($command, 'type', 'empty', @extra); - my $ran = "$command type empty" . (@extra ? " @extra" : ''); - is ($err, '', "Command $command ran with no errors (empty)"); - is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", - ' and success logged'); - my $desc; - if ($command eq 'expires') { $desc = 'expiration' } - elsif ($command eq 'getacl') { $desc = 'ACL' } - elsif ($command eq 'owner') { $desc = 'owner' } - is ($out, "$new\n$method type empty$extra\nNo $desc set\n", - ' and ran the right method with output'); - $error++; - } elsif ($command eq 'getattr') { - ($out, $err) = run_backend ($command, 'type', 'empty', @extra); - my $ran = "$command type empty" . (@extra ? " @extra" : ''); - is ($err, '', "Command $command ran with no errors (empty)"); - is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", - ' and success logged'); - is ($out, "$new\n$method type empty$extra\n", - ' and ran the right method with output'); - $error++; - } elsif ($command eq 'check') { - ($out, $err) = run_backend ($command, 'type', 'empty', @extra); - my $ran = "$command type empty" . (@extra ? " @extra" : ''); - is ($err, '', "Command $command ran with no errors (empty)"); - is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", - ' and success logged'); - is ($out, "$new\n$method type empty$extra\nno\n", - ' and ran the right method with output'); - } - ($out, $err) = run_backend ($command, 'error', 'name', @extra); - my $ran = "$command error name" . (@extra ? " @extra" : ''); - is ($err, "error count $error\n", "Command $command ran with errors"); - is ($OUTPUT, "command $ran from admin (1.2.3.4) failed: error count" - . " $error\n", ' and syslog correct'); - is ($out, "$new\n$method error name$extra\n", - ' and ran the right method'); - $error++; -} -for my $command (sort keys %acl_commands) { - my @extra = ('foo') x ($acl_commands{$command}[0] - 1); - my $extra = @extra ? join (' ', '', @extra) : ''; - ($out, $err) = run_backend ('acl', $command, 'name', @extra); - my $ran = "acl $command name" . (@extra ? " @extra" : ''); - is ($err, '', "Command acl $command ran with no errors"); - is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", - ' and success logged'); - my $expected; - if ($command eq 'show') { - $expected = "$new\nacl_$command name$extra\nacl_show"; - } elsif ($command eq 'history') { - $expected = "$new\nacl_$command name$extra\nacl_history"; - } else { - $expected = "$new\nacl_$command name$extra\n"; - } - is ($out, $expected, ' and ran the right method'); - ($out, $err) = run_backend ('acl', $command, 'error', @extra); - $ran = "acl $command error" . (@extra ? " @extra" : ''); - is ($err, "error count $error\n", "Command acl $command ran with errors"); - is ($OUTPUT, "command $ran from admin (1.2.3.4) failed: error count" - . " $error\n", ' and syslog correct'); - is ($out, "$new\nacl_$command error$extra\n", - ' and ran the right method'); - $error++; -} -for my $command (sort keys %flag_commands) { - my @extra = ('foo') x ($flag_commands{$command}[0] - 2); - my $extra = @extra ? join (' ', '', @extra) : ''; - ($out, $err) = run_backend ('flag', $command, 'type', 'name', @extra); - my $ran = "flag $command type name" . (@extra ? " @extra" : ''); - is ($err, '', "Command flag $command ran with no errors"); - is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", - ' and success logged'); - is ($out, "$new\nflag_$command type name$extra\n", - ' and ran the right method'); - ($out, $err) = run_backend ('flag', $command, 'error', 'name', @extra); - $ran = "flag $command error name" . (@extra ? " @extra" : ''); - is ($err, "error count $error\n", - "Command flag $command ran with errors"); - is ($OUTPUT, "command $ran from admin (1.2.3.4) failed: error count" - . " $error\n", ' and syslog correct'); - is ($out, "$new\nflag_$command error name$extra\n", - ' and ran the right method'); - $error++; -} - -# Almost done. All that remains is to test the robustness of the bad -# character checks against every possible character and test permitting the -# empty argument. -($out, $err) = run_backend ('show', 'type', ''); -is ($err, '', 'Allowed the empty argument'); -is ($OUTPUT, "command show type from admin (1.2.3.4) succeeded\n", - ' and success logged'); -my $ok = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_/.@-'; -($out, $err) = run_backend ('show', 'type', $ok); -is ($err, '', 'Allowed all valid characters'); -is ($OUTPUT, "command show type $ok from admin (1.2.3.4) succeeded\n", - ' and success logged'); -is ($out, "$new\nshow type $ok\nshow", ' and returned the right output'); -for my $n (0 .. 255) { - my $c = chr ($n); - my $name = $ok . $c; - ($out, $err) = run_backend ('show', 'type', $name); - if (index ($ok, $c) == -1) { - is ($err, "invalid characters in argument: $name\n", - "Rejected invalid character $n"); - my $stripped = $name; - $stripped =~ s/[^\x20-\x7e]/_/g; - is ($OUTPUT, "error for admin (1.2.3.4): invalid characters in" - . " argument: $stripped\n", ' and syslog correct'); - is ($out, "$new\n", ' and did nothing'); - } else { - is ($err, '', "Accepted valid character $n"); - is ($OUTPUT, "command show type $name from admin (1.2.3.4)" - . " succeeded\n", ' and success logged'); - is ($out, "$new\nshow type $name\nshow", ' and ran the method'); - } -} diff --git a/tests/server/keytab-t b/tests/server/keytab-t new file mode 100755 index 0000000..2a0ceed --- /dev/null +++ b/tests/server/keytab-t @@ -0,0 +1,88 @@ +#!/usr/bin/perl -w +# +# Tests for the keytab-backend dispatch code. +# +# Written by Russ Allbery +# Copyright 2006, 2007, 2010 Board of Trustees, Leland Stanford Jr. University +# +# See LICENSE for licensing terms. + +use strict; +use vars qw($CONFIG $KADMIN $SYSLOG $TMP); + +use Test::More tests => 63; + +# Load the keytab-backend code and override various settings. +my $OUTPUT; +$SYSLOG = \$OUTPUT; +eval { do "$ENV{SOURCE}/../server/keytab-backend" }; +$CONFIG = "$ENV{SOURCE}/data/allow-extract"; +$KADMIN = "$ENV{SOURCE}/data/fake-kadmin"; +$TMP = '.'; + +# Run the keytab backend. +sub run_backend { + my (@args) = @_; + my $result = ''; + open (OUTPUT, '>', \$result) or die "cannot create output string: $!\n"; + select OUTPUT; + local $| = 1; + eval { download (@args) }; + my $error = $@; + select STDOUT; + return ($result, $error); +} + +# The actual tests. +$ENV{REMOTE_USER} = 'admin'; +my ($out, $err) = run_backend (); +is ($err, "keytab-backend: invalid arguments: \n", 'Fails with no arguments'); +is ($OUTPUT, "invalid arguments: \n", ' and syslog matches'); +is ($out, '', ' and produces no output'); +($out, $err) = run_backend ('foo', 'bar', 'baz'); +is ($err, "keytab-backend: invalid arguments: foo bar baz\n", + 'Fails with three arguments'); +is ($OUTPUT, "invalid arguments: foo bar baz\n", ' and syslog matches'); +is ($out, '', ' and produces no output'); +for my $bad (qw{service service\*@example =@example host/foo+bar@example + rcmd.foo@EXAMPLE host/foo/bar@EXAMPLE /bar@EXAMPLE.NET + bar/@EXAMPLE.NET bar/bar@}) { + ($out, $err) = run_backend ('keytab', $bad); + is ($err, "keytab-backend: bad principal name $bad\n", + "Invalid principal $bad"); + is ($OUTPUT, "bad principal name $bad\n", ' and syslog matches'); + is ($out, '', ' and produces no output'); +} +for my $bad (qw{service/foo@EXAMPLE.ORGA bar@EXAMPLE.NET + host/example.net@EXAMPLE.ORG aservice/foo@EXAMPLE.ORG}) { + ($out, $err) = run_backend ('keytab', $bad); + is ($err, + "keytab-backend: permission denied: admin may not retrieve $bad\n", + "Permission denied for $bad"); + is ($OUTPUT, "permission denied: admin may not retrieve $bad\n", + ' and syslog matches'); + is ($out, '', ' and produces no output'); +} +for my $good (qw{service/foo@EXAMPLE.ORG foo/bar@EXAMPLE.NET + host/example.org@EXAMPLE.ORG}) { + ($out, $err) = run_backend ($good); + is ($err, '', "Success for good keytab $good"); + is ($out, "$good\n", ' and the right output'); + is ($OUTPUT, "keytab $good retrieved by admin\n", ' and syslog is right'); + ok (! -f "$TMP/keytab$$", ' and the file is gone'); +} +($out, $err) = run_backend ('keytab', 'error@EXAMPLE.ORG'); +is ($err, "keytab-backend: retrieve of error\@EXAMPLE.ORG failed for" + . " admin: kadmin.local exited with status 1\n", + 'Good error on kadmin failure'); +is ($OUTPUT, "retrieve of error\@EXAMPLE.ORG failed for admin: kadmin.local" + . " exited with status 1\n", ' and syslog matches'); +is ($out, '', ' and no output'); + +# Test a configuration failure. +$CONFIG = '/path/to/bad/file'; +($out, $err) = run_backend ('get', 'service/foo@EXAMPLE.ORG'); +like ($err, qr{^keytab-backend: cannot open /path/to/bad/file: }, + 'Fails with bad configuration file'); +like ($OUTPUT, qr{^cannot open /path/to/bad/file: }, ' and syslog matches'); +is ($out, '', ' and produces no output'); diff --git a/tests/server/keytab-t.in b/tests/server/keytab-t.in deleted file mode 100644 index 2a0ceed..0000000 --- a/tests/server/keytab-t.in +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/perl -w -# -# Tests for the keytab-backend dispatch code. -# -# Written by Russ Allbery -# Copyright 2006, 2007, 2010 Board of Trustees, Leland Stanford Jr. University -# -# See LICENSE for licensing terms. - -use strict; -use vars qw($CONFIG $KADMIN $SYSLOG $TMP); - -use Test::More tests => 63; - -# Load the keytab-backend code and override various settings. -my $OUTPUT; -$SYSLOG = \$OUTPUT; -eval { do "$ENV{SOURCE}/../server/keytab-backend" }; -$CONFIG = "$ENV{SOURCE}/data/allow-extract"; -$KADMIN = "$ENV{SOURCE}/data/fake-kadmin"; -$TMP = '.'; - -# Run the keytab backend. -sub run_backend { - my (@args) = @_; - my $result = ''; - open (OUTPUT, '>', \$result) or die "cannot create output string: $!\n"; - select OUTPUT; - local $| = 1; - eval { download (@args) }; - my $error = $@; - select STDOUT; - return ($result, $error); -} - -# The actual tests. -$ENV{REMOTE_USER} = 'admin'; -my ($out, $err) = run_backend (); -is ($err, "keytab-backend: invalid arguments: \n", 'Fails with no arguments'); -is ($OUTPUT, "invalid arguments: \n", ' and syslog matches'); -is ($out, '', ' and produces no output'); -($out, $err) = run_backend ('foo', 'bar', 'baz'); -is ($err, "keytab-backend: invalid arguments: foo bar baz\n", - 'Fails with three arguments'); -is ($OUTPUT, "invalid arguments: foo bar baz\n", ' and syslog matches'); -is ($out, '', ' and produces no output'); -for my $bad (qw{service service\*@example =@example host/foo+bar@example - rcmd.foo@EXAMPLE host/foo/bar@EXAMPLE /bar@EXAMPLE.NET - bar/@EXAMPLE.NET bar/bar@}) { - ($out, $err) = run_backend ('keytab', $bad); - is ($err, "keytab-backend: bad principal name $bad\n", - "Invalid principal $bad"); - is ($OUTPUT, "bad principal name $bad\n", ' and syslog matches'); - is ($out, '', ' and produces no output'); -} -for my $bad (qw{service/foo@EXAMPLE.ORGA bar@EXAMPLE.NET - host/example.net@EXAMPLE.ORG aservice/foo@EXAMPLE.ORG}) { - ($out, $err) = run_backend ('keytab', $bad); - is ($err, - "keytab-backend: permission denied: admin may not retrieve $bad\n", - "Permission denied for $bad"); - is ($OUTPUT, "permission denied: admin may not retrieve $bad\n", - ' and syslog matches'); - is ($out, '', ' and produces no output'); -} -for my $good (qw{service/foo@EXAMPLE.ORG foo/bar@EXAMPLE.NET - host/example.org@EXAMPLE.ORG}) { - ($out, $err) = run_backend ($good); - is ($err, '', "Success for good keytab $good"); - is ($out, "$good\n", ' and the right output'); - is ($OUTPUT, "keytab $good retrieved by admin\n", ' and syslog is right'); - ok (! -f "$TMP/keytab$$", ' and the file is gone'); -} -($out, $err) = run_backend ('keytab', 'error@EXAMPLE.ORG'); -is ($err, "keytab-backend: retrieve of error\@EXAMPLE.ORG failed for" - . " admin: kadmin.local exited with status 1\n", - 'Good error on kadmin failure'); -is ($OUTPUT, "retrieve of error\@EXAMPLE.ORG failed for admin: kadmin.local" - . " exited with status 1\n", ' and syslog matches'); -is ($out, '', ' and no output'); - -# Test a configuration failure. -$CONFIG = '/path/to/bad/file'; -($out, $err) = run_backend ('get', 'service/foo@EXAMPLE.ORG'); -like ($err, qr{^keytab-backend: cannot open /path/to/bad/file: }, - 'Fails with bad configuration file'); -like ($OUTPUT, qr{^cannot open /path/to/bad/file: }, ' and syslog matches'); -is ($out, '', ' and produces no output'); diff --git a/tests/server/pod-t b/tests/server/pod-t new file mode 100755 index 0000000..52d81eb --- /dev/null +++ b/tests/server/pod-t @@ -0,0 +1,22 @@ +#!/usr/bin/perl +# +# Test POD formatting for client documentation. +# +# Written by Russ Allbery +# Copyright 2008, 2010 Board of Trustees, Leland Stanford Jr. University +# +# See LICENSE for licensing terms. + +use Test::More; + +my @files = qw(keytab-backend wallet-admin wallet-backend); +my $total = scalar (@files); +plan tests => $total; + +eval 'use Test::Pod 1.00'; +SKIP: { + skip 'Test::Pod 1.00 required for testing POD', $total if $@; + for my $file (@files) { + pod_file_ok ("$ENV{SOURCE}/../server/$file", "server/$file"); + } +} diff --git a/tests/server/pod-t.in b/tests/server/pod-t.in deleted file mode 100644 index 52d81eb..0000000 --- a/tests/server/pod-t.in +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/perl -# -# Test POD formatting for client documentation. -# -# Written by Russ Allbery -# Copyright 2008, 2010 Board of Trustees, Leland Stanford Jr. University -# -# See LICENSE for licensing terms. - -use Test::More; - -my @files = qw(keytab-backend wallet-admin wallet-backend); -my $total = scalar (@files); -plan tests => $total; - -eval 'use Test::Pod 1.00'; -SKIP: { - skip 'Test::Pod 1.00 required for testing POD', $total if $@; - for my $file (@files) { - pod_file_ok ("$ENV{SOURCE}/../server/$file", "server/$file"); - } -} -- cgit v1.2.3 From 0f81ba24e021a63d42c51ee9bec6e521fc540251 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Tue, 9 Feb 2010 22:13:53 -0800 Subject: Fix multiple builddir != srcdir issues with test suite Simplify the build rules for the test suite to take advantage of the improved runtests support for builddir != srcdir. Stop doing Autoconf substitution on full.conf now that we have that support. --- .gitignore | 1 - Makefile.am | 34 +++++++++++++--------------------- configure.ac | 5 ++++- tests/client/basic-t.in | 2 +- tests/client/full-t.in | 7 +++++-- tests/client/prompt-t.in | 2 +- tests/data/full.conf | 3 +++ tests/data/full.conf.in | 3 --- 8 files changed, 27 insertions(+), 30 deletions(-) create mode 100644 tests/data/full.conf delete mode 100644 tests/data/full.conf.in (limited to 'configure.ac') diff --git a/.gitignore b/.gitignore index b0a49df..3778ee8 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,6 @@ /tests/client/basic-t /tests/client/full-t /tests/client/prompt-t -/tests/data/full.conf /tests/data/test.keytab /tests/data/test.password /tests/data/test.principal diff --git a/Makefile.am b/Makefile.am index 77514a7..d4dc8a5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,20 +23,19 @@ PERL_FILES = perl/Wallet/ACL.pm perl/Wallet/ACL/Base.pm \ perl/t/keytab.t perl/t/lib/Util.pm perl/t/object.t perl/t/pod.t \ perl/t/schema.t perl/t/server.t perl/t/verifier-netdb.t \ perl/t/verifier.t -TEST_FILES = tests/TESTS tests/data/README tests/data/allow-extract \ - tests/data/basic.conf tests/data/cmd-fake tests/data/fake-data \ - tests/data/fake-kadmin tests/data/fake-keytab \ - tests/data/fake-keytab-2 tests/data/fake-keytab-merge \ - tests/data/fake-keytab-old tests/data/fake-srvtab \ - tests/data/wallet.conf tests/libtest.sh AUTOMAKE_OPTIONS = foreign subdir-objects ACLOCAL_AMFLAGS = -I m4 -EXTRA_DIST = LICENSE autogen client/wallet.pod config/allow-extract \ - config/keytab config/keytab.acl config/wallet docs/design \ - contrib/README contrib/wallet-report contrib/wallet-report.8 \ - docs/design-acl docs/design-api docs/netdb-role-api docs/notes \ - docs/setup examples/stanford.conf $(PERL_FILES) $(TEST_FILES) +EXTRA_DIST = LICENSE autogen client/wallet.pod config/allow-extract \ + config/keytab config/keytab.acl config/wallet docs/design \ + contrib/README contrib/wallet-report contrib/wallet-report.8 \ + docs/design-acl docs/design-api docs/netdb-role-api docs/notes \ + docs/setup examples/stanford.conf tests/TESTS tests/data/README \ + tests/data/allow-extract tests/data/basic.conf tests/data/cmd-fake \ + tests/data/cmd-wrapper tests/data/fake-data tests/data/fake-kadmin \ + tests/data/fake-keytab tests/data/fake-keytab-2 \ + tests/data/fake-keytab-merge tests/data/fake-keytab-old \ + tests/data/fake-srvtab tests/data/wallet.conf $(PERL_FILES) noinst_LIBRARIES = portable/libportable.a util/libutil.a portable_libportable_a_SOURCES = portable/dummy.c portable/krb5-extra.c \ @@ -89,8 +88,8 @@ all-local: perl/blib/lib/Wallet/Config.pm perl/blib/lib/Wallet/Config.pm: set -e; if [ x"$(builddir)" != x"$(srcdir)" ] ; then \ mkdir perl/Wallet perl/Wallet/ACL perl/Wallet/ACL/NetDB \ - perl/Wallet/Object perl/t perl/t/data perl/t/lib \ - 2>/dev/null || true ; \ + perl/Wallet/Kadmin perl/Wallet/Object perl/t perl/t/data \ + perl/t/lib 2>/dev/null || true ; \ for f in $(PERL_FILES) ; do \ cp "$(srcdir)/$$f" "$(builddir)/$$f" ; \ done \ @@ -160,14 +159,7 @@ tests_util_messages_t_LDADD = tests/tap/libtap.a util/libutil.a \ tests_util_xmalloc_LDADD = util/libutil.a portable/libportable.a check-local: $(check_PROGRAMS) - set -e; if [ x"$(builddir)" != x"$(srcdir)" ] ; then \ - mkdir tests/data/acls 2>/dev/null || true ; \ - for f in $(TEST_FILES) ; do \ - cp "$(srcdir)/$$f" "$(builddir)/$$f" ; \ - done \ - fi - cd tests && ./runtests TESTS - @echo '' + cd tests && ./runtests $(abs_top_srcdir)/tests/TESTS @echo '' cd perl && $(MAKE) test diff --git a/configure.ac b/configure.ac index 664c6f7..78201c1 100644 --- a/configure.ac +++ b/configure.ac @@ -60,8 +60,11 @@ AS_IF([test x"$REMCTLD" != x], [AC_DEFINE_UNQUOTED([PATH_REMCTLD], ["$REMCTLD"], [Define to the full path to remctld to run remctl tests.])]) +dnl Create the tests/data directory for builds outside the source directory. +AC_CONFIG_COMMANDS([tests/data/.placeholder], [touch tests/data/.placeholder]) + AC_CONFIG_HEADER([config.h]) -AC_CONFIG_FILES([Makefile perl/Makefile.PL tests/data/full.conf]) +AC_CONFIG_FILES([Makefile perl/Makefile.PL]) AC_CONFIG_FILES([tests/client/basic-t], [chmod +x tests/client/basic-t]) AC_CONFIG_FILES([tests/client/full-t], [chmod +x tests/client/full-t]) AC_CONFIG_FILES([tests/client/prompt-t], [chmod +x tests/client/prompt-t]) diff --git a/tests/client/basic-t.in b/tests/client/basic-t.in index 1dbc0b9..30bc004 100644 --- a/tests/client/basic-t.in +++ b/tests/client/basic-t.in @@ -12,7 +12,7 @@ . "$SOURCE/tap/libtap.sh" . "$SOURCE/tap/kerberos.sh" . "$SOURCE/tap/remctl.sh" -cd "$BUILD" +cd "$SOURCE" # We need a modified krb5.conf file to test wallet configuration settings in # krb5.conf. Despite the hard-coding of test-k5.stanford.edu, this test isn't diff --git a/tests/client/full-t.in b/tests/client/full-t.in index a4ca19d..8acc360 100644 --- a/tests/client/full-t.in +++ b/tests/client/full-t.in @@ -51,6 +51,9 @@ sub wallet { return ($output, $error, $status); } +# cd to the correct directory. +chdir "$ENV{SOURCE}" or die "Cannot chdir to $ENV{SOURCE}: $!\n"; + SKIP: { skip 'no keytab configuration', $total unless -f "$ENV{BUILD}/data/test.keytab"; @@ -62,8 +65,8 @@ SKIP: { unlink ('krb5cc_test', 'test-pid'); my $principal = contents ("$ENV{BUILD}/data/test.principal"); remctld_spawn ($remctld, $principal, - '@abs_top_builddir@/tests/data/test.keytab', - '@abs_top_builddir@/tests/data/full.conf'); + "$ENV{BUILD}/data/test.keytab", + "$ENV{SOURCE}/data/full.conf"); $ENV{KRB5CCNAME} = 'krb5cc_test'; getcreds ("$ENV{BUILD}/data/test.keytab", $principal); diff --git a/tests/client/prompt-t.in b/tests/client/prompt-t.in index e037b3f..1d8b079 100644 --- a/tests/client/prompt-t.in +++ b/tests/client/prompt-t.in @@ -36,7 +36,7 @@ SKIP: { unlink ('krb5cc_test', 'test-pid'); my $principal = contents ("$ENV{BUILD}/data/test.principal"); remctld_spawn ($remctld, $principal, "$ENV{BUILD}/data/test.keytab", - "$ENV{BUILD}/data/basic.conf"); + "$ENV{SOURCE}/data/basic.conf"); $ENV{KRB5CCNAME} = 'krb5cc_test'; # Read in the principal and password. diff --git a/tests/data/full.conf b/tests/data/full.conf new file mode 100644 index 0000000..4c0f435 --- /dev/null +++ b/tests/data/full.conf @@ -0,0 +1,3 @@ +# remctl configuration for full wallet client tests. + +wallet ALL data/cmd-wrapper ANYUSER diff --git a/tests/data/full.conf.in b/tests/data/full.conf.in deleted file mode 100644 index 25aef9e..0000000 --- a/tests/data/full.conf.in +++ /dev/null @@ -1,3 +0,0 @@ -# remctl configuration for full wallet client tests. - -wallet ALL @abs_top_builddir@/tests/data/cmd-wrapper ANYUSER -- cgit v1.2.3 From cf79136dd24f39925ddfe5ff4e9388309089bb04 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Sat, 20 Feb 2010 22:51:38 -0800 Subject: Release 0.10 --- NEWS | 2 +- README | 2 +- configure.ac | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'configure.ac') diff --git a/NEWS b/NEWS index b4c31d4..4c8bda6 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,6 @@ User-Visible wallet Changes -wallet 0.10 (unreleased) +wallet 0.10 (2010-02-21) Add support for Heimdal KDCs as well as MIT Kerberos KDCs. There is now a mandatory new setting in Wallet::Config: $KEYTAB_KRBTYPE. It diff --git a/README b/README index d3ef5fb..86b0ac4 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ - wallet release 0.9 + wallet release 0.10 (secure data management system) Written by Russ Allbery diff --git a/configure.ac b/configure.ac index 78201c1..5c3da92 100644 --- a/configure.ac +++ b/configure.ac @@ -9,7 +9,7 @@ dnl See LICENSE for licensing terms. dnl We cannot use -Wall -Werror with AM_INIT_AUTOMAKE since we override dnl distuninstallcheck (not supported by Perl). AC_PREREQ([2.64]) -AC_INIT([wallet], [0.9], [rra@stanford.edu]) +AC_INIT([wallet], [0.10], [rra@stanford.edu]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_LIBOBJ_DIR([portable]) AC_CONFIG_MACRO_DIR([m4]) -- cgit v1.2.3 From 02eedafcb393ef771ab6231d6583c58d12b97837 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Sat, 20 Feb 2010 22:57:40 -0800 Subject: Add portable/uio.h and include it in client/wallet.c Now that the wallet client uses struct iovec, it needs to include the relevant header file. Import the portability layer for possible future Windows support. --- LICENSE | 5 +++-- Makefile.am | 2 +- client/wallet.c | 1 + configure.ac | 2 +- portable/uio.h | 27 +++++++++++++++++++++++++++ 5 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 portable/uio.h (limited to 'configure.ac') diff --git a/LICENSE b/LICENSE index 8eca7ad..bd01ed1 100644 --- a/LICENSE +++ b/LICENSE @@ -74,8 +74,9 @@ license: OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. The files portable/asprintf.c, portable/dummy.c, portable/macros.h, -portable/stdbool.h, portable/strlcat.c, portable/strlcpy.c, and -util/concat.c have been placed in the public domain by their author. +portable/stdbool.h, portable/strlcat.c, portable/strlcpy.c, +portable/uio.h, and util/concat.c have been placed in the public domain by +their author. The file portable/snprintf.c is released under the following license: diff --git a/Makefile.am b/Makefile.am index 05ffe53..162a0f1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -44,7 +44,7 @@ EXTRA_DIST = LICENSE autogen client/wallet.pod config/allow-extract \ noinst_LIBRARIES = portable/libportable.a util/libutil.a portable_libportable_a_SOURCES = portable/dummy.c portable/krb5-extra.c \ portable/krb5.h portable/macros.h portable/stdbool.h \ - portable/system.h + portable/system.h portable/uio.h portable_libportable_a_CPPFLAGS = $(KRB5_CPPFLAGS) portable_libportable_a_LIBADD = $(LIBOBJS) util_libutil_a_SOURCES = util/concat.c util/concat.h util/macros.h \ diff --git a/client/wallet.c b/client/wallet.c index dc4fe18..e6d8eb9 100644 --- a/client/wallet.c +++ b/client/wallet.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include diff --git a/configure.ac b/configure.ac index 5c3da92..c4dc7eb 100644 --- a/configure.ac +++ b/configure.ac @@ -33,7 +33,7 @@ AC_CHECK_MEMBERS([krb5_keytab_entry.keyblock], , , [#include ]) RRA_LIB_KRB5_RESTORE AC_HEADER_STDBOOL -AC_CHECK_HEADERS([sys/bitypes.h syslog.h]) +AC_CHECK_HEADERS([sys/bitypes.h sys/uio.h syslog.h]) AC_CHECK_DECLS([snprintf, vsnprintf]) RRA_C_C99_VAMACROS RRA_C_GNU_VAMACROS diff --git a/portable/uio.h b/portable/uio.h new file mode 100644 index 0000000..3c9e840 --- /dev/null +++ b/portable/uio.h @@ -0,0 +1,27 @@ +/* + * Portability wrapper around . + * + * Provides a definition of the iovec struct for platforms that don't have it + * (primarily Windows). Currently, the corresponding readv and writev + * functions are not provided or prototyped here. + * + * Written by Russ Allbery + * This work is hereby placed in the public domain by its author. + */ + +#ifndef PORTABLE_UIO_H +#define PORTABLE_UIO_H 1 + +#include + +/* remctl.h provides its own definition of this struct on Windows. */ +#if defined(HAVE_SYS_UIO_H) +# include +#elif !defined(REMCTL_H) +struct iovec { + void *iov_base; + size_t iov_len; +}; +#endif + +#endif /* !PORTABLE_UIO_H */ -- cgit v1.2.3 From 99f8f88be291cf55229e6c2488ff8e916ae901c2 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Mon, 8 Mar 2010 10:55:50 -0800 Subject: Release 0.11 --- NEWS | 2 +- README | 2 +- configure.ac | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'configure.ac') diff --git a/NEWS b/NEWS index 6744475..f9d4a9a 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,6 @@ User-Visible wallet Changes -wallet 0.11 (unreleased) +wallet 0.11 (2010-03-08) When deleting an ACL on the server, verify that the ACL is not referenced by any object first. Database referential integrity should diff --git a/README b/README index 3c8ddbc..4879f97 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ - wallet release 0.10 + wallet release 0.11 (secure data management system) Written by Russ Allbery diff --git a/configure.ac b/configure.ac index c4dc7eb..df97861 100644 --- a/configure.ac +++ b/configure.ac @@ -9,7 +9,7 @@ dnl See LICENSE for licensing terms. dnl We cannot use -Wall -Werror with AM_INIT_AUTOMAKE since we override dnl distuninstallcheck (not supported by Perl). AC_PREREQ([2.64]) -AC_INIT([wallet], [0.10], [rra@stanford.edu]) +AC_INIT([wallet], [0.11], [rra@stanford.edu]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_LIBOBJ_DIR([portable]) AC_CONFIG_MACRO_DIR([m4]) -- cgit v1.2.3 From 7a1d4f9e4b96362edef29f71e848458d619cce3b Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Thu, 29 Jul 2010 17:36:30 -0700 Subject: Add an initial test for wallet-rekey This confirms basic functionality, but doesn't test more interesting things like rekeying multiple keys in the same keytab or skipping principals that aren't from the local realm. --- .gitignore | 1 + Makefile.am | 4 +-- configure.ac | 1 + tests/TESTS | 1 + tests/client/rekey-t.in | 65 +++++++++++++++++++++++++++++++++++++++++++ tests/data/fake-keytab-merge | Bin 666 -> 698 bytes tests/data/fake-keytab-rekey | Bin 0 -> 698 bytes 7 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 tests/client/rekey-t.in create mode 100644 tests/data/fake-keytab-rekey (limited to 'configure.ac') diff --git a/.gitignore b/.gitignore index 67f4760..576e160 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ /tests/client/basic-t /tests/client/full-t /tests/client/prompt-t +/tests/client/rekey-t /tests/data/.placeholder /tests/data/test.keytab /tests/data/test.password diff --git a/Makefile.am b/Makefile.am index 0b5593f..af5f25f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -39,8 +39,8 @@ EXTRA_DIST = .gitignore LICENSE autogen client/wallet.pod \ tests/data/cmd-fake tests/data/cmd-wrapper tests/data/fake-data \ tests/data/fake-kadmin tests/data/fake-keytab \ tests/data/fake-keytab-2 tests/data/fake-keytab-merge \ - tests/data/fake-keytab-old tests/data/fake-srvtab \ - tests/data/full.conf tests/data/wallet.conf \ + tests/data/fake-keytab-old tests/data/fake-keytab-rekey \ + tests/data/fake-srvtab tests/data/full.conf tests/data/wallet.conf \ tests/docs/pod-spelling-t tests/docs/pod-t tests/server/admin-t \ tests/server/backend-t tests/server/keytab-t tests/server/report-t \ tests/tap/kerberos.sh tests/tap/libtap.sh tests/tap/remctl.sh \ diff --git a/configure.ac b/configure.ac index df97861..9f2d284 100644 --- a/configure.ac +++ b/configure.ac @@ -68,4 +68,5 @@ AC_CONFIG_FILES([Makefile perl/Makefile.PL]) AC_CONFIG_FILES([tests/client/basic-t], [chmod +x tests/client/basic-t]) AC_CONFIG_FILES([tests/client/full-t], [chmod +x tests/client/full-t]) AC_CONFIG_FILES([tests/client/prompt-t], [chmod +x tests/client/prompt-t]) +AC_CONFIG_FILES([tests/client/rekey-t], [chmod +x tests/client/rekey-t]) AC_OUTPUT diff --git a/tests/TESTS b/tests/TESTS index 161941c..54b8190 100644 --- a/tests/TESTS +++ b/tests/TESTS @@ -1,6 +1,7 @@ client/basic client/full client/prompt +client/rekey docs/pod docs/pod-spelling portable/asprintf diff --git a/tests/client/rekey-t.in b/tests/client/rekey-t.in new file mode 100644 index 0000000..9127f6c --- /dev/null +++ b/tests/client/rekey-t.in @@ -0,0 +1,65 @@ +#! /bin/sh +# +# Test suite for the wallet-rekey command-line client. +# +# Written by Russ Allbery +# Copyright 2006, 2007, 2008, 2010 +# Board of Trustees, Leland Stanford Jr. University +# +# See LICENSE for licensing terms. + +# Load the test library. +. "$SOURCE/tap/libtap.sh" +. "$SOURCE/tap/kerberos.sh" +. "$SOURCE/tap/remctl.sh" +cd "$SOURCE" + +# We need a modified krb5.conf file to test wallet configuration settings in +# krb5.conf. Despite the hard-coding of test-k5.stanford.edu, this test isn't +# Stanford-specific; it just matches the files that are distributed with the +# package. +krb5conf= +for p in /etc/krb5.conf /usr/local/etc/krb5.conf data/krb5.conf ; do + if [ -r "$p" ] ; then + krb5conf="$p" + sed -e '/^[ ]*test-k5.stanford.edu =/,/}/d' \ + -e 's/\(default_realm.*=\) .*/\1 test-k5.stanford.edu/' \ + -e 's/^[ ]*wallet_.*//' \ + -e '/^[ ]*wallet[ ]*=[ ]*{/,/}/d' \ + "$p" > ./krb5.conf + KRB5_CONFIG="./krb5.conf" + export KRB5_CONFIG + break + fi +done +if [ -z "$krb5conf" ] ; then + skip_all 'no krb5.conf found, put one in tests/data/krb5.conf' +fi + +# Test setup. +kerberos_setup +if [ $? != 0 ] ; then + rm krb5.conf + skip_all 'Kerberos tests not configured' +elif [ -z '@REMCTLD@' ] ; then + rm krb5.conf + skip_all 'No remctld found' +else + plan 2 +fi +remctld_start '@REMCTLD@' "$SOURCE/data/basic.conf" +wallet="$BUILD/../client/wallet-rekey" + +# Rekeying should result in a merged keytab with both the old and new keys. +cp data/fake-keytab-old keytab +ok_program '' 0 '' \ + "$wallet" -k "$principal" -p 14373 -s localhost -c fake-wallet keytab +ktutil_list keytab klist-seen +ktutil_list data/fake-keytab-rekey klist-good +ok '...and the rekeyed keytab is correct' cmp klist-seen klist-good +rm -f keytab klist-good klist-seen + +# Clean up. +rm -f autocreated krb5.conf +remctld_stop +kerberos_cleanup diff --git a/tests/data/fake-keytab-merge b/tests/data/fake-keytab-merge index 31ddc49..4858eb4 100644 Binary files a/tests/data/fake-keytab-merge and b/tests/data/fake-keytab-merge differ diff --git a/tests/data/fake-keytab-rekey b/tests/data/fake-keytab-rekey new file mode 100644 index 0000000..6c9c7f2 Binary files /dev/null and b/tests/data/fake-keytab-rekey differ -- cgit v1.2.3 From 468ded4c2fae05a815bef91bdcb17d52f9cdcb2b Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Wed, 25 Aug 2010 15:08:05 -0700 Subject: Update to rra-c-util 2.6 and C TAP Harness 1.5 Update to C TAP Harness 1.5: * Better reporting of fatal errors in the test suite. * Summarize results at the end of test execution. * Add tests/HOWTO from docs/writing-tests in C TAP Harness. Update to rra-c-util 2.6: * Fix portability to bundled Heimdal on OpenBSD. * Improve checking for krb5_kt_free_entry with older MIT Kerberos. * Fix portability for missing krb5_get_init_creds_opt_free. * Fix header guard for util/xwrite.h. * Restore default compiler configuration after GSS-API library probe. --- NEWS | 14 ++ configure.ac | 4 +- m4/gssapi.m4 | 3 +- m4/krb5.m4 | 39 +++-- m4/remctl.m4 | 2 +- portable/krb5-extra.c | 3 +- portable/krb5.h | 31 +++- tests/portable/snprintf-t.c | 12 +- tests/runtests.c | 403 +++++++++++++++++++++++++++++--------------- tests/tap/basic.c | 196 +++++++++++++++++---- tests/tap/basic.h | 41 ++++- tests/tap/kerberos.c | 31 +--- tests/tap/kerberos.sh | 17 +- tests/tap/libtap.sh | 32 +++- tests/tap/remctl.sh | 18 +- tests/util/messages-t.c | 4 +- tests/util/xmalloc.c | 5 +- util/macros.h | 1 - util/messages.c | 50 +++--- util/messages.h | 26 +-- 20 files changed, 631 insertions(+), 301 deletions(-) (limited to 'configure.ac') diff --git a/NEWS b/NEWS index 6202878..f4e7abb 100644 --- a/NEWS +++ b/NEWS @@ -23,6 +23,20 @@ wallet 0.12 (unreleased) Add a help command to wallet-report, which returns a summary of all available commands. + Update to C TAP Harness 1.5: + + * Better reporting of fatal errors in the test suite. + * Summarize results at the end of test execution. + * Add tests/HOWTO from docs/writing-tests in C TAP Harness. + + Update to rra-c-util 2.6: + + * Fix portability to bundled Heimdal on OpenBSD. + * Improve checking for krb5_kt_free_entry with older MIT Kerberos. + * Fix portability for missing krb5_get_init_creds_opt_free. + * Fix header guard for util/xwrite.h. + * Restore default compiler configuration after GSS-API library probe. + wallet 0.11 (2010-03-08) When deleting an ACL on the server, verify that the ACL is not diff --git a/configure.ac b/configure.ac index 9f2d284..137e6ef 100644 --- a/configure.ac +++ b/configure.ac @@ -27,8 +27,10 @@ RRA_LIB_KRB5 RRA_LIB_KRB5_SWITCH AC_CHECK_FUNCS([krb5_get_init_creds_opt_alloc \ krb5_get_init_creds_opt_set_default_flags \ - krb5_kt_free_entry \ krb5_principal_get_realm]) +AC_CHECK_FUNCS([krb5_get_init_creds_opt_free], + [RRA_FUNC_KRB5_GET_INIT_CREDS_OPT_FREE_ARGS]) +AC_CHECK_DECLS([krb5_kt_free_entry]) AC_CHECK_MEMBERS([krb5_keytab_entry.keyblock], , , [#include ]) RRA_LIB_KRB5_RESTORE diff --git a/m4/gssapi.m4 b/m4/gssapi.m4 index 4b08569..0a657ff 100644 --- a/m4/gssapi.m4 +++ b/m4/gssapi.m4 @@ -57,7 +57,8 @@ AC_DEFUN([_RRA_LIB_GSSAPI_REDUCED], AC_CHECK_LIB([gssapi_krb5], [gss_import_name], [GSSAPI_LIBS="-lgssapi_krb5"], [AC_CHECK_LIB([gssapi], [gss_import_name], [GSSAPI_LIBS="-lgssapi"], [AC_CHECK_LIB([gss], [gss_import_name], [GSSAPI_LIBS="-lgss"], - [AC_MSG_ERROR([cannot find usable GSS-API library])])])])]) + [AC_MSG_ERROR([cannot find usable GSS-API library])])])]) + RRA_LIB_GSSAPI_RESTORE]) dnl Does the appropriate library checks for GSS-API linkage when we don't dnl have krb5-config or reduced dependencies. libgss is used as a last diff --git a/m4/krb5.m4 b/m4/krb5.m4 index bba9694..38a050e 100644 --- a/m4/krb5.m4 +++ b/m4/krb5.m4 @@ -2,7 +2,7 @@ dnl Find the compiler and linker flags for Kerberos v5. dnl dnl Finds the compiler and linker flags for linking with Kerberos v5 dnl libraries. Provides the --with-krb5, --with-krb5-include, and -dnl --with-krb5-lib configure options to specify non-standards paths to the +dnl --with-krb5-lib configure options to specify non-standard paths to the dnl Kerberos libraries. Uses krb5-config where available unless reduced dnl dependencies is requested. dnl @@ -13,6 +13,9 @@ dnl Kerberos libraries, saving the current values first, and dnl RRA_LIB_KRB5_RESTORE to restore those settings to before the last dnl RRA_LIB_KRB5_SWITCH. dnl +dnl If KRB5_CPPFLAGS, KRB5_LDFLAGS, or KRB5_LIBS are set before calling these +dnl macros, their values will be added to whatever the macros discover. +dnl dnl Provides the RRA_LIB_KRB5_OPTIONAL macro, which should be used if Kerberos dnl support is optional. This macro will still always set the substitution dnl variables, but they'll be empty unless --with-krb5 is given. Also, @@ -25,8 +28,12 @@ dnl change library ordering in that case. dnl dnl Depends on RRA_ENABLE_REDUCED_DEPENDS and RRA_SET_LDFLAGS. dnl +dnl Also provides RRA_FUNC_KRB5_GET_INIT_CREDS_OPT_FREE_ARGS, which checks +dnl whether krb5_get_init_creds_opt_free takes one argument or two. Defines +dnl HAVE_KRB5_GET_INIT_CREDS_OPT_FREE_2_ARGS if it takes two arguments. +dnl dnl Written by Russ Allbery -dnl Copyright 2005, 2006, 2007, 2008, 2009 +dnl Copyright 2005, 2006, 2007, 2008, 2009, 2010 dnl Board of Trustees, Leland Stanford Jr. University dnl dnl See LICENSE for licensing terms. @@ -99,10 +106,11 @@ AC_DEFUN([_RRA_LIB_KRB5_MANUAL], [AC_CHECK_LIB([nsl], [socket], [LIBS="-lnsl -lsocket $LIBS"], , [-lsocket])]) AC_SEARCH_LIBS([crypt], [crypt]) + AC_SEARCH_LIBS([rk_simple_execve], [roken]) rra_krb5_extra="$LIBS" LIBS="$rra_krb5_save_LIBS" AC_CHECK_LIB([krb5], [krb5_init_context], - [KRB5_LIBS="-lkrb5 -lasn1 -lroken -lcrypto -lcom_err $rra_krb5_extra"], + [KRB5_LIBS="-lkrb5 -lasn1 -lcom_err -lcrypto $rra_krb5_extra"], [AC_CHECK_LIB([krb5support], [krb5int_getspecific], [rra_krb5_extra="-lkrb5support $rra_krb5_extra"], [AC_CHECK_LIB([pthreads], [pthread_setspecific], @@ -125,7 +133,7 @@ AC_DEFUN([_RRA_LIB_KRB5_MANUAL], [AS_IF([test x"$1" = xtrue], [AC_MSG_ERROR([cannot find usable Kerberos v5 library])])], [$rra_krb5_extra])], - [-lasn1 -lroken -lcrypto -lcom_err $rra_krb5_extra]) + [-lasn1 -lcom_err -lcrypto $rra_krb5_extra]) LIBS="$KRB5_LIBS $LIBS" AC_CHECK_FUNCS([krb5_get_error_message], [AC_CHECK_FUNCS([krb5_free_error_message])], @@ -200,9 +208,6 @@ AC_DEFUN([RRA_LIB_KRB5], [rra_krb5_root= rra_krb5_libdir= rra_krb5_includedir= - KRB5_CPPFLAGS= - KRB5_LDFLAGS= - KRB5_LIBS= AC_SUBST([KRB5_CPPFLAGS]) AC_SUBST([KRB5_LDFLAGS]) AC_SUBST([KRB5_LIBS]) @@ -230,9 +235,6 @@ AC_DEFUN([RRA_LIB_KRB5_OPTIONAL], rra_krb5_libdir= rra_krb5_includedir= rra_use_kerberos= - KRB5_CPPFLAGS= - KRB5_LDFLAGS= - KRB5_LIBS= AC_SUBST([KRB5_CPPFLAGS]) AC_SUBST([KRB5_LDFLAGS]) AC_SUBST([KRB5_LIBS]) @@ -261,3 +263,20 @@ AC_DEFUN([RRA_LIB_KRB5_OPTIONAL], [_RRA_LIB_KRB5_INTERNAL([false])])]) AS_IF([test x"$KRB5_LIBS" != x], [AC_DEFINE([HAVE_KERBEROS], 1, [Define to enable Kerberos features.])])]) + +dnl Check whether krb5_get_init_creds_opt_free takes one argument or two. +dnl Early Heimdal used to take a single argument. Defines +dnl HAVE_KRB5_GET_INIT_CREDS_OPT_FREE_2_ARGS if it takes two arguments. +dnl +dnl Should be called with RRA_LIB_KRB5_SWITCH active. +AC_DEFUN([RRA_FUNC_KRB5_GET_INIT_CREDS_OPT_FREE_ARGS], +[AC_CACHE_CHECK([if krb5_get_init_creds_opt_free takes two arguments], + [rra_cv_func_krb5_get_init_creds_opt_free_args], + [AC_TRY_COMPILE([#include ], + [krb5_get_init_creds_opt *opts; krb5_context c; + krb5_get_init_creds_opt_free(c, opts);], + [rra_cv_func_krb5_get_init_creds_opt_free_args=yes], + [rra_cv_func_krb5_get_init_creds_opt_free_args=no])]) + AS_IF([test $rra_cv_func_krb5_get_init_creds_opt_free_args = yes], + [AC_DEFINE([HAVE_KRB5_GET_INIT_CREDS_OPT_FREE_2_ARGS], 1, + [Define if krb5_get_init_creds_opt_free takes two arguments.])])]) diff --git a/m4/remctl.m4 b/m4/remctl.m4 index 8ee3c16..bb3a56f 100644 --- a/m4/remctl.m4 +++ b/m4/remctl.m4 @@ -21,7 +21,7 @@ dnl dnl See LICENSE for licensing terms. dnl Save the current CPPFLAGS, LDFLAGS, and LIBS settings and switch to -dnl versions that include the Kerberos v5 flags. Used as a wrapper, with +dnl versions that include the remctl flags. Used as a wrapper, with dnl RRA_LIB_REMCTL_RESTORE, around tests. AC_DEFUN([RRA_LIB_REMCTL_SWITCH], [rra_remctl_save_CPPFLAGS="$CPPFLAGS" diff --git a/portable/krb5-extra.c b/portable/krb5-extra.c index dcddbe4..89ccbde 100644 --- a/portable/krb5-extra.c +++ b/portable/krb5-extra.c @@ -97,7 +97,8 @@ krb5_free_error_message(krb5_context ctx UNUSED, const char *msg) * assumes that an all-zero bit pattern will create a NULL pointer. */ krb5_error_code -krb5_get_init_creds_opt_alloc(krb5_context ctx, krb5_get_init_creds_opt **opts) +krb5_get_init_creds_opt_alloc(krb5_context ctx UNUSED, + krb5_get_init_creds_opt **opts) { *opts = calloc(1, sizeof(krb5_get_init_creds_opt)); if (*opts == NULL) diff --git a/portable/krb5.h b/portable/krb5.h index d9ef283..3b5700b 100644 --- a/portable/krb5.h +++ b/portable/krb5.h @@ -23,10 +23,17 @@ #ifndef PORTABLE_KRB5_H #define PORTABLE_KRB5_H 1 -#include +/* + * Allow inclusion of config.h to be skipped, since sometimes we have to use a + * stripped-down version of config.h with a different name. + */ +#ifndef CONFIG_H_INCLUDED +# include +#endif #include #include +#include BEGIN_DECLS @@ -50,27 +57,39 @@ void krb5_free_error_message(krb5_context, const char *); #endif /* - * Both current MIT and current Heimdal prefer _opt_alloc, but older versions - * of both require allocating your own struct and calling _opt_init. + * Both current MIT and current Heimdal prefer _opt_alloc and _opt_free, but + * older versions of both require allocating your own struct and calling + * _opt_init. */ #ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC krb5_error_code krb5_get_init_creds_opt_alloc(krb5_context, krb5_get_init_creds_opt **); #endif +#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_FREE +# ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_FREE_2_ARGS +# define krb5_get_init_creds_opt_free(c, o) krb5_get_init_creds_opt_free(o) +# endif +#else +# define krb5_get_init_creds_opt_free(c, o) free(o) +#endif /* Heimdal-specific. */ #ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_DEFAULT_FLAGS #define krb5_get_init_creds_opt_set_default_flags(c, p, r, o) /* empty */ #endif -/* Heimdal: krb5_kt_free_entry, MIT: krb5_free_keytab_entry_contents. */ -#ifndef HAVE_KRB5_KT_FREE_ENTRY +/* + * Heimdal: krb5_kt_free_entry, MIT: krb5_free_keytab_entry_contents. We + * check for the declaration rather than the function since the function is + * present in older MIT Kerberos libraries but not prototyped. + */ +#if !HAVE_DECL_KRB5_KT_FREE_ENTRY # define krb5_kt_free_entry(c, e) krb5_free_keytab_entry_contents((c), (e)) #endif /* * Heimdal provides a nice function that just returns a const char *. On MIT, - * there's an accessor macro that returns the krb5_data pointer, wihch + * there's an accessor macro that returns the krb5_data pointer, which * requires more work to get at the underlying char *. */ #ifndef HAVE_KRB5_PRINCIPAL_GET_REALM diff --git a/tests/portable/snprintf-t.c b/tests/portable/snprintf-t.c index ca6ae61..fd4c228 100644 --- a/tests/portable/snprintf-t.c +++ b/tests/portable/snprintf-t.c @@ -2,7 +2,7 @@ * snprintf test suite. * * Written by Russ Allbery - * Copyright 2009 Board of Trustees, Leland Stanford Jr. University + * Copyright 2009, 2010 Board of Trustees, Leland Stanford Jr. University * Copyright (c) 2004, 2005, 2006 * by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, @@ -16,6 +16,12 @@ #include +/* + * Disable the requirement that format strings be literals. We need variable + * formats for easy testing. + */ +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + /* * Intentionally don't add the printf attribute here since we pass a * zero-length printf format during testing and don't want warnings. @@ -86,7 +92,7 @@ static unsigned long long ullong_nums[] = { static void -test_format(bool truncate, const char *expected, int count, +test_format(bool trunc, const char *expected, int count, const char *format, ...) { char buf[128]; @@ -94,7 +100,7 @@ test_format(bool truncate, const char *expected, int count, va_list args; va_start(args, format); - result = test_vsnprintf(buf, truncate ? 32 : sizeof(buf), format, args); + result = test_vsnprintf(buf, trunc ? 32 : sizeof(buf), format, args); va_end(args); is_string(expected, buf, "format %s, wanted %s", format, expected); is_int(count, result, "...and output length correct"); diff --git a/tests/runtests.c b/tests/runtests.c index 1670012..ab77629 100644 --- a/tests/runtests.c +++ b/tests/runtests.c @@ -8,22 +8,41 @@ * Expects a list of executables located in the given file, one line per * executable. For each one, runs it as part of a test suite, reporting * results. Test output should start with a line containing the number of - * tests (numbered from 1 to this number), and then each line should be in the - * following format: + * tests (numbered from 1 to this number), optionally preceded by "1..", + * although that line may be given anywhere in the output. Each additional + * line should be in the following format: * * ok * not ok * ok # skip + * not ok # todo * - * where is the number of the test. ok indicates success, not ok - * indicates failure, and "# skip" indicates the test was skipped for some - * reason (maybe because it doesn't apply to this platform). This is a subset - * of TAP as documented in Test::Harness::TAP, which comes with Perl. + * where is the number of the test. An optional comment is permitted + * after the number if preceded by whitespace. ok indicates success, not ok + * indicates failure. "# skip" and "# todo" are a special cases of a comment, + * and must start with exactly that formatting. They indicate the test was + * skipped for some reason (maybe because it doesn't apply to this platform) + * or is testing something known to currently fail. The text following either + * "# skip" or "# todo" and whitespace is the reason. + * + * As a special case, the first line of the output may be in the form: + * + * 1..0 # skip some reason + * + * which indicates that this entire test case should be skipped and gives a + * reason. + * + * Any other lines are ignored, although for compliance with the TAP protocol + * all lines other than the ones in the above format should be sent to + * standard error rather than standard output and start with #. + * + * This is a subset of TAP as documented in Test::Harness::TAP or + * TAP::Parser::Grammar, which comes with Perl. * * Any bug reports, bug fixes, and improvements are very much welcome and * should be sent to the e-mail address below. * - * Copyright 2000, 2001, 2004, 2006, 2007, 2008, 2009 + * Copyright 2000, 2001, 2004, 2006, 2007, 2008, 2009, 2010 * Russ Allbery * * Permission is hereby granted, free of charge, to any person obtaining a @@ -88,6 +107,14 @@ enum test_status { TEST_INVALID }; +/* Indicates the state of our plan. */ +enum plan_status { + PLAN_INIT, /* Nothing seen yet. */ + PLAN_FIRST, /* Plan seen before any tests. */ + PLAN_PENDING, /* Test seen and no plan yet. */ + PLAN_FINAL /* Plan seen after some tests. */ +}; + /* Error exit statuses for test processes. */ #define CHILDERR_DUP 100 /* Couldn't redirect stderr or stdout. */ #define CHILDERR_EXEC 101 /* Couldn't exec child process. */ @@ -97,12 +124,14 @@ enum test_status { struct testset { char *file; /* The file name of the test. */ char *path; /* The path to the test program. */ - int count; /* Expected count of tests. */ - int current; /* The last seen test number. */ - int length; /* The length of the last status message. */ - int passed; /* Count of passing tests. */ - int failed; /* Count of failing lists. */ - int skipped; /* Count of skipped tests (passed). */ + enum plan_status plan; /* The status of our plan. */ + unsigned long count; /* Expected count of tests. */ + unsigned long current; /* The last seen test number. */ + unsigned int length; /* The length of the last status message. */ + unsigned long passed; /* Count of passing tests. */ + unsigned long failed; /* Count of failing lists. */ + unsigned long skipped; /* Count of skipped tests (passed). */ + unsigned long allocated; /* The size of the results table. */ enum test_status *results; /* Table of results by test number. */ int aborted; /* Whether the set as aborted. */ int reported; /* Whether the results were reported. */ @@ -131,8 +160,9 @@ Failed Set Fail/Total (%) Skip Stat Failing Tests\n\ -------------------------- -------------- ---- ---- ------------------------"; /* Include the file name and line number in malloc failures. */ -#define xmalloc(size) x_malloc((size), __FILE__, __LINE__) -#define xstrdup(p) x_strdup((p), __FILE__, __LINE__) +#define xmalloc(size) x_malloc((size), __FILE__, __LINE__) +#define xrealloc(p, size) x_realloc((p), (size), __FILE__, __LINE__) +#define xstrdup(p) x_strdup((p), __FILE__, __LINE__) /* @@ -164,13 +194,27 @@ x_malloc(size_t size, const char *file, int line) void *p; p = malloc(size); - if (!p) + if (p == NULL) sysdie("failed to malloc %lu bytes at %s line %d", (unsigned long) size, file, line); return p; } +/* + * Reallocate memory, reporting a fatal error and exiting on failure. + */ +static void * +x_realloc(void *p, size_t size, const char *file, int line) +{ + p = realloc(p, size); + if (p == NULL) + sysdie("failed to realloc %lu bytes at %s line %d", + (unsigned long) size, file, line); + return p; +} + + /* * Copy a string, reporting a fatal error and exiting on failure. */ @@ -182,7 +226,7 @@ x_strdup(const char *s, const char *file, int line) len = strlen(s) + 1; p = malloc(len); - if (!p) + if (p == NULL) sysdie("failed to strdup %lu bytes at %s line %d", (unsigned long) len, file, line); memcpy(p, s, len); @@ -234,62 +278,6 @@ skip_whitespace(const char *p) } -/* - * Read the first line of test output, which should contain the range of - * test numbers, and initialize the testset structure. Assume it was zeroed - * before being passed in. Return true if initialization succeeds, false - * otherwise. - */ -static int -test_init(const char *line, struct testset *ts) -{ - int i; - - /* - * Prefer a simple number of tests, but if the count is given as a range - * such as 1..10, accept that too for compatibility with Perl's - * Test::Harness. - */ - line = skip_whitespace(line); - if (strncmp(line, "1..", 3) == 0) - line += 3; - - /* - * Get the count, check it for validity, and initialize the struct. If we - * have something of the form "1..0 # skip foo", the whole file was - * skipped; record that. - */ - i = strtol(line, (char **) &line, 10); - if (i == 0) { - line = skip_whitespace(line); - if (*line == '#') { - line = skip_whitespace(line + 1); - if (strncasecmp(line, "skip", 4) == 0) { - line = skip_whitespace(line + 4); - if (*line != '\0') { - ts->reason = xstrdup(line); - ts->reason[strlen(ts->reason) - 1] = '\0'; - } - ts->all_skipped = 1; - ts->aborted = 1; - return 0; - } - } - } - if (i <= 0) { - puts("ABORTED (invalid test count)"); - ts->aborted = 1; - ts->reported = 1; - return 0; - } - ts->count = i; - ts->results = xmalloc(ts->count * sizeof(enum test_status)); - for (i = 0; i < ts->count; i++) - ts->results[i] = TEST_INVALID; - return 1; -} - - /* * Start a program, connecting its stdout to a pipe on our end and its stderr * to /dev/null, and storing the file descriptor to read from in the two @@ -340,7 +328,7 @@ test_start(const char *path, int *fd) static void test_backspace(struct testset *ts) { - int i; + unsigned int i; if (!isatty(STDOUT_FILENO)) return; @@ -354,6 +342,87 @@ test_backspace(struct testset *ts) } +/* + * Read the plan line of test output, which should contain the range of test + * numbers. We may initialize the testset structure here if we haven't yet + * seen a test. Return true if initialization succeeded and the test should + * continue, false otherwise. + */ +static int +test_plan(const char *line, struct testset *ts) +{ + unsigned long i; + long n; + + /* + * Accept a plan without the leading 1.. for compatibility with older + * versions of runtests. This will only be allowed if we've not yet seen + * a test result. + */ + line = skip_whitespace(line); + if (strncmp(line, "1..", 3) == 0) + line += 3; + + /* + * Get the count, check it for validity, and initialize the struct. If we + * have something of the form "1..0 # skip foo", the whole file was + * skipped; record that. If we do skip the whole file, zero out all of + * our statistics, since they're no longer relevant. + */ + n = strtol(line, (char **) &line, 10); + if (n == 0) { + line = skip_whitespace(line); + if (*line == '#') { + line = skip_whitespace(line + 1); + if (strncasecmp(line, "skip", 4) == 0) { + line = skip_whitespace(line + 4); + if (*line != '\0') { + ts->reason = xstrdup(line); + ts->reason[strlen(ts->reason) - 1] = '\0'; + } + ts->all_skipped = 1; + ts->aborted = 1; + ts->count = 0; + ts->passed = 0; + ts->skipped = 0; + ts->failed = 0; + return 0; + } + } + } + if (n <= 0) { + puts("ABORTED (invalid test count)"); + ts->aborted = 1; + ts->reported = 1; + return 0; + } + if (ts->plan == PLAN_INIT && ts->allocated == 0) { + ts->count = n; + ts->allocated = n; + ts->plan = PLAN_FIRST; + ts->results = xmalloc(ts->count * sizeof(enum test_status)); + for (i = 0; i < ts->count; i++) + ts->results[i] = TEST_INVALID; + } else if (ts->plan == PLAN_PENDING) { + if ((unsigned long) n < ts->count) { + printf("ABORTED (invalid test number %lu)\n", ts->count); + ts->aborted = 1; + ts->reported = 1; + return 0; + } + ts->count = n; + if ((unsigned long) n > ts->allocated) { + ts->results = xrealloc(ts->results, n * sizeof(enum test_status)); + for (i = ts->allocated; i < ts->count; i++) + ts->results[i] = TEST_INVALID; + ts->allocated = n; + } + ts->plan = PLAN_FINAL; + } + return 1; +} + + /* * Given a single line of output from a test, parse it and return the success * status of that test. Anything printed to stdout not matching the form @@ -366,20 +435,21 @@ test_checkline(const char *line, struct testset *ts) enum test_status status = TEST_PASS; const char *bail; char *end; - int current; + long number; + unsigned long i, current; /* Before anything, check for a test abort. */ bail = strstr(line, "Bail out!"); if (bail != NULL) { bail = skip_whitespace(bail + strlen("Bail out!")); if (*bail != '\0') { - int length; + size_t length; length = strlen(bail); if (bail[length - 1] == '\n') length--; test_backspace(ts); - printf("ABORTED (%.*s)\n", length, bail); + printf("ABORTED (%.*s)\n", (int) length, bail); ts->reported = 1; } ts->aborted = 1; @@ -393,6 +463,26 @@ test_checkline(const char *line, struct testset *ts) if (line[strlen(line) - 1] != '\n') return; + /* If the line begins with a hash mark, ignore it. */ + if (line[0] == '#') + return; + + /* If we haven't yet seen a plan, look for one. */ + if (ts->plan == PLAN_INIT && isdigit((unsigned char)(*line))) { + if (!test_plan(line, ts)) + return; + } else if (strncmp(line, "1..", 3) == 0) { + if (ts->plan == PLAN_PENDING) { + if (!test_plan(line, ts)) + return; + } else { + puts("ABORTED (multiple plans)"); + ts->aborted = 1; + ts->reported = 1; + return; + } + } + /* Parse the line, ignoring something we can't parse. */ if (strncmp(line, "not ", 4) == 0) { status = TEST_FAIL; @@ -402,17 +492,36 @@ test_checkline(const char *line, struct testset *ts) return; line = skip_whitespace(line + 2); errno = 0; - current = strtol(line, &end, 10); + number = strtol(line, &end, 10); if (errno != 0 || end == line) - current = ts->current + 1; - if (current <= 0 || current > ts->count) { + number = ts->current + 1; + current = number; + if (number <= 0 || (current > ts->count && ts->plan == PLAN_FIRST)) { test_backspace(ts); - printf("ABORTED (invalid test number %d)\n", current); + printf("ABORTED (invalid test number %lu)\n", current); ts->aborted = 1; ts->reported = 1; return; } + /* We have a valid test result. Tweak the results array if needed. */ + if (ts->plan == PLAN_INIT || ts->plan == PLAN_PENDING) { + ts->plan = PLAN_PENDING; + if (current > ts->count) + ts->count = current; + if (current > ts->allocated) { + unsigned long n; + + n = (ts->allocated == 0) ? 32 : ts->allocated * 2; + if (n < current) + n = current; + ts->results = xrealloc(ts->results, n * sizeof(enum test_status)); + for (i = ts->allocated; i < n; i++) + ts->results[i] = TEST_INVALID; + ts->allocated = n; + } + } + /* * Handle directives. We should probably do something more interesting * with unexpected passes of todo tests. @@ -431,7 +540,7 @@ test_checkline(const char *line, struct testset *ts) /* Make sure that the test number is in range and not a duplicate. */ if (ts->results[current - 1] != TEST_INVALID) { test_backspace(ts); - printf("ABORTED (duplicate test number %d)\n", current); + printf("ABORTED (duplicate test number %lu)\n", current); ts->aborted = 1; ts->reported = 1; return; @@ -442,13 +551,13 @@ test_checkline(const char *line, struct testset *ts) case TEST_PASS: ts->passed++; break; case TEST_FAIL: ts->failed++; break; case TEST_SKIP: ts->skipped++; break; - default: break; + case TEST_INVALID: break; } ts->current = current; ts->results[current - 1] = status; test_backspace(ts); if (isatty(STDOUT_FILENO)) { - ts->length = printf("%d/%d", current, ts->count); + ts->length = printf("%lu/%lu", current, ts->count); fflush(stdout); } } @@ -461,12 +570,13 @@ test_checkline(const char *line, struct testset *ts) * chars plus the space needed would go over the limit (use a limit of 0 to * disable this. */ -static int -test_print_range(int first, int last, int chars, int limit) +static unsigned int +test_print_range(unsigned long first, unsigned long last, unsigned int chars, + unsigned int limit) { - int needed = 0; - int out = 0; - int n; + unsigned int needed = 0; + unsigned int out = 0; + unsigned long n; if (chars > 0) { needed += 2; @@ -484,8 +594,8 @@ test_print_range(int first, int last, int chars, int limit) out += printf("..."); } else { if (last > first) - out += printf("%d-", first); - out += printf("%d", last); + out += printf("%lu-", first); + out += printf("%lu", last); } return out; } @@ -500,16 +610,16 @@ test_print_range(int first, int last, int chars, int limit) static void test_summarize(struct testset *ts, int status) { - int i; - int missing = 0; - int failed = 0; - int first = 0; - int last = 0; + unsigned long i; + unsigned long missing = 0; + unsigned long failed = 0; + unsigned long first = 0; + unsigned long last = 0; if (ts->aborted) { fputs("ABORTED", stdout); if (ts->count > 0) - printf(" (passed %d/%d)", ts->passed, ts->count - ts->skipped); + printf(" (passed %lu/%lu)", ts->passed, ts->count - ts->skipped); } else { for (i = 0; i < ts->count; i++) { if (ts->results[i] == TEST_INVALID) { @@ -553,9 +663,9 @@ test_summarize(struct testset *ts, int status) fputs(!status ? "ok" : "dubious", stdout); if (ts->skipped > 0) { if (ts->skipped == 1) - printf(" (skipped %d test)", ts->skipped); + printf(" (skipped %lu test)", ts->skipped); else - printf(" (skipped %d tests)", ts->skipped); + printf(" (skipped %lu tests)", ts->skipped); } } } @@ -570,8 +680,9 @@ test_summarize(struct testset *ts, int status) /* * Given a test set, analyze the results, classify the exit status, handle a - * few special error messages, and then pass it along to test_summarize() - * for the regular output. + * few special error messages, and then pass it along to test_summarize() for + * the regular output. Returns true if the test set ran successfully and all + * tests passed or were skipped, false otherwise. */ static int test_analyze(struct testset *ts) @@ -606,6 +717,10 @@ test_analyze(struct testset *ts) } else if (WIFSIGNALED(ts->status)) { test_summarize(ts, -WTERMSIG(ts->status)); return 0; + } else if (ts->plan != PLAN_FIRST && ts->plan != PLAN_FINAL) { + puts("ABORTED (no valid test plan)"); + ts->aborted = 1; + return 0; } else { test_summarize(ts, 0); return (ts->failed == 0); @@ -622,14 +737,12 @@ static int test_run(struct testset *ts) { pid_t testpid, child; - int outfd, i, status; + int outfd, status; + unsigned long i; FILE *output; char buffer[BUFSIZ]; - /* - * Initialize the test and our data structures, flagging this set in error - * if the initialization fails. - */ + /* Run the test program. */ testpid = test_start(ts->path, &outfd); output = fdopen(outfd, "r"); if (!output) { @@ -637,15 +750,11 @@ test_run(struct testset *ts) fflush(stdout); sysdie("fdopen failed"); } - if (!fgets(buffer, sizeof(buffer), output)) - ts->aborted = 1; - if (!ts->aborted && !test_init(buffer, ts)) - ts->aborted = 1; /* Pass each line of output to test_checkline(). */ while (!ts->aborted && fgets(buffer, sizeof(buffer), output)) test_checkline(buffer, ts); - if (ferror(output)) + if (ferror(output) || ts->plan == PLAN_INIT) ts->aborted = 1; test_backspace(ts); @@ -686,7 +795,8 @@ static void test_fail_summary(const struct testlist *fails) { struct testset *ts; - int i, chars, total, first, last; + unsigned int chars; + unsigned long i, first, last, total; puts(header); @@ -695,7 +805,7 @@ test_fail_summary(const struct testlist *fails) for (; fails; fails = fails->next) { ts = fails->ts; total = ts->count - ts->skipped; - printf("%-26.26s %4d/%-4d %3.0f%% %4d ", ts->file, ts->failed, + printf("%-26.26s %4lu/%-4lu %3.0f%% %4lu ", ts->file, ts->failed, total, total ? (ts->failed * 100.0) / total : 0, ts->skipped); if (WIFEXITED(ts->status)) @@ -711,19 +821,25 @@ test_fail_summary(const struct testlist *fails) last = 0; for (i = 0; i < ts->count; i++) { if (ts->results[i] == TEST_FAIL) { - if (first && i == last) + if (first != 0 && i == last) last = i + 1; else { - if (first) + if (first != 0) chars += test_print_range(first, last, chars, 20); first = i + 1; last = i + 1; } } } - if (first) + if (first != 0) test_print_range(first, last, chars, 20); putchar('\n'); + free(ts->file); + free(ts->path); + free(ts->results); + if (ts->reason != NULL) + free(ts->reason); + free(ts); } } @@ -746,7 +862,7 @@ find_test(const char *name, struct testset *ts, const char *source, { char *path; const char *bases[] = { ".", build, source, NULL }; - int i; + unsigned int i; for (i = 0; bases[i] != NULL; i++) { path = xmalloc(strlen(bases[i]) + strlen(name) + 4); @@ -778,20 +894,21 @@ static int test_batch(const char *testlist, const char *source, const char *build) { FILE *tests; - size_t length, i; - size_t longest = 0; + unsigned int length, i; + unsigned int longest = 0; char buffer[BUFSIZ]; - int line; + unsigned int line; struct testset ts, *tmp; struct timeval start, end; struct rusage stats; - struct testlist *failhead = 0; - struct testlist *failtail = 0; - int total = 0; - int passed = 0; - int skipped = 0; - int failed = 0; - int aborted = 0; + struct testlist *failhead = NULL; + struct testlist *failtail = NULL; + struct testlist *next; + unsigned long total = 0; + unsigned long passed = 0; + unsigned long skipped = 0; + unsigned long failed = 0; + unsigned long aborted = 0; /* * Open our file of tests to run and scan it, checking for lines that @@ -805,7 +922,7 @@ test_batch(const char *testlist, const char *source, const char *build) line++; length = strlen(buffer) - 1; if (buffer[length] != '\n') { - fprintf(stderr, "%s:%d: line too long\n", testlist, line); + fprintf(stderr, "%s:%u: line too long\n", testlist, line); exit(1); } if (length > longest) @@ -834,7 +951,7 @@ test_batch(const char *testlist, const char *source, const char *build) line++; length = strlen(buffer) - 1; if (buffer[length] != '\n') { - fprintf(stderr, "%s:%d: line too long\n", testlist, line); + fprintf(stderr, "%s:%u: line too long\n", testlist, line); exit(1); } buffer[length] = '\0'; @@ -844,12 +961,14 @@ test_batch(const char *testlist, const char *source, const char *build) if (isatty(STDOUT_FILENO)) fflush(stdout); memset(&ts, 0, sizeof(ts)); + ts.plan = PLAN_INIT; ts.file = xstrdup(buffer); find_test(buffer, &ts, source, build); ts.reason = NULL; if (test_run(&ts)) { free(ts.file); free(ts.path); + free(ts.results); if (ts.reason != NULL) free(ts.reason); } else { @@ -858,13 +977,13 @@ test_batch(const char *testlist, const char *source, const char *build) if (!failhead) { failhead = xmalloc(sizeof(struct testset)); failhead->ts = tmp; - failhead->next = 0; + failhead->next = NULL; failtail = failhead; } else { failtail->next = xmalloc(sizeof(struct testset)); failtail = failtail->next; failtail->ts = tmp; - failtail->next = 0; + failtail->next = NULL; } } aborted += ts.aborted; @@ -880,29 +999,35 @@ test_batch(const char *testlist, const char *source, const char *build) getrusage(RUSAGE_CHILDREN, &stats); /* Print out our final results. */ - if (failhead) + if (failhead != NULL) { test_fail_summary(failhead); + while (failhead != NULL) { + next = failhead->next; + free(failhead); + failhead = next; + } + } putchar('\n'); if (aborted != 0) { if (aborted == 1) - printf("Aborted %d test set", aborted); + printf("Aborted %lu test set", aborted); else - printf("Aborted %d test sets", aborted); - printf(", passed %d/%d tests", passed, total); + printf("Aborted %lu test sets", aborted); + printf(", passed %lu/%lu tests", passed, total); } else if (failed == 0) fputs("All tests successful", stdout); else - printf("Failed %d/%d tests, %.2f%% okay", failed, total, + printf("Failed %lu/%lu tests, %.2f%% okay", failed, total, (total - failed) * 100.0 / total); if (skipped != 0) { if (skipped == 1) - printf(", %d test skipped", skipped); + printf(", %lu test skipped", skipped); else - printf(", %d tests skipped", skipped); + printf(", %lu tests skipped", skipped); } puts("."); - printf("Files=%d, Tests=%d", line, total); + printf("Files=%u, Tests=%lu", line, total); printf(", %.2f seconds", tv_diff(&end, &start)); printf(" (%.2f usr + %.2f sys = %.2f CPU)\n", tv_seconds(&stats.ru_utime), tv_seconds(&stats.ru_stime), diff --git a/tests/tap/basic.c b/tests/tap/basic.c index 5ca9ff4..829f91a 100644 --- a/tests/tap/basic.c +++ b/tests/tap/basic.c @@ -2,12 +2,13 @@ * Some utility routines for writing tests. * * Herein are a variety of utility routines for writing tests. All routines - * of the form ok*() take a test number and some number of appropriate + * of the form ok() or is*() take a test number and some number of appropriate * arguments, check to be sure the results match the expected output using the * arguments, and print out something appropriate for that test number. Other - * utility routines help in constructing more complex tests. + * utility routines help in constructing more complex tests, skipping tests, + * or setting up the TAP output format. * - * Copyright 2009 Russ Allbery + * Copyright 2009, 2010 Russ Allbery * Copyright 2006, 2007, 2008 * Board of Trustees, Leland Stanford Jr. University * Copyright (c) 2004, 2005, 2006 @@ -34,7 +35,7 @@ * The test count. Always contains the number that will be used for the next * test status. */ -int testnum = 1; +unsigned long testnum = 1; /* * Status information stored so that we can give a test summary at the end of @@ -44,10 +45,14 @@ int testnum = 1; * We also store the PID of the process that called plan() and only summarize * results when that process exits, so as to not misreport results in forked * processes. + * + * If _lazy is true, we're doing lazy planning and will print out the plan + * based on the last test number at the end of testing. */ -static int _planned = 0; -static int _failed = 0; +static unsigned long _planned = 0; +static unsigned long _failed = 0; static pid_t _process = 0; +static int _lazy = 0; /* @@ -57,22 +62,28 @@ static pid_t _process = 0; static void finish(void) { - int highest = testnum - 1; - - if (_process != 0 && getpid() == _process && _planned > 0) { + unsigned long highest = testnum - 1; + + if (_planned == 0 && !_lazy) + return; + if (_process != 0 && getpid() == _process) { + if (_lazy) { + printf("1..%lu\n", highest); + _planned = highest; + } if (_planned > highest) - printf("# Looks like you planned %d test%s but only ran %d\n", + printf("# Looks like you planned %lu test%s but only ran %lu\n", _planned, (_planned > 1 ? "s" : ""), highest); else if (_planned < highest) - printf("# Looks like you planned %d test%s but ran %d extra\n", + printf("# Looks like you planned %lu test%s but ran %lu extra\n", _planned, (_planned > 1 ? "s" : ""), highest - _planned); else if (_failed > 0) - printf("# Looks like you failed %d test%s of %d\n", _failed, + printf("# Looks like you failed %lu test%s of %lu\n", _failed, (_failed > 1 ? "s" : ""), _planned); else if (_planned > 1) - printf("# All %d tests successful or skipped\n", _planned); + printf("# All %lu tests successful or skipped\n", _planned); else - printf("# %d test successful or skipped\n", _planned); + printf("# %lu test successful or skipped\n", _planned); } } @@ -82,12 +93,12 @@ finish(void) * the number of tests in the test suite. */ void -plan(int count) +plan(unsigned long count) { if (setvbuf(stdout, NULL, _IOLBF, BUFSIZ) != 0) fprintf(stderr, "# cannot set stdout to line buffered: %s\n", strerror(errno)); - printf("1..%d\n", count); + printf("1..%lu\n", count); testnum = 1; _planned = count; _process = getpid(); @@ -95,6 +106,23 @@ plan(int count) } +/* + * Initialize things for lazy planning, where we'll automatically print out a + * plan at the end of the program. Turns on line buffering on stdout as well. + */ +void +plan_lazy(void) +{ + if (setvbuf(stdout, NULL, _IOLBF, BUFSIZ) != 0) + fprintf(stderr, "# cannot set stdout to line buffered: %s\n", + strerror(errno)); + testnum = 1; + _process = getpid(); + _lazy = 1; + atexit(finish); +} + + /* * Skip the entire test suite and exits. Should be called instead of plan(), * not after it, since it prints out a special plan line. @@ -134,7 +162,7 @@ print_desc(const char *format, va_list args) void ok(int success, const char *format, ...) { - printf("%sok %d", success ? "" : "not ", testnum++); + printf("%sok %lu", success ? "" : "not ", testnum++); if (!success) _failed++; if (format != NULL) { @@ -148,13 +176,28 @@ ok(int success, const char *format, ...) } +/* + * Same as ok(), but takes the format arguments as a va_list. + */ +void +okv(int success, const char *format, va_list args) +{ + printf("%sok %lu", success ? "" : "not ", testnum++); + if (!success) + _failed++; + if (format != NULL) + print_desc(format, args); + putchar('\n'); +} + + /* * Skip a test. */ void skip(const char *reason, ...) { - printf("ok %d # skip", testnum++); + printf("ok %lu # skip", testnum++); if (reason != NULL) { va_list args; @@ -171,12 +214,12 @@ skip(const char *reason, ...) * Report the same status on the next count tests. */ void -ok_block(int count, int status, const char *format, ...) +ok_block(unsigned long count, int status, const char *format, ...) { - int i; + unsigned long i; for (i = 0; i < count; i++) { - printf("%sok %d", status ? "" : "not ", testnum++); + printf("%sok %lu", status ? "" : "not ", testnum++); if (!status) _failed++; if (format != NULL) { @@ -195,12 +238,12 @@ ok_block(int count, int status, const char *format, ...) * Skip the next count tests. */ void -skip_block(int count, const char *reason, ...) +skip_block(unsigned long count, const char *reason, ...) { - int i; + unsigned long i; for (i = 0; i < count; i++) { - printf("ok %d # skip", testnum++); + printf("ok %lu # skip", testnum++); if (reason != NULL) { va_list args; @@ -219,13 +262,13 @@ skip_block(int count, const char *reason, ...) * if those two numbers match. */ void -is_int(int wanted, int seen, const char *format, ...) +is_int(long wanted, long seen, const char *format, ...) { if (wanted == seen) - printf("ok %d", testnum++); + printf("ok %lu", testnum++); else { - printf("# wanted: %d\n# seen: %d\n", wanted, seen); - printf("not ok %d", testnum++); + printf("# wanted: %ld\n# seen: %ld\n", wanted, seen); + printf("not ok %lu", testnum++); _failed++; } if (format != NULL) { @@ -251,10 +294,10 @@ is_string(const char *wanted, const char *seen, const char *format, ...) if (seen == NULL) seen = "(null)"; if (strcmp(wanted, seen) == 0) - printf("ok %d", testnum++); + printf("ok %lu", testnum++); else { printf("# wanted: %s\n# seen: %s\n", wanted, seen); - printf("not ok %d", testnum++); + printf("not ok %lu", testnum++); _failed++; } if (format != NULL) { @@ -276,10 +319,10 @@ void is_double(double wanted, double seen, const char *format, ...) { if (wanted == seen) - printf("ok %d", testnum++); + printf("ok %lu", testnum++); else { printf("# wanted: %g\n# seen: %g\n", wanted, seen); - printf("not ok %d", testnum++); + printf("not ok %lu", testnum++); _failed++; } if (format != NULL) { @@ -301,11 +344,11 @@ void is_hex(unsigned long wanted, unsigned long seen, const char *format, ...) { if (wanted == seen) - printf("ok %d", testnum++); + printf("ok %lu", testnum++); else { printf("# wanted: %lx\n# seen: %lx\n", (unsigned long) wanted, (unsigned long) seen); - printf("not ok %d", testnum++); + printf("not ok %lu", testnum++); _failed++; } if (format != NULL) { @@ -354,3 +397,88 @@ sysbail(const char *format, ...) printf(": %s\n", strerror(oerrno)); exit(1); } + + +/* + * Report a diagnostic to stderr. + */ +void +diag(const char *format, ...) +{ + va_list args; + + fflush(stdout); + printf("# "); + va_start(args, format); + vprintf(format, args); + va_end(args); + printf("\n"); +} + + +/* + * Report a diagnostic to stderr, appending strerror(errno). + */ +void +sysdiag(const char *format, ...) +{ + va_list args; + int oerrno = errno; + + fflush(stdout); + printf("# "); + va_start(args, format); + vprintf(format, args); + va_end(args); + printf(": %s\n", strerror(oerrno)); +} + + +/* + * Locate a test file. Given the partial path to a file, look under BUILD and + * then SOURCE for the file and return the full path to the file. Returns + * NULL if the file doesn't exist. A non-NULL return should be freed with + * test_file_path_free(). + * + * This function uses sprintf because it attempts to be independent of all + * other portability layers. The use immediately after a memory allocation + * should be safe without using snprintf or strlcpy/strlcat. + */ +char * +test_file_path(const char *file) +{ + char *base; + char *path = NULL; + size_t length; + const char *envs[] = { "BUILD", "SOURCE", NULL }; + int i; + + for (i = 0; envs[i] != NULL; i++) { + base = getenv(envs[i]); + if (base == NULL) + continue; + length = strlen(base) + 1 + strlen(file) + 1; + path = malloc(length); + if (path == NULL) + sysbail("cannot allocate memory"); + sprintf(path, "%s/%s", base, file); + if (access(path, R_OK) == 0) + break; + free(path); + path = NULL; + } + return path; +} + + +/* + * Free a path returned from test_file_path(). This function exists primarily + * for Windows, where memory must be freed from the same library domain that + * it was allocated from. + */ +void +test_file_path_free(char *path) +{ + if (path != NULL) + free(path); +} diff --git a/tests/tap/basic.h b/tests/tap/basic.h index efe94ba..9602db4 100644 --- a/tests/tap/basic.h +++ b/tests/tap/basic.h @@ -1,6 +1,7 @@ /* * Basic utility routines for the TAP protocol. * + * Copyright 2009, 2010 Russ Allbery * Copyright 2006, 2007, 2008 * Board of Trustees, Leland Stanford Jr. University * Copyright (c) 2004, 2005, 2006 @@ -14,6 +15,7 @@ #ifndef TAP_BASIC_H #define TAP_BASIC_H 1 +#include /* va_list */ #include /* pid_t */ /* @@ -56,29 +58,40 @@ BEGIN_DECLS * The test count. Always contains the number that will be used for the next * test status. */ -extern int testnum; +extern unsigned long testnum; /* Print out the number of tests and set standard output to line buffered. */ -void plan(int count); +void plan(unsigned long count); + +/* + * Prepare for lazy planning, in which the plan will be printed automatically + * at the end of the test program. + */ +void plan_lazy(void); /* Skip the entire test suite. Call instead of plan. */ void skip_all(const char *format, ...) __attribute__((__noreturn__, __format__(printf, 1, 2))); -/* Basic reporting functions. */ +/* + * Basic reporting functions. The okv() function is the same as ok() but + * takes the test description as a va_list to make it easier to reuse the + * reporting infrastructure when writing new tests. + */ void ok(int success, const char *format, ...) __attribute__((__format__(printf, 2, 3))); +void okv(int success, const char *format, va_list args); void skip(const char *reason, ...) __attribute__((__format__(printf, 1, 2))); /* Report the same status on, or skip, the next count tests. */ -void ok_block(int count, int success, const char *format, ...) +void ok_block(unsigned long count, int success, const char *format, ...) __attribute__((__format__(printf, 3, 4))); -void skip_block(int count, const char *reason, ...) +void skip_block(unsigned long count, const char *reason, ...) __attribute__((__format__(printf, 2, 3))); /* Check an expected value against a seen value. */ -void is_int(int wanted, int seen, const char *format, ...) +void is_int(long wanted, long seen, const char *format, ...) __attribute__((__format__(printf, 3, 4))); void is_double(double wanted, double seen, const char *format, ...) __attribute__((__format__(printf, 3, 4))); @@ -93,6 +106,20 @@ void bail(const char *format, ...) void sysbail(const char *format, ...) __attribute__((__noreturn__, __nonnull__, __format__(printf, 1, 2))); +/* Report a diagnostic to stderr prefixed with #. */ +void diag(const char *format, ...) + __attribute__((__nonnull__, __format__(printf, 1, 2))); +void sysdiag(const char *format, ...) + __attribute__((__nonnull__, __format__(printf, 1, 2))); + +/* + * Find a test file under BUILD or SOURCE, returning the full path. The + * returned path should be freed with test_file_path_free(). + */ +char *test_file_path(const char *file) + __attribute__((__malloc__, __nonnull__)); +void test_file_path_free(char *path); + END_DECLS -#endif /* LIBTEST_H */ +#endif /* TAP_BASIC_H */ diff --git a/tests/tap/kerberos.c b/tests/tap/kerberos.c index 700212e..a17d980 100644 --- a/tests/tap/kerberos.c +++ b/tests/tap/kerberos.c @@ -22,33 +22,6 @@ #include -/* - * Given the partial path to a file, look under BUILD and then SOURCE for the - * file and return the full path to the file in newly-allocated memory. - * Returns NULL if the file doesn't exist. - */ -static char * -find_file(const char *file) -{ - char *base; - char *path = NULL; - const char *envs[] = { "BUILD", "SOURCE", NULL }; - int i; - - for (i = 0; envs[i] != NULL; i++) { - base = getenv(envs[i]); - if (base == NULL) - continue; - path = concatpath(base, file); - if (access(path, R_OK) == 0) - break; - free(path); - path = NULL; - } - return path; -} - - /* * Obtain Kerberos tickets for the principal specified in test.principal using * the keytab specified in test.keytab, both of which are presumed to be in @@ -78,7 +51,7 @@ kerberos_setup(void) krb5_creds creds; /* Read the principal name and find the keytab file. */ - path = find_file("data/test.principal"); + path = test_file_path("data/test.principal"); if (path == NULL) return NULL; file = fopen(path, "r"); @@ -95,7 +68,7 @@ kerberos_setup(void) bail("no newline in %s", path); free(path); principal[strlen(principal) - 1] = '\0'; - path = find_file("data/test.keytab"); + path = test_file_path("data/test.keytab"); if (path == NULL) return NULL; diff --git a/tests/tap/kerberos.sh b/tests/tap/kerberos.sh index fbeaaba..904cae5 100644 --- a/tests/tap/kerberos.sh +++ b/tests/tap/kerberos.sh @@ -1,4 +1,4 @@ -# Shell function library for Kerberos test support. +# Shell function library to initialize Kerberos credentials # # Written by Russ Allbery # Copyright 2009, 2010 Board of Trustees, Leland Stanford Jr. University @@ -10,18 +10,9 @@ # configured. Sets the global principal variable to the principal to use. kerberos_setup () { local keytab - keytab='' - for f in "$BUILD/data/test.keytab" "$SOURCE/data/test.keytab" ; do - if [ -r "$f" ] ; then - keytab="$f" - fi - done - principal='' - for f in "$BUILD/data/test.principal" "$SOURCE/data/test.principal" ; do - if [ -r "$f" ] ; then - principal=`cat "$BUILD/data/test.principal"` - fi - done + keytab=`test_file_path data/test.keytab` + principal=`test_file_path data/test.principal` + principal=`cat "$principal" 2>/dev/null` if [ -z "$keytab" ] || [ -z "$principal" ] ; then return 1 fi diff --git a/tests/tap/libtap.sh b/tests/tap/libtap.sh index 1846840..a9b46d4 100644 --- a/tests/tap/libtap.sh +++ b/tests/tap/libtap.sh @@ -1,7 +1,7 @@ # Shell function library for test cases. # # Written by Russ Allbery -# Copyright 2009 Russ Allbery +# Copyright 2009, 2010 Russ Allbery # Copyright 2006, 2007, 2008 Board of Trustees, Leland Stanford Jr. University # # See LICENSE for licensing terms. @@ -15,10 +15,22 @@ plan () { trap finish 0 } +# Prepare for lazy planning. +plan_lazy () { + count=1 + planned=0 + failed=0 + trap finish 0 +} + # Report the test status on exit. finish () { local highest looks highest=`expr "$count" - 1` + if [ "$planned" = 0 ] ; then + echo "1..$highest" + planned="$highest" + fi looks='# Looks like you' if [ "$planned" -gt 0 ] ; then if [ "$planned" -gt "$highest" ] ; then @@ -146,3 +158,21 @@ bail () { echo 'Bail out!' "$@" exit 1 } + +# Output a diagnostic on standard error, preceded by the required # mark. +diag () { + echo '#' "$@" +} + +# Search for the given file first in $BUILD and then in $SOURCE and echo the +# path where the file was found, or the empty string if the file wasn't +# found. +test_file_path () { + if [ -f "$BUILD/$1" ] ; then + echo "$BUILD/$1" + elif [ -f "$SOURCE/$1" ] ; then + echo "$SOURCE/$1" + else + echo '' + fi +} diff --git a/tests/tap/remctl.sh b/tests/tap/remctl.sh index b9667ef..9e01bcf 100644 --- a/tests/tap/remctl.sh +++ b/tests/tap/remctl.sh @@ -10,18 +10,12 @@ remctld_start () { local keytab principal rm -f "$BUILD/data/remctld.pid" - keytab='' - for f in "$BUILD/data/test.keytab" "$SOURCE/data/test.keytab" ; do - if [ -r "$f" ] ; then - keytab="$f" - fi - done - principal='' - for f in "$BUILD/data/test.principal" "$SOURCE/data/test.principal" ; do - if [ -r "$f" ] ; then - principal=`cat "$BUILD/data/test.principal"` - fi - done + keytab=`test_file_path data/test.keytab` + principal=`test_file_path data/test.principal` + principal=`cat "$principal" 2>/dev/null` + if [ -z "$keytab" ] || [ -z "$principal" ] ; then + return 1 + fi if [ -n "$VALGRIND" ] ; then ( "$VALGRIND" --log-file=valgrind.%p --leak-check=full "$1" -m \ -p 14373 -s "$principal" -P "$BUILD/data/remctld.pid" -f "$2" -d \ diff --git a/tests/util/messages-t.c b/tests/util/messages-t.c index fb82a42..a58f82c 100644 --- a/tests/util/messages-t.c +++ b/tests/util/messages-t.c @@ -146,8 +146,8 @@ test_strerror(int status, const char *output, int error, char *full_output, *name; full_output = concat(output, ": ", strerror(error), "\n", (char *) NULL); - xasprintf(&name, "strerror %d", testnum / 3 + 1); - is_function_output(function, status, full_output, name); + xasprintf(&name, "strerror %lu", testnum / 3 + 1); + is_function_output(function, status, full_output, "%s", name); free(full_output); free(name); } diff --git a/tests/util/xmalloc.c b/tests/util/xmalloc.c index 3bd5588..b6f4564 100644 --- a/tests/util/xmalloc.c +++ b/tests/util/xmalloc.c @@ -246,8 +246,6 @@ main(int argc, char *argv[]) size_t limit = 0; int willfail = 0; unsigned char code; - struct rlimit rl; - void *tmp; if (argc < 3) die("Usage error. Type, size, and limit must be given."); @@ -290,6 +288,9 @@ main(int argc, char *argv[]) */ if (limit > 0) { #if HAVE_SETRLIMIT && defined(RLIMIT_AS) + struct rlimit rl; + void *tmp; + rl.rlim_cur = limit; rl.rlim_max = limit; if (setrlimit(RLIMIT_AS, &rl) < 0) { diff --git a/util/macros.h b/util/macros.h index 97b2c2b..0104d9f 100644 --- a/util/macros.h +++ b/util/macros.h @@ -8,7 +8,6 @@ #ifndef UTIL_MACROS_H #define UTIL_MACROS_H 1 -#include #include /* Used for unused parameters to silence gcc warnings. */ diff --git a/util/messages.c b/util/messages.c index ef920b2..3592692 100644 --- a/util/messages.c +++ b/util/messages.c @@ -107,9 +107,9 @@ const char *message_program_name = NULL; * handler list, the count of handlers, and the argument list. */ static void -message_handlers(message_handler_func **list, int count, va_list args) +message_handlers(message_handler_func **list, unsigned int count, va_list args) { - int i; + unsigned int i; if (*list != stdout_handlers && *list != stderr_handlers) free(*list); @@ -127,7 +127,7 @@ message_handlers(message_handler_func **list, int count, va_list args) */ #define HANDLER_FUNCTION(type) \ void \ - message_handlers_ ## type(int count, ...) \ + message_handlers_ ## type(unsigned int count, ...) \ { \ va_list args; \ \ @@ -145,7 +145,7 @@ HANDLER_FUNCTION(die) * Print a message to stdout, supporting message_program_name. */ void -message_log_stdout(int len UNUSED, const char *fmt, va_list args, int err) +message_log_stdout(size_t len UNUSED, const char *fmt, va_list args, int err) { if (message_program_name != NULL) fprintf(stdout, "%s: ", message_program_name); @@ -162,7 +162,7 @@ message_log_stdout(int len UNUSED, const char *fmt, va_list args, int err) * stdout so that errors and regular output occur in the right order. */ void -message_log_stderr(int len UNUSED, const char *fmt, va_list args, int err) +message_log_stderr(size_t len UNUSED, const char *fmt, va_list args, int err) { fflush(stdout); if (message_program_name != NULL) @@ -183,7 +183,7 @@ message_log_stderr(int len UNUSED, const char *fmt, va_list args, int err) * log the errno information. */ static void -message_log_syslog(int pri, int len, const char *fmt, va_list args, int err) +message_log_syslog(int pri, size_t len, const char *fmt, va_list args, int err) { char *buffer; @@ -218,11 +218,11 @@ message_log_syslog(int pri, int len, const char *fmt, va_list args, int err) * Do the same sort of wrapper to generate all of the separate syslog logging * functions. */ -#define SYSLOG_FUNCTION(name, type) \ - void \ - message_log_syslog_ ## name(int l, const char *f, va_list a, int e) \ - { \ - message_log_syslog(LOG_ ## type, l, f, a, e); \ +#define SYSLOG_FUNCTION(name, type) \ + void \ + message_log_syslog_ ## name(size_t l, const char *f, va_list a, int e) \ + { \ + message_log_syslog(LOG_ ## type, l, f, a, e); \ } SYSLOG_FUNCTION(debug, DEBUG) SYSLOG_FUNCTION(info, INFO) @@ -243,7 +243,7 @@ debug(const char *format, ...) { va_list args; message_handler_func *log; - int length; + ssize_t length; if (debug_handlers == NULL) return; @@ -254,7 +254,7 @@ debug(const char *format, ...) return; for (log = debug_handlers; *log != NULL; log++) { va_start(args, format); - (**log)(length, format, args, 0); + (**log)((size_t) length, format, args, 0); va_end(args); } } @@ -264,7 +264,7 @@ notice(const char *format, ...) { va_list args; message_handler_func *log; - int length; + ssize_t length; va_start(args, format); length = vsnprintf(NULL, 0, format, args); @@ -273,7 +273,7 @@ notice(const char *format, ...) return; for (log = notice_handlers; *log != NULL; log++) { va_start(args, format); - (**log)(length, format, args, 0); + (**log)((size_t) length, format, args, 0); va_end(args); } } @@ -283,7 +283,7 @@ sysnotice(const char *format, ...) { va_list args; message_handler_func *log; - int length; + ssize_t length; int error = errno; va_start(args, format); @@ -293,7 +293,7 @@ sysnotice(const char *format, ...) return; for (log = notice_handlers; *log != NULL; log++) { va_start(args, format); - (**log)(length, format, args, error); + (**log)((size_t) length, format, args, error); va_end(args); } } @@ -303,7 +303,7 @@ warn(const char *format, ...) { va_list args; message_handler_func *log; - int length; + ssize_t length; va_start(args, format); length = vsnprintf(NULL, 0, format, args); @@ -312,7 +312,7 @@ warn(const char *format, ...) return; for (log = warn_handlers; *log != NULL; log++) { va_start(args, format); - (**log)(length, format, args, 0); + (**log)((size_t) length, format, args, 0); va_end(args); } } @@ -322,7 +322,7 @@ syswarn(const char *format, ...) { va_list args; message_handler_func *log; - int length; + ssize_t length; int error = errno; va_start(args, format); @@ -332,7 +332,7 @@ syswarn(const char *format, ...) return; for (log = warn_handlers; *log != NULL; log++) { va_start(args, format); - (**log)(length, format, args, error); + (**log)((size_t) length, format, args, error); va_end(args); } } @@ -342,7 +342,7 @@ die(const char *format, ...) { va_list args; message_handler_func *log; - int length; + ssize_t length; va_start(args, format); length = vsnprintf(NULL, 0, format, args); @@ -350,7 +350,7 @@ die(const char *format, ...) if (length >= 0) for (log = die_handlers; *log != NULL; log++) { va_start(args, format); - (**log)(length, format, args, 0); + (**log)((size_t) length, format, args, 0); va_end(args); } exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1); @@ -361,7 +361,7 @@ sysdie(const char *format, ...) { va_list args; message_handler_func *log; - int length; + ssize_t length; int error = errno; va_start(args, format); @@ -370,7 +370,7 @@ sysdie(const char *format, ...) if (length >= 0) for (log = die_handlers; *log != NULL; log++) { va_start(args, format); - (**log)(length, format, args, error); + (**log)((size_t) length, format, args, error); va_end(args); } exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1); diff --git a/util/messages.h b/util/messages.h index ff86f39..dbdb256 100644 --- a/util/messages.h +++ b/util/messages.h @@ -49,35 +49,35 @@ void sysdie(const char *, ...) * of those handlers. These functions are not thread-safe; they set global * variables. */ -void message_handlers_debug(int count, ...); -void message_handlers_notice(int count, ...); -void message_handlers_warn(int count, ...); -void message_handlers_die(int count, ...); +void message_handlers_debug(unsigned int count, ...); +void message_handlers_notice(unsigned int count, ...); +void message_handlers_warn(unsigned int count, ...); +void message_handlers_die(unsigned int count, ...); /* * Some useful handlers, intended to be passed to message_handlers_*. All * handlers take the length of the formatted message, the format, a variadic * argument list, and the errno setting if any. */ -void message_log_stdout(int, const char *, va_list, int) +void message_log_stdout(size_t, const char *, va_list, int) __attribute((__nonnull__)); -void message_log_stderr(int, const char *, va_list, int) +void message_log_stderr(size_t, const char *, va_list, int) __attribute((__nonnull__)); -void message_log_syslog_debug(int, const char *, va_list, int) +void message_log_syslog_debug(size_t, const char *, va_list, int) __attribute((__nonnull__)); -void message_log_syslog_info(int, const char *, va_list, int) +void message_log_syslog_info(size_t, const char *, va_list, int) __attribute((__nonnull__)); -void message_log_syslog_notice(int, const char *, va_list, int) +void message_log_syslog_notice(size_t, const char *, va_list, int) __attribute((__nonnull__)); -void message_log_syslog_warning(int, const char *, va_list, int) +void message_log_syslog_warning(size_t, const char *, va_list, int) __attribute((__nonnull__)); -void message_log_syslog_err(int, const char *, va_list, int) +void message_log_syslog_err(size_t, const char *, va_list, int) __attribute((__nonnull__)); -void message_log_syslog_crit(int, const char *, va_list, int) +void message_log_syslog_crit(size_t, const char *, va_list, int) __attribute((__nonnull__)); /* The type of a message handler. */ -typedef void (*message_handler_func)(int, const char *, va_list, int); +typedef void (*message_handler_func)(size_t, const char *, va_list, int); /* If non-NULL, called before exit and its return value passed to exit. */ extern int (*message_fatal_cleanup)(void); -- cgit v1.2.3 From 7854b3f48bcc63e5fb59f306c8348fa49b3fc9a9 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Wed, 25 Aug 2010 16:35:19 -0700 Subject: Release 0.12 --- NEWS | 2 +- README | 2 +- configure.ac | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'configure.ac') diff --git a/NEWS b/NEWS index f4e7abb..c11bff9 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,6 @@ User-Visible wallet Changes -wallet 0.12 (unreleased) +wallet 0.12 (2010-08-25) New client program wallet-rekey that, given a list of keytabs on the command line, requests new keytab objects for each principal in the diff --git a/README b/README index 03dbc2c..5eae7fd 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ - wallet release 0.11 + wallet release 0.12 (secure data management system) Written by Russ Allbery diff --git a/configure.ac b/configure.ac index 137e6ef..ffd7eeb 100644 --- a/configure.ac +++ b/configure.ac @@ -9,7 +9,7 @@ dnl See LICENSE for licensing terms. dnl We cannot use -Wall -Werror with AM_INIT_AUTOMAKE since we override dnl distuninstallcheck (not supported by Perl). AC_PREREQ([2.64]) -AC_INIT([wallet], [0.11], [rra@stanford.edu]) +AC_INIT([wallet], [0.12], [rra@stanford.edu]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_LIBOBJ_DIR([portable]) AC_CONFIG_MACRO_DIR([m4]) -- cgit v1.2.3 From 234e3805c524a7432caed8be328df6e2fbfe9afb Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Wed, 27 Feb 2013 14:25:37 -0800 Subject: Update to rra-c-util 4.8 and C TAP Harness 1.12 Update to rra-c-util 4.8: * Look for krb5-config in /usr/kerberos/bin after the user's PATH. * Kerberos library probing fixes without transitive shared libraries. * Fix Autoconf warnings when probing for AIX's bundled Kerberos. * Avoid using krb5-config if --with-{krb5,gssapi}-{include,lib} given. * Correctly remove -I/usr/include from Kerberos and GSS-API flags. * Build on systems where krb5/krb5.h exists but krb5.h does not. * Pass --deps to krb5-config unless --enable-reduced-depends was used. * Do not use krb5-config results unless gssapi is supported. * Fix probing for Heimdal's libroken to work with older versions. * Update warning flags for GCC 4.6.1. * Update utility library and test suite for newer GCC warnings. * Fix broken GCC attribute markers causing compilation problems. * Suppress warnings on compilers that support gcc's __attribute__. * Add notices to all files copied over from rra-c-util. * Fix warnings when reporting memory allocation failure in messages.c. * Fix message utility library compiler warnings on 64-bit systems. * Include strings.h for additional POSIX functions where found. * Use an atexit handler to clean up after Kerberos tests. * Kerberos test configuration now goes in tests/config. * The principal of the test keytab is determined automatically. * Simplify the test suite calls for Kerberos and remctl tests. * Check for a missing ssize_t. * Improve the xstrndup utility function. * Checked asprintf variants are now void functions and cannot fail. * Fix use of long long in portable/mkstemp.c. * Fix test suite portability to Solaris. * Substantial improvements to the POD syntax and spelling checks. Update to C TAP Harness 1.12: * Fix compliation of runtests with more aggressive warnings. * Add a more complete usage message and a -h command-line flag. * Flush stderr before printing output from tests. * Better handle running shell tests without BUILD and SOURCE set. * Fix runtests to honor -s even if BUILD and -b aren't given. * runtests now frees all allocated resources on exit. * Only use feature-test macros when requested or built with gcc -ansi. * Drop is_double from the C TAP library to avoid requiring -lm. * Avoid using local in the shell libtap.sh library. * Suppress warnings on compilers that support gcc's __attribute__. Change-Id: I394294d5486ac1ce265c7713bec71a148aaaf1ce Reviewed-on: https://gerrit.stanford.edu/841 Reviewed-by: Russ Allbery Tested-by: Russ Allbery --- .gitignore | 8 +- Makefile.am | 20 +- NEWS | 43 ++++ README | 2 +- configure.ac | 35 +-- m4/gssapi.m4 | 114 +++++--- m4/krb5-config.m4 | 101 ++++++++ m4/krb5.m4 | 206 +++++++++------ m4/lib-depends.m4 | 9 +- m4/lib-pathname.m4 | 10 +- m4/remctl.m4 | 94 +++++-- m4/snprintf.m4 | 9 +- m4/vamacros.m4 | 9 +- portable/asprintf.c | 12 +- portable/dummy.c | 12 +- portable/krb5-extra.c | 12 +- portable/krb5.h | 20 +- portable/macros.h | 35 ++- portable/mkstemp.c | 14 +- portable/setenv.c | 19 +- portable/snprintf.c | 3 + portable/stdbool.h | 20 +- portable/strlcat.c | 12 +- portable/strlcpy.c | 12 +- portable/system.h | 75 ++++-- portable/uio.h | 12 +- tests/client/full-t.in | 8 +- tests/client/prompt-t.in | 12 +- tests/config/README | 24 ++ tests/data/perl.conf | 6 + tests/docs/pod-spelling-t | 108 +++----- tests/docs/pod-t | 52 +++- tests/portable/asprintf-t.c | 13 +- tests/portable/mkstemp-t.c | 13 +- tests/portable/setenv-t.c | 16 +- tests/portable/snprintf-t.c | 18 +- tests/portable/strlcat-t.c | 16 +- tests/portable/strlcpy-t.c | 16 +- tests/runtests.c | 165 ++++++++---- tests/tap/basic.c | 239 +++++++++++++---- tests/tap/basic.h | 81 +++--- tests/tap/kerberos.c | 499 ++++++++++++++++++++++++++++++------ tests/tap/kerberos.h | 115 ++++++++- tests/tap/kerberos.sh | 64 +++-- tests/tap/libtap.sh | 192 +++++++++----- tests/tap/macros.h | 88 +++++++ tests/tap/messages.c | 49 ++-- tests/tap/messages.h | 30 ++- tests/tap/perl/Test/RRA.pm | 222 ++++++++++++++++ tests/tap/perl/Test/RRA/Automake.pm | 362 ++++++++++++++++++++++++++ tests/tap/perl/Test/RRA/Config.pm | 200 +++++++++++++++ tests/tap/process.c | 125 +++++++-- tests/tap/process.h | 52 +++- tests/tap/remctl.sh | 61 +++-- tests/tap/string.c | 65 +++++ tests/tap/string.h | 49 ++++ tests/util/messages-krb5-t.c | 41 ++- tests/util/messages-t.c | 126 +++++---- tests/util/xmalloc-t | 130 ++++++---- tests/util/xmalloc.c | 72 ++++-- util/macros.h | 21 +- util/messages-krb5.c | 50 +++- util/messages-krb5.h | 23 +- util/messages.c | 26 +- util/messages.h | 37 ++- util/xmalloc.c | 62 +++-- util/xmalloc.h | 27 +- 67 files changed, 3603 insertions(+), 890 deletions(-) create mode 100644 m4/krb5-config.m4 create mode 100644 tests/config/README create mode 100644 tests/data/perl.conf create mode 100644 tests/tap/macros.h create mode 100644 tests/tap/perl/Test/RRA.pm create mode 100644 tests/tap/perl/Test/RRA/Automake.pm create mode 100644 tests/tap/perl/Test/RRA/Config.pm create mode 100644 tests/tap/string.c create mode 100644 tests/tap/string.h (limited to 'configure.ac') diff --git a/.gitignore b/.gitignore index d5ae8a0..23ffe53 100644 --- a/.gitignore +++ b/.gitignore @@ -22,11 +22,9 @@ /tests/client/full-t /tests/client/prompt-t /tests/client/rekey-t -/tests/data/.placeholder -/tests/data/test.keytab -/tests/data/test.password -/tests/data/test.principal -/tests/data/test.krbtype +/tests/config/keytab +/tests/config/password +/tests/config/principal /tests/portable/asprintf-t /tests/portable/mkstemp-t /tests/portable/setenv-t diff --git a/Makefile.am b/Makefile.am index 0e1d99c..772a71e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -26,7 +26,6 @@ PERL_FILES = perl/Wallet/ACL.pm perl/Wallet/ACL/Base.pm \ perl/t/schema.t perl/t/server.t perl/t/verifier-netdb.t \ perl/t/verifier.t -AUTOMAKE_OPTIONS = foreign subdir-objects ACLOCAL_AMFLAGS = -I m4 EXTRA_DIST = .gitignore LICENSE autogen client/wallet.pod \ client/wallet-rekey.pod config/allow-extract config/keytab \ @@ -97,18 +96,22 @@ dist_pkgdata_DATA = perl/sql/Wallet-Schema-0.07-0.08-MySQL.sql \ # # -Wconversion http://bugs.debian.org/488884 (htons warnings) # -# Last checked against gcc 4.4 (2010-08-15). -WARNINGS = -g -O -Wall -Wextra -Wendif-labels -Wformat=2 -Winit-self \ - -Wswitch-enum -Wdeclaration-after-statement -Wshadow -Wpointer-arith \ - -Wbad-function-cast -Wwrite-strings -Wstrict-prototypes \ - -Wmissing-prototypes -Wnested-externs -Werror +# Last checked against gcc 4.6.1 (2011-05-04). -D_FORTIFY_SOURCE=2 enables +# warn_unused_result attribute markings on glibc functions on Linux, which +# catches a few more issues. +WARNINGS = -g -O -D_FORTIFY_SOURCE=2 -Wall -Wextra -Wendif-labels \ + -Wformat=2 -Winit-self -Wswitch-enum -Wdeclaration-after-statement \ + -Wshadow -Wpointer-arith -Wbad-function-cast -Wcast-align \ + -Wwrite-strings -Wjump-misses-init -Wlogical-op \ + -Wstrict-prototypes -Wmissing-prototypes -Wredundant-decls \ + -Wnested-externs -Werror warnings: $(MAKE) V=0 CFLAGS='$(WARNINGS)' $(MAKE) V=0 CFLAGS='$(WARNINGS)' $(check_PROGRAMS) # Remove some additional files. -DISTCLEANFILES = perl/Makefile tests/data/.placeholder +DISTCLEANFILES = perl/Makefile MAINTAINERCLEANFILES = Makefile.in aclocal.m4 build-aux/compile \ build-aux/depcomp build-aux/install-sh build-aux/missing \ client/wallet.1 config.h.in config.h.in~ configure \ @@ -163,7 +166,8 @@ check_LIBRARIES = tests/tap/libtap.a tests_tap_libtap_a_CPPFLAGS = -I$(abs_top_srcdir)/tests $(KRB5_CPPFLAGS) tests_tap_libtap_a_SOURCES = tests/tap/basic.c tests/tap/basic.h \ tests/tap/kerberos.c tests/tap/kerberos.h tests/tap/messages.c \ - tests/tap/messages.h tests/tap/process.c tests/tap/process.h + tests/tap/messages.h tests/tap/process.c tests/tap/process.h \ + tests/tap/string.c tests/tap/string.h # All of the test programs. tests_portable_asprintf_t_SOURCES = tests/portable/asprintf-t.c \ diff --git a/NEWS b/NEWS index b948d91..0d98220 100644 --- a/NEWS +++ b/NEWS @@ -27,6 +27,49 @@ wallet 1.0 (unreleased) Add docs/objects-and-schemes, which provides a brief summary of the current supported object types and ACL schemes. + Update to rra-c-util 4.8: + + * Look for krb5-config in /usr/kerberos/bin after the user's PATH. + * Kerberos library probing fixes without transitive shared libraries. + * Fix Autoconf warnings when probing for AIX's bundled Kerberos. + * Avoid using krb5-config if --with-{krb5,gssapi}-{include,lib} given. + * Correctly remove -I/usr/include from Kerberos and GSS-API flags. + * Build on systems where krb5/krb5.h exists but krb5.h does not. + * Pass --deps to krb5-config unless --enable-reduced-depends was used. + * Do not use krb5-config results unless gssapi is supported. + * Fix probing for Heimdal's libroken to work with older versions. + * Update warning flags for GCC 4.6.1. + * Update utility library and test suite for newer GCC warnings. + * Fix broken GCC attribute markers causing compilation problems. + * Suppress warnings on compilers that support gcc's __attribute__. + * Add notices to all files copied over from rra-c-util. + * Fix warnings when reporting memory allocation failure in messages.c. + * Fix message utility library compiler warnings on 64-bit systems. + * Include strings.h for additional POSIX functions where found. + * Use an atexit handler to clean up after Kerberos tests. + * Kerberos test configuration now goes in tests/config. + * The principal of the test keytab is determined automatically. + * Simplify the test suite calls for Kerberos and remctl tests. + * Check for a missing ssize_t. + * Improve the xstrndup utility function. + * Checked asprintf variants are now void functions and cannot fail. + * Fix use of long long in portable/mkstemp.c. + * Fix test suite portability to Solaris. + * Substantial improvements to the POD syntax and spelling checks. + + Update to C TAP Harness 1.12: + + * Fix compliation of runtests with more aggressive warnings. + * Add a more complete usage message and a -h command-line flag. + * Flush stderr before printing output from tests. + * Better handle running shell tests without BUILD and SOURCE set. + * Fix runtests to honor -s even if BUILD and -b aren't given. + * runtests now frees all allocated resources on exit. + * Only use feature-test macros when requested or built with gcc -ansi. + * Drop is_double from the C TAP library to avoid requiring -lm. + * Avoid using local in the shell libtap.sh library. + * Suppress warnings on compilers that support gcc's __attribute__. + wallet 0.12 (2010-08-25) New client program wallet-rekey that, given a list of keytabs on the diff --git a/README b/README index c440b8c..b714098 100644 --- a/README +++ b/README @@ -223,7 +223,7 @@ TESTING support in the server, however, you will need to do some preparatory work before running the test suite. Review the files: - tests/data/README + tests/config/README perl/t/data/README and follow the instructions in those files to enable the full test diff --git a/configure.ac b/configure.ac index ffd7eeb..a79e42d 100644 --- a/configure.ac +++ b/configure.ac @@ -13,7 +13,8 @@ AC_INIT([wallet], [0.12], [rra@stanford.edu]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_LIBOBJ_DIR([portable]) AC_CONFIG_MACRO_DIR([m4]) -AM_INIT_AUTOMAKE([1.11 check-news silent-rules]) +AM_INIT_AUTOMAKE([1.11 check-news dist-xz foreign silent-rules subdir-objects + -Wall -Wno-override -Werror]) AM_MAINTAINER_MODE AC_PROG_CC @@ -22,6 +23,18 @@ AM_PROG_CC_C_O AC_PROG_INSTALL AC_PROG_RANLIB +AC_ARG_WITH([wallet-server], + [AC_HELP_STRING([--with-wallet-server=HOST], [Default wallet server])], + [AS_IF([test x"$withval" != xno && test x"$withval" != xyes], + [AC_DEFINE_UNQUOTED([WALLET_SERVER], ["$withval"], + [Define to the default server host name.])])]) +AC_ARG_WITH([wallet-port], + [AC_HELP_STRING([--with-wallet-port=PORT], + [Default wallet server port])], + [AS_IF([test x"$withval" != xno && test x"$withval" != xyes], + [AC_DEFINE_UNQUOTED([WALLET_PORT], [$withval], + [Define to the default server port.])])]) + RRA_LIB_REMCTL RRA_LIB_KRB5 RRA_LIB_KRB5_SWITCH @@ -30,8 +43,9 @@ AC_CHECK_FUNCS([krb5_get_init_creds_opt_alloc \ krb5_principal_get_realm]) AC_CHECK_FUNCS([krb5_get_init_creds_opt_free], [RRA_FUNC_KRB5_GET_INIT_CREDS_OPT_FREE_ARGS]) +AC_CHECK_DECLS([krb5_kt_free_entry], [], [], [RRA_INCLUDES_KRB5]) AC_CHECK_DECLS([krb5_kt_free_entry]) -AC_CHECK_MEMBERS([krb5_keytab_entry.keyblock], , , [#include ]) +AC_CHECK_MEMBERS([krb5_keytab_entry.keyblock], [], [], [RRA_INCLUDES_KRB5]) RRA_LIB_KRB5_RESTORE AC_HEADER_STDBOOL @@ -40,31 +54,18 @@ AC_CHECK_DECLS([snprintf, vsnprintf]) RRA_C_C99_VAMACROS RRA_C_GNU_VAMACROS AC_TYPE_LONG_LONG_INT +AC_CHECK_TYPES([ssize_t], [], [], + [#include ]) RRA_FUNC_SNPRINTF AC_CHECK_FUNCS([setrlimit]) AC_REPLACE_FUNCS([asprintf mkstemp setenv strlcat strlcpy]) -AC_ARG_WITH([wallet-server], - [AC_HELP_STRING([--with-wallet-server=HOST], [Default wallet server])], - [AS_IF([test x"$withval" != xno && test x"$withval" != xyes], - [AC_DEFINE_UNQUOTED([WALLET_SERVER], ["$withval"], - [Define to the default server host name.])])]) -AC_ARG_WITH([wallet-port], - [AC_HELP_STRING([--with-wallet-port=PORT], - [Default wallet server port])], - [AS_IF([test x"$withval" != xno && test x"$withval" != xyes], - [AC_DEFINE_UNQUOTED([WALLET_PORT], [$withval], - [Define to the default server port.])])]) - AC_ARG_VAR([REMCTLD], [Path to the remctld binary]) AC_PATH_PROG([REMCTLD], [remctld], , [$PATH:/usr/sbin:/usr/local/sbin]) AS_IF([test x"$REMCTLD" != x], [AC_DEFINE_UNQUOTED([PATH_REMCTLD], ["$REMCTLD"], [Define to the full path to remctld to run remctl tests.])]) -dnl Create the tests/data directory for builds outside the source directory. -AC_CONFIG_COMMANDS([tests/data/.placeholder], [touch tests/data/.placeholder]) - AC_CONFIG_HEADER([config.h]) AC_CONFIG_FILES([Makefile perl/Makefile.PL]) AC_CONFIG_FILES([tests/client/basic-t], [chmod +x tests/client/basic-t]) diff --git a/m4/gssapi.m4 b/m4/gssapi.m4 index 0a657ff..c596609 100644 --- a/m4/gssapi.m4 +++ b/m4/gssapi.m4 @@ -3,7 +3,8 @@ dnl dnl Finds the compiler and linker flags for linking with GSS-API libraries. dnl Provides the --with-gssapi, --with-gssapi-include, and --with-gssapi-lib dnl configure option to specify a non-standard path to the GSS-API libraries. -dnl Uses krb5-config where available unless reduced dependencies is requested. +dnl Uses krb5-config where available unless reduced dependencies is requested +dnl or --with-gssapi-include or --with-gssapi-lib are given. dnl dnl Provides the macro RRA_LIB_GSSAPI and sets the substitution variables dnl GSSAPI_CPPFLAGS, GSSAPI_LDFLAGS, and GSSAPI_LIBS. Also provides @@ -11,13 +12,34 @@ dnl RRA_LIB_GSSAPI_SWITCH to set CPPFLAGS, LDFLAGS, and LIBS to include the dnl GSS-API libraries, saving the ecurrent values, and RRA_LIB_GSSAPI_RESTORE dnl to restore those settings to before the last RRA_LIB_GSSAPI_SWITCH. dnl -dnl Depends on RRA_ENABLE_REDUCED_DEPENDS and RRA_SET_LDFLAGS. +dnl Also provides RRA_INCLUDES_KRB5, which are the headers to include when +dnl probing the Kerberos library properties. +dnl +dnl Depends on RRA_KRB5_CONFIG, RRA_ENABLE_REDUCED_DEPENDS, and +dnl RRA_SET_LDFLAGS. +dnl +dnl The canonical version of this file is maintained in the rra-c-util +dnl package, available at . dnl dnl Written by Russ Allbery -dnl Copyright 2005, 2006, 2007, 2008, 2009 -dnl Board of Trustees, Leland Stanford Jr. University +dnl Copyright 2005, 2006, 2007, 2008, 2009, 2011, 2012 +dnl The Board of Trustees of the Leland Stanford Junior University dnl -dnl See LICENSE for licensing terms. +dnl This file is free software; the authors give unlimited permission to copy +dnl and/or distribute it, with or without modifications, as long as this +dnl notice is preserved. + +dnl Headers to include when probing for Kerberos library properties. +AC_DEFUN([RRA_INCLUDES_GSSAPI], [[ +#ifdef HAVE_GSSAPI_GSSAPI_H +# include +#else +# include +#endif +#ifdef HAVE_GSSAPI_GSSAPI_KRB5_H +# include +#endif +]]) dnl Save the current CPPFLAGS, LDFLAGS, and LIBS settings and switch to dnl versions that include the GSS-API flags. Used as a wrapper, with @@ -68,18 +90,18 @@ AC_DEFUN([_RRA_LIB_GSSAPI_MANUAL], [RRA_LIB_GSSAPI_SWITCH rra_gssapi_extra= LIBS= - AC_SEARCH_LIBS([res_search], [resolv], , + AC_SEARCH_LIBS([res_search], [resolv], [], [AC_SEARCH_LIBS([__res_search], [resolv])]) AC_SEARCH_LIBS([gethostbyname], [nsl]) - AC_SEARCH_LIBS([socket], [socket], , - [AC_CHECK_LIB([nsl], [socket], [LIBS="-lnsl -lsocket $LIBS"], , + AC_SEARCH_LIBS([socket], [socket], [], + [AC_CHECK_LIB([nsl], [socket], [LIBS="-lnsl -lsocket $LIBS"], [], [-lsocket])]) AC_SEARCH_LIBS([crypt], [crypt]) + AC_SEARCH_LIBS([roken_concat], [roken]) rra_gssapi_extra="$LIBS" LIBS="$rra_gssapi_save_LIBS" AC_CHECK_LIB([gssapi], [gss_import_name], - [GSSAPI_LIBS="-lgssapi -lkrb5 -lasn1 -lroken -lcrypto -lcom_err" - GSSAPI_LIBS="$GSSAPI_LIBS $rra_gssapi_extra"], + [GSSAPI_LIBS="-lgssapi -lkrb5 -lasn1 -lcrypto -lcom_err $rra_gssapi_extra"], [AC_CHECK_LIB([krb5support], [krb5int_getspecific], [rra_gssapi_extra="-lkrb5support $rra_gssapi_extra"], [AC_CHECK_LIB([pthreads], [pthread_setspecific], @@ -88,7 +110,7 @@ AC_DEFUN([_RRA_LIB_GSSAPI_MANUAL], [rra_gssapi_pthread="-lpthread"])]) AC_CHECK_LIB([krb5support], [krb5int_setspecific], [rra_gssapi_extra="-lkrb5support $rra_gssapi_extra" - rra_gssapi_extra="$rra_gssapi_extra $rra_gssapi_pthread"], , + rra_gssapi_extra="$rra_gssapi_extra $rra_gssapi_pthread"], [], [$rra_gssapi_pthread])]) AC_CHECK_LIB([com_err], [error_message], [rra_gssapi_extra="-lcom_err $rra_gssapi_extra"]) @@ -101,7 +123,7 @@ AC_DEFUN([_RRA_LIB_GSSAPI_MANUAL], [GSSAPI_LIBS="-lgss"], [AC_MSG_ERROR([cannot find usable GSS-API library])])], [$rra_gssapi_extra])], - [-lkrb5 -lasn1 -lroken -lcrypto -lcom_err $rra_gssapi_extra]) + [-lkrb5 -lasn1 -lcrypto -lcom_err $rra_gssapi_extra]) RRA_LIB_GSSAPI_RESTORE]) dnl Sanity-check the results of krb5-config and be sure we can really link a @@ -116,6 +138,44 @@ AC_DEFUN([_RRA_LIB_GSSAPI_CHECK], _RRA_LIB_GSSAPI_PATHS _RRA_LIB_GSSAPI_MANUAL])]) +dnl Determine GSS-API compiler and linker flags from krb5-config. +AC_DEFUN([_RRA_LIB_GSSAPI_CONFIG], +[RRA_KRB5_CONFIG([${rra_gssapi_root}], [gssapi], [GSSAPI], + [_RRA_LIB_GSSAPI_CHECK], + [_RRA_LIB_GSSAPI_PATHS + _RRA_LIB_GSSAPI_MANUAL])]) + +dnl Check for a header using a file existence check rather than using +dnl AC_CHECK_HEADERS. This is used if there were arguments to configure +dnl specifying the GSS-API library path, since we may have one header in the +dnl default include path and another under our explicitly-configured GSS-API +dnl location. +AC_DEFUN([_RRA_LIB_GSSAPI_CHECK_HEADER], +[AC_MSG_CHECKING([for $1]) + AS_IF([test -f "${rra_gssapi_incroot}/$1"], + [AC_DEFINE_UNQUOTED(AS_TR_CPP([HAVE_$1]), [1], + [Define to 1 if you have the <$1> header file.]) + AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no])])]) + +dnl Determine the GSS-API header location and probe for some other +dnl characteristics of the libraries. We use a file existence check rather +dnl than letting the compiler probe for the right header location +AC_DEFUN([_RRA_LIB_GSSAPI_EXTRA], +[rra_gssapi_incroot= + AS_IF([test x"$rra_gssapi_includedir" != x], + [rra_gssapi_incroot="$rra_gssapi_includedir"], + [AS_IF([test x"$rra_gssapi_root" != x], + [rra_gssapi_incroot="${rra_gssapi_root}/include"])]) + AS_IF([test x"$rra_gssapi_incroot" = x], + [AC_CHECK_HEADERS([gssapi/gssapi.h gssapi/gssapi_krb5.h])], + [_RRA_LIB_GSSAPI_CHECK_HEADER([gssapi/gssapi.h]) + _RRA_LIB_GSSAPI_CHECK_HEADER([gssapi/gssapi_krb5.h])]) + AC_CHECK_DECL([GSS_C_NT_USER_NAME], + [AC_DEFINE([HAVE_GSS_RFC_OIDS], 1, + [Define to 1 if the GSS-API library uses RFC-compliant OIDs.])], [], + [RRA_INCLUDES_GSSAPI])]) + dnl The main macro. AC_DEFUN([RRA_LIB_GSSAPI], [AC_REQUIRE([RRA_ENABLE_REDUCED_DEPENDS]) @@ -148,24 +208,12 @@ AC_DEFUN([RRA_LIB_GSSAPI], AS_IF([test x"$rra_reduced_depends" = xtrue], [_RRA_LIB_GSSAPI_PATHS _RRA_LIB_GSSAPI_REDUCED], - [AC_ARG_VAR([KRB5_CONFIG], [Path to krb5-config]) - AS_IF([test x"$rra_gssapi_root" != x && test -z "$KRB5_CONFIG"], - [AS_IF([test -x "${rra_gssapi_root}/bin/krb5-config"], - [KRB5_CONFIG="${rra_gssapi_root}/bin/krb5-config"])], - [AC_PATH_PROG([KRB5_CONFIG], [krb5-config])]) - AS_IF([test x"$KRB5_CONFIG" != x && test -x "$KRB5_CONFIG"], - [AC_CACHE_CHECK([for gssapi support in krb5-config], - [rra_cv_lib_gssapi_config], - [AS_IF(["$KRB5_CONFIG" 2>&1 | grep gssapi >/dev/null 2>&1], - [rra_cv_lib_gssapi_config=yes], - [rra_cv_lib_gssapi_config=no])]) - AS_IF([test "$rra_cv_lib_gssapi_config" = yes], - [GSSAPI_CPPFLAGS=`"$KRB5_CONFIG" --cflags gssapi 2>/dev/null` - GSSAPI_LIBS=`"$KRB5_CONFIG" --libs gssapi 2>/dev/null`], - [GSSAPI_CPPFLAGS=`"$KRB5_CONFIG" --cflags 2>/dev/null` - GSSAPI_LIBS=`"$KRB5_CONFIG" --libs 2>/dev/null`]) - GSSAPI_CPPFLAGS=`echo "$GSSAPI_CPPFLAGS" \ - | sed 's%-I/usr/include ?%%'` - _RRA_LIB_GSSAPI_CHECK], - [_RRA_LIB_GSSAPI_PATHS - _RRA_LIB_GSSAPI_MANUAL])])]) + [AS_IF([test x"$rra_gssapi_includedir" = x \ + && test x"$rra_gssapi_libdir" = x], + [_RRA_LIB_GSSAPI_CONFIG], + [_RRA_LIB_GSSAPI_PATHS + _RRA_LIB_GSSAPI_MANUAL])]) + + RRA_LIB_GSSAPI_SWITCH + _RRA_LIB_GSSAPI_EXTRA + RRA_LIB_GSSAPI_RESTORE]) diff --git a/m4/krb5-config.m4 b/m4/krb5-config.m4 new file mode 100644 index 0000000..d73085f --- /dev/null +++ b/m4/krb5-config.m4 @@ -0,0 +1,101 @@ +dnl Use krb5-config to get link paths for Kerberos libraries. +dnl +dnl Provides one macro, RRA_KRB5_CONFIG, which attempts to get compiler and +dnl linker flags for a library via krb5-config and sets the appropriate shell +dnl variables. Defines the Autoconf variable PATH_KRB5_CONFIG, which can be +dnl used to find the default path to krb5-config. +dnl +dnl Depends on RRA_ENABLE_REDUCED_DEPENDS. +dnl +dnl The canonical version of this file is maintained in the rra-c-util +dnl package, available at . +dnl +dnl Written by Russ Allbery +dnl Copyright 2011, 2012 +dnl The Board of Trustees of the Leland Stanford Junior University +dnl +dnl This file is free software; the authors give unlimited permission to copy +dnl and/or distribute it, with or without modifications, as long as this +dnl notice is preserved. + +dnl Check for krb5-config in the user's path and set PATH_KRB5_CONFIG. This +dnl is moved into a separate macro so that it can be loaded via AC_REQUIRE, +dnl meaning it will only be run once even if we link with multiple krb5-config +dnl libraries. +AC_DEFUN([_RRA_KRB5_CONFIG_PATH], +[AC_ARG_VAR([PATH_KRB5_CONFIG], [Path to krb5-config]) + AC_PATH_PROG([PATH_KRB5_CONFIG], [krb5-config], [], + [${PATH}:/usr/kerberos/bin])]) + +dnl Check whether the --deps flag is supported by krb5-config. Takes the path +dnl to krb5-config to use. Note that this path is not embedded in the cache +dnl variable, so this macro implicitly assumes that we will always use the +dnl same krb5-config program. +AC_DEFUN([_RRA_KRB5_CONFIG_DEPS], +[AC_REQUIRE([_RRA_KRB5_CONFIG_PATH]) + AC_CACHE_CHECK([for --deps support in krb5-config], + [rra_cv_krb5_config_deps], + [AS_IF(["$1" 2>&1 | grep deps >/dev/null 2>&1], + [rra_cv_krb5_config_deps=yes], + [rra_cv_krb5_config_deps=no])])]) + +dnl Obtain the library flags for a particular library using krb5-config. +dnl Takes the path to the krb5-config program to use, the argument to +dnl krb5-config to use, and the variable prefix under which to store the +dnl library flags. +AC_DEFUN([_RRA_KRB5_CONFIG_LIBS], +[AC_REQUIRE([_RRA_KRB5_CONFIG_PATH]) + AC_REQUIRE([RRA_ENABLE_REDUCED_DEPENDS]) + _RRA_KRB5_CONFIG_DEPS([$1]) + AS_IF([test x"$rra_reduced_depends" = xfalse \ + && test x"$rra_cv_krb5_config_deps" = xyes], + [$3[]_LIBS=`"$1" --deps --libs $2 2>/dev/null`], + [$3[]_LIBS=`"$1" --libs $2 2>/dev/null`])]) + +dnl Attempt to find the flags for a library using krb5-config. Takes the +dnl following arguments (in order): +dnl +dnl 1. The root directory for the library in question, generally from an +dnl Autoconf --with flag. Used by preference as the path to krb5-config. +dnl +dnl 2. The argument to krb5-config to retrieve flags for this particular +dnl library. +dnl +dnl 3. The variable prefix to use when setting CPPFLAGS and LIBS variables +dnl based on the result of krb5-config. +dnl +dnl 4. Further actions to take if krb5-config was found and supported that +dnl library type. +dnl +dnl 5. Further actions to take if krb5-config could not be used to get flags +dnl for that library type. +dnl +dnl Special-case a krb5-config argument of krb5 and run krb5-config without an +dnl argument if that option was requested and not supported. Old versions of +dnl krb5-config didn't take an argument to specify the library type, but +dnl always returned the flags for libkrb5. +AC_DEFUN([RRA_KRB5_CONFIG], +[AC_REQUIRE([_RRA_KRB5_CONFIG_PATH]) + rra_krb5_config_$3= + rra_krb5_config_$3[]_ok= + AS_IF([test x"$1" != x && test -x "$1/bin/krb5-config"], + [rra_krb5_config_$3="$1/bin/krb5-config"], + [rra_krb5_config_$3="$PATH_KRB5_CONFIG"]) + AS_IF([test x"$rra_krb5_config_$3" != x && test -x "$rra_krb5_config_$3"], + [AC_CACHE_CHECK([for $2 support in krb5-config], [rra_cv_lib_$3[]_config], + [AS_IF(["$rra_krb5_config_$3" 2>&1 | grep $2 >/dev/null 2>&1], + [rra_cv_lib_$3[]_config=yes], + [rra_cv_lib_$3[]_config=no])]) + AS_IF([test "$rra_cv_lib_$3[]_config" = yes], + [$3[]_CPPFLAGS=`"$rra_krb5_config_$3" --cflags $2 2>/dev/null` + _RRA_KRB5_CONFIG_LIBS([$rra_krb5_config_$3], [$2], [$3]) + rra_krb5_config_$3[]_ok=yes], + [AS_IF([test x"$2" = xkrb5], + [$3[]_CPPFLAGS=`"$rra_krb5_config_$3" --cflags 2>/dev/null` + $3[]_LIBS=`"$rra_krb5_config_$3" --libs $2 2>/dev/null` + rra_krb5_config_$3[]_ok=yes])])]) + AS_IF([test x"$rra_krb5_config_$3[]_ok" = xyes], + [$3[]_CPPFLAGS=`echo "$$3[]_CPPFLAGS" | sed 's%-I/usr/include %%'` + $3[]_CPPFLAGS=`echo "$$3[]_CPPFLAGS" | sed 's%-I/usr/include$%%'` + $4], + [$5])]) diff --git a/m4/krb5.m4 b/m4/krb5.m4 index 38a050e..964023a 100644 --- a/m4/krb5.m4 +++ b/m4/krb5.m4 @@ -1,17 +1,18 @@ -dnl Find the compiler and linker flags for Kerberos v5. +dnl Find the compiler and linker flags for Kerberos. dnl -dnl Finds the compiler and linker flags for linking with Kerberos v5 -dnl libraries. Provides the --with-krb5, --with-krb5-include, and -dnl --with-krb5-lib configure options to specify non-standard paths to the -dnl Kerberos libraries. Uses krb5-config where available unless reduced -dnl dependencies is requested. +dnl Finds the compiler and linker flags for linking with Kerberos libraries. +dnl Provides the --with-krb5, --with-krb5-include, and --with-krb5-lib +dnl configure options to specify non-standard paths to the Kerberos libraries. +dnl Uses krb5-config where available unless reduced dependencies is requested +dnl or --with-krb5-include or --with-krb5-lib are given. dnl dnl Provides the macro RRA_LIB_KRB5 and sets the substitution variables dnl KRB5_CPPFLAGS, KRB5_LDFLAGS, and KRB5_LIBS. Also provides dnl RRA_LIB_KRB5_SWITCH to set CPPFLAGS, LDFLAGS, and LIBS to include the dnl Kerberos libraries, saving the current values first, and dnl RRA_LIB_KRB5_RESTORE to restore those settings to before the last -dnl RRA_LIB_KRB5_SWITCH. +dnl RRA_LIB_KRB5_SWITCH. HAVE_KERBEROS will always be defined if RRA_LIB_KRB5 +dnl is used. dnl dnl If KRB5_CPPFLAGS, KRB5_LDFLAGS, or KRB5_LIBS are set before calling these dnl macros, their values will be added to whatever the macros discover. @@ -32,14 +33,31 @@ dnl Also provides RRA_FUNC_KRB5_GET_INIT_CREDS_OPT_FREE_ARGS, which checks dnl whether krb5_get_init_creds_opt_free takes one argument or two. Defines dnl HAVE_KRB5_GET_INIT_CREDS_OPT_FREE_2_ARGS if it takes two arguments. dnl +dnl Also provides RRA_INCLUDES_KRB5, which are the headers to include when +dnl probing the Kerberos library properties. +dnl +dnl The canonical version of this file is maintained in the rra-c-util +dnl package, available at . +dnl dnl Written by Russ Allbery -dnl Copyright 2005, 2006, 2007, 2008, 2009, 2010 -dnl Board of Trustees, Leland Stanford Jr. University +dnl Copyright 2005, 2006, 2007, 2008, 2009, 2010, 2011 +dnl The Board of Trustees of the Leland Stanford Junior University dnl -dnl See LICENSE for licensing terms. +dnl This file is free software; the authors give unlimited permission to copy +dnl and/or distribute it, with or without modifications, as long as this +dnl notice is preserved. + +dnl Headers to include when probing for Kerberos library properties. +AC_DEFUN([RRA_INCLUDES_KRB5], [[ +#if HAVE_KRB5_H +# include +#else +# include +#endif +]]) dnl Save the current CPPFLAGS, LDFLAGS, and LIBS settings and switch to -dnl versions that include the Kerberos v5 flags. Used as a wrapper, with +dnl versions that include the Kerberos flags. Used as a wrapper, with dnl RRA_LIB_KRB5_RESTORE, around tests. AC_DEFUN([RRA_LIB_KRB5_SWITCH], [rra_krb5_save_CPPFLAGS="$CPPFLAGS" @@ -69,44 +87,62 @@ AC_DEFUN([_RRA_LIB_KRB5_PATHS], [AS_IF([test x"$rra_krb5_root" != x/usr], [KRB5_CPPFLAGS="-I${rra_krb5_root}/include"])])])]) -dnl Does the appropriate library checks for reduced-dependency Kerberos v5 +dnl Check for a header using a file existence check rather than using +dnl AC_CHECK_HEADERS. This is used if there were arguments to configure +dnl specifying the Kerberos header path, since we may have one header in the +dnl default include path and another under our explicitly-configured Kerberos +dnl location. +AC_DEFUN([_RRA_LIB_KRB5_CHECK_HEADER], +[AC_MSG_CHECKING([for $1]) + AS_IF([test -f "${rra_krb5_incroot}/$1"], + [AC_DEFINE_UNQUOTED(AS_TR_CPP([HAVE_$1]), [1], + [Define to 1 if you have the <$1> header file.]) + AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no])])]) + +dnl Does the appropriate library checks for reduced-dependency Kerberos dnl linkage. The single argument, if true, says to fail if Kerberos could not dnl be found. AC_DEFUN([_RRA_LIB_KRB5_REDUCED], [RRA_LIB_KRB5_SWITCH AC_CHECK_LIB([krb5], [krb5_init_context], [KRB5_LIBS="-lkrb5"], [AS_IF([test x"$1" = xtrue], - [AC_MSG_ERROR([cannot find usable Kerberos v5 library])])]) + [AC_MSG_ERROR([cannot find usable Kerberos library])])]) LIBS="$KRB5_LIBS $LIBS" + AS_IF([test x"$rra_krb5_incroot" = x], + [AC_CHECK_HEADERS([krb5.h krb5/krb5.h])], + [_RRA_LIB_KRB5_CHECK_HEADER([krb5.h]) + _RRA_LIB_KRB5_CHECK_HEADER([krb5/krb5.h])]) AC_CHECK_FUNCS([krb5_get_error_message], [AC_CHECK_FUNCS([krb5_free_error_message])], - [AC_CHECK_FUNCS([krb5_get_error_string], , - [AC_CHECK_FUNCS([krb5_get_err_txt], , + [AC_CHECK_FUNCS([krb5_get_error_string], [], + [AC_CHECK_FUNCS([krb5_get_err_txt], [], [AC_CHECK_LIB([ksvc], [krb5_svc_get_msg], [KRB5_LIBS="$KRB5_LIBS -lksvc" AC_DEFINE([HAVE_KRB5_SVC_GET_MSG], [1]) - AC_CHECK_HEADERS([ibm_svc/krb5_svc.h])], + AC_CHECK_HEADERS([ibm_svc/krb5_svc.h], [], [], + [RRA_INCLUDES_KRB5])], [AC_CHECK_LIB([com_err], [com_err], [KRB5_LIBS="$KRB5_LIBS -lcom_err"], [AC_MSG_ERROR([cannot find usable com_err library])]) AC_CHECK_HEADERS([et/com_err.h])])])])]) RRA_LIB_KRB5_RESTORE]) -dnl Does the appropriate library checks for Kerberos v5 linkage when we don't +dnl Does the appropriate library checks for Kerberos linkage when we don't dnl have krb5-config or reduced dependencies. The single argument, if true, dnl says to fail if Kerberos could not be found. AC_DEFUN([_RRA_LIB_KRB5_MANUAL], [RRA_LIB_KRB5_SWITCH rra_krb5_extra= LIBS= - AC_SEARCH_LIBS([res_search], [resolv], , + AC_SEARCH_LIBS([res_search], [resolv], [], [AC_SEARCH_LIBS([__res_search], [resolv])]) AC_SEARCH_LIBS([gethostbyname], [nsl]) - AC_SEARCH_LIBS([socket], [socket], , - [AC_CHECK_LIB([nsl], [socket], [LIBS="-lnsl -lsocket $LIBS"], , + AC_SEARCH_LIBS([socket], [socket], [], + [AC_CHECK_LIB([nsl], [socket], [LIBS="-lnsl -lsocket $LIBS"], [], [-lsocket])]) AC_SEARCH_LIBS([crypt], [crypt]) - AC_SEARCH_LIBS([rk_simple_execve], [roken]) + AC_SEARCH_LIBS([roken_concat], [roken]) rra_krb5_extra="$LIBS" LIBS="$rra_krb5_save_LIBS" AC_CHECK_LIB([krb5], [krb5_init_context], @@ -119,28 +155,34 @@ AC_DEFUN([_RRA_LIB_KRB5_MANUAL], [rra_krb5_pthread="-lpthread"])]) AC_CHECK_LIB([krb5support], [krb5int_setspecific], [rra_krb5_extra="-lkrb5support $rra_krb5_extra $rra_krb5_pthread"], - , [$rra_krb5_pthread])]) + [], [$rra_krb5_pthread $rra_krb5_extra])], + [$rra_krb5_extra]) AC_CHECK_LIB([com_err], [error_message], - [rra_krb5_extra="-lcom_err $rra_krb5_extra"]) + [rra_krb5_extra="-lcom_err $rra_krb5_extra"], [], [$rra_krb5_extra]) AC_CHECK_LIB([ksvc], [krb5_svc_get_msg], - [rra_krb5_extra="-lksvc $rra_krb5_extra"]) + [rra_krb5_extra="-lksvc $rra_krb5_extra"], [], [$rra_krb5_extra]) AC_CHECK_LIB([k5crypto], [krb5int_hash_md5], - [rra_krb5_extra="-lk5crypto $rra_krb5_extra"]) + [rra_krb5_extra="-lk5crypto $rra_krb5_extra"], [], [$rra_krb5_extra]) AC_CHECK_LIB([k5profile], [profile_get_values], - [rra_krb5_extra="-lk5profile $rra_krb5_extra"]) + [rra_krb5_extra="-lk5profile $rra_krb5_extra"], [], [$rra_krb5_extra]) AC_CHECK_LIB([krb5], [krb5_cc_default], [KRB5_LIBS="-lkrb5 $rra_krb5_extra"], [AS_IF([test x"$1" = xtrue], - [AC_MSG_ERROR([cannot find usable Kerberos v5 library])])], + [AC_MSG_ERROR([cannot find usable Kerberos library])])], [$rra_krb5_extra])], [-lasn1 -lcom_err -lcrypto $rra_krb5_extra]) LIBS="$KRB5_LIBS $LIBS" + AS_IF([test x"$rra_krb5_incroot" = x], + [AC_CHECK_HEADERS([krb5.h krb5/krb5.h])], + [_RRA_LIB_KRB5_CHECK_HEADER([krb5.h]) + _RRA_LIB_KRB5_CHECK_HEADER([krb5/krb5.h])]) AC_CHECK_FUNCS([krb5_get_error_message], [AC_CHECK_FUNCS([krb5_free_error_message])], - [AC_CHECK_FUNCS([krb5_get_error_string], , - [AC_CHECK_FUNCS([krb5_get_err_txt], , + [AC_CHECK_FUNCS([krb5_get_error_string], [], + [AC_CHECK_FUNCS([krb5_get_err_txt], [], [AC_CHECK_FUNCS([krb5_svc_get_msg], - [AC_CHECK_HEADERS([ibm_svc/krb5_svc.h])], + [AC_CHECK_HEADERS([ibm_svc/krb5_svc.h], [], [], + [RRA_INCLUDES_KRB5])], [AC_CHECK_HEADERS([et/com_err.h])])])])]) RRA_LIB_KRB5_RESTORE]) @@ -158,49 +200,49 @@ AC_DEFUN([_RRA_LIB_KRB5_CHECK], _RRA_LIB_KRB5_PATHS _RRA_LIB_KRB5_MANUAL([$1])])]) +dnl Determine Kerberos compiler and linker flags from krb5-config. Does the +dnl additional probing we need to do to uncover error handling features, and +dnl falls back on the manual checks. +AC_DEFUN([_RRA_LIB_KRB5_CONFIG], +[RRA_KRB5_CONFIG([${rra_krb5_root}], [krb5], [KRB5], + [_RRA_LIB_KRB5_CHECK([$1]) + RRA_LIB_KRB5_SWITCH + AS_IF([test x"$rra_krb5_incroot" = x], + [AC_CHECK_HEADERS([krb5.h krb5/krb5.h])], + [_RRA_LIB_KRB5_CHECK_HEADER([krb5.h]) + _RRA_LIB_KRB5_CHECK_HEADER([krb5/krb5.h])]) + AC_CHECK_FUNCS([krb5_get_error_message], + [AC_CHECK_FUNCS([krb5_free_error_message])], + [AC_CHECK_FUNCS([krb5_get_error_string], [], + [AC_CHECK_FUNCS([krb5_get_err_txt], [], + [AC_CHECK_FUNCS([krb5_svc_get_msg], + [AC_CHECK_HEADERS([ibm_svc/krb5_svc.h], [], [], + [RRA_INCLUDES_KRB5])], + [AC_CHECK_HEADERS([et/com_err.h])])])])]) + RRA_LIB_KRB5_RESTORE], + [_RRA_LIB_KRB5_PATHS + _RRA_LIB_KRB5_MANUAL([$1])])]) + dnl The core of the library checking, shared between RRA_LIB_KRB5 and dnl RRA_LIB_KRB5_OPTIONAL. The single argument, if "true", says to fail if -dnl Kerberos could not be found. +dnl Kerberos could not be found. Set up rra_krb5_incroot for later header +dnl checking. AC_DEFUN([_RRA_LIB_KRB5_INTERNAL], [AC_REQUIRE([RRA_ENABLE_REDUCED_DEPENDS]) + rra_krb5_incroot= + AS_IF([test x"$rra_krb5_includedir" != x], + [rra_krb5_incroot="$rra_krb5_includedir"], + [AS_IF([test x"$rra_krb5_root" != x], + [rra_krb5_incroot="${rra_krb5_root}/include"])]) AS_IF([test x"$rra_reduced_depends" = xtrue], [_RRA_LIB_KRB5_PATHS _RRA_LIB_KRB5_REDUCED([$1])], - [AC_ARG_VAR([KRB5_CONFIG], [Path to krb5-config]) - AS_IF([test x"$rra_krb5_root" != x && test -z "$KRB5_CONFIG"], - [AS_IF([test -x "${rra_krb5_root}/bin/krb5-config"], - [KRB5_CONFIG="${rra_krb5_root}/bin/krb5-config"])], - [AC_PATH_PROG([KRB5_CONFIG], [krb5-config])]) - AS_IF([test x"$KRB5_CONFIG" != x && test -x "$KRB5_CONFIG"], - [AC_CACHE_CHECK([for krb5 support in krb5-config], - [rra_cv_lib_krb5_config], - [AS_IF(["$KRB5_CONFIG" 2>&1 | grep krb5 >/dev/null 2>&1], - [rra_cv_lib_krb5_config=yes], - [rra_cv_lib_krb5_config=no])]) - AS_IF([test x"$rra_cv_lib_krb5_config" = xyes], - [KRB5_CPPFLAGS=`"$KRB5_CONFIG" --cflags krb5 2>/dev/null` - KRB5_LIBS=`"$KRB5_CONFIG" --libs krb5 2>/dev/null`], - [KRB5_CPPFLAGS=`"$KRB5_CONFIG" --cflags 2>/dev/null` - KRB5_LIBS=`"$KRB5_CONFIG" --libs 2>/dev/null`]) - KRB5_CPPFLAGS=`echo "$KRB5_CPPFLAGS" | sed 's%-I/usr/include ?%%'` - _RRA_LIB_KRB5_CHECK([$1]) - RRA_LIB_KRB5_SWITCH - AC_CHECK_FUNCS([krb5_get_error_message], - [AC_CHECK_FUNCS([krb5_free_error_message])], - [AC_CHECK_FUNCS([krb5_get_error_string], , - [AC_CHECK_FUNCS([krb5_get_err_txt], , - [AC_CHECK_FUNCS([krb5_svc_get_msg], - [AC_CHECK_HEADERS([ibm_svc/krb5_svc.h])], - [AC_CHECK_HEADERS([et/com_err.h])])])])]) - RRA_LIB_KRB5_RESTORE], - [_RRA_LIB_KRB5_PATHS - _RRA_LIB_KRB5_MANUAL([$1])])]) + [AS_IF([test x"$rra_krb5_includedir" = x && test x"$rra_krb5_libdir" = x], + [_RRA_LIB_KRB5_CONFIG([$1])], + [_RRA_LIB_KRB5_PATHS + _RRA_LIB_KRB5_MANUAL([$1])])]) rra_krb5_uses_com_err=false - case "$LIBS" in - *-lcom_err*) - rra_krb5_uses_com_err=true - ;; - esac + AS_CASE([$LIBS], [*-lcom_err*], [rra_krb5_uses_com_err=true]) AM_CONDITIONAL([KRB5_USES_COM_ERR], [test x"$rra_krb5_uses_com_err" = xtrue])]) dnl The main macro for packages with mandatory Kerberos support. @@ -208,26 +250,28 @@ AC_DEFUN([RRA_LIB_KRB5], [rra_krb5_root= rra_krb5_libdir= rra_krb5_includedir= + rra_use_kerberos=true AC_SUBST([KRB5_CPPFLAGS]) AC_SUBST([KRB5_LDFLAGS]) AC_SUBST([KRB5_LIBS]) AC_ARG_WITH([krb5], [AS_HELP_STRING([--with-krb5=DIR], - [Location of Kerberos v5 headers and libraries])], + [Location of Kerberos headers and libraries])], [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], [rra_krb5_root="$withval"])]) AC_ARG_WITH([krb5-include], [AS_HELP_STRING([--with-krb5-include=DIR], - [Location of Kerberos v5 headers])], + [Location of Kerberos headers])], [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], [rra_krb5_includedir="$withval"])]) AC_ARG_WITH([krb5-lib], [AS_HELP_STRING([--with-krb5-lib=DIR], - [Location of Kerberos v5 libraries])], + [Location of Kerberos libraries])], [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], [rra_krb5_libdir="$withval"])]) - _RRA_LIB_KRB5_INTERNAL([true])]) + _RRA_LIB_KRB5_INTERNAL([true]) + AC_DEFINE([HAVE_KERBEROS], 1, [Define to enable Kerberos features.])]) dnl The main macro for packages with optional Kerberos support. AC_DEFUN([RRA_LIB_KRB5_OPTIONAL], @@ -241,29 +285,41 @@ AC_DEFUN([RRA_LIB_KRB5_OPTIONAL], AC_ARG_WITH([krb5], [AS_HELP_STRING([--with-krb5@<:@=DIR@:>@], - [Location of Kerberos v5 headers and libraries])], + [Location of Kerberos headers and libraries])], [AS_IF([test x"$withval" = xno], [rra_use_kerberos=false], [AS_IF([test x"$withval" != xyes], [rra_krb5_root="$withval"]) rra_use_kerberos=true])]) AC_ARG_WITH([krb5-include], [AS_HELP_STRING([--with-krb5-include=DIR], - [Location of Kerberos v5 headers])], + [Location of Kerberos headers])], [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], [rra_krb5_includedir="$withval"])]) AC_ARG_WITH([krb5-lib], [AS_HELP_STRING([--with-krb5-lib=DIR], - [Location of Kerberos v5 libraries])], + [Location of Kerberos libraries])], [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], [rra_krb5_libdir="$withval"])]) AS_IF([test x"$rra_use_kerberos" != xfalse], [AS_IF([test x"$rra_use_kerberos" = xtrue], [_RRA_LIB_KRB5_INTERNAL([true])], - [_RRA_LIB_KRB5_INTERNAL([false])])]) + [_RRA_LIB_KRB5_INTERNAL([false])])], + [AM_CONDITIONAL([KRB5_USES_COM_ERR], [false])]) AS_IF([test x"$KRB5_LIBS" != x], [AC_DEFINE([HAVE_KERBEROS], 1, [Define to enable Kerberos features.])])]) +dnl Source used by RRA_FUNC_KRB5_GET_INIT_CREDS_OPT_FREE_ARGS. +AC_DEFUN([_RRA_FUNC_KRB5_OPT_FREE_ARGS_SOURCE], [RRA_INCLUDES_KRB5] [[ +int +main(void) +{ + krb5_get_init_creds_opt *opts; + krb5_context c; + krb5_get_init_creds_opt_free(c, opts); +} +]]) + dnl Check whether krb5_get_init_creds_opt_free takes one argument or two. dnl Early Heimdal used to take a single argument. Defines dnl HAVE_KRB5_GET_INIT_CREDS_OPT_FREE_2_ARGS if it takes two arguments. @@ -272,9 +328,7 @@ dnl Should be called with RRA_LIB_KRB5_SWITCH active. AC_DEFUN([RRA_FUNC_KRB5_GET_INIT_CREDS_OPT_FREE_ARGS], [AC_CACHE_CHECK([if krb5_get_init_creds_opt_free takes two arguments], [rra_cv_func_krb5_get_init_creds_opt_free_args], - [AC_TRY_COMPILE([#include ], - [krb5_get_init_creds_opt *opts; krb5_context c; - krb5_get_init_creds_opt_free(c, opts);], + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_RRA_FUNC_KRB5_OPT_FREE_ARGS_SOURCE])], [rra_cv_func_krb5_get_init_creds_opt_free_args=yes], [rra_cv_func_krb5_get_init_creds_opt_free_args=no])]) AS_IF([test $rra_cv_func_krb5_get_init_creds_opt_free_args = yes], diff --git a/m4/lib-depends.m4 b/m4/lib-depends.m4 index 039e245..b5185f3 100644 --- a/m4/lib-depends.m4 +++ b/m4/lib-depends.m4 @@ -9,11 +9,16 @@ dnl dnl This macro doesn't do much but is defined separately so that other macros dnl can require it with AC_REQUIRE. dnl +dnl The canonical version of this file is maintained in the rra-c-util +dnl package, available at . +dnl dnl Written by Russ Allbery dnl Copyright 2005, 2006, 2007 -dnl Board of Trustees, Leland Stanford Jr. University +dnl The Board of Trustees of the Leland Stanford Junior University dnl -dnl See LICENSE for licensing terms. +dnl This file is free software; the authors give unlimited permission to copy +dnl and/or distribute it, with or without modifications, as long as this +dnl notice is preserved. AC_DEFUN([RRA_ENABLE_REDUCED_DEPENDS], [rra_reduced_depends=false diff --git a/m4/lib-pathname.m4 b/m4/lib-pathname.m4 index fc326a0..fd5a5a1 100644 --- a/m4/lib-pathname.m4 +++ b/m4/lib-pathname.m4 @@ -12,10 +12,16 @@ dnl dnl This file also provides the Autoconf macro RRA_SET_LIBDIR, which sets the dnl libdir variable to PREFIX/lib{,32,64} as appropriate. dnl +dnl The canonical version of this file is maintained in the rra-c-util +dnl package, available at . +dnl dnl Written by Russ Allbery -dnl Copyright 2008, 2009 Board of Trustees, Leland Stanford Jr. University +dnl Copyright 2008, 2009 +dnl The Board of Trustees of the Leland Stanford Junior University dnl -dnl See LICENSE for licensing terms. +dnl This file is free software; the authors give unlimited permission to copy +dnl and/or distribute it, with or without modifications, as long as this +dnl notice is preserved. dnl Probe for the alternate library name that we should attempt on this dnl architecture, given the size of an int, and set rra_lib_arch_name to that diff --git a/m4/remctl.m4 b/m4/remctl.m4 index bb3a56f..588404f 100644 --- a/m4/remctl.m4 +++ b/m4/remctl.m4 @@ -10,15 +10,28 @@ dnl REMCTL_CPPFLAGS, REMCTL_LDFLAGS, and REMCTL_LIBS. Also provides dnl RRA_LIB_REMCTL_SWITCH to set CPPFLAGS, LDFLAGS, and LIBS to include the dnl remctl libraries, saving the current values first, and dnl RRA_LIB_REMCTL_RESTORE to restore those settings to before the last -dnl RRA_LIB_REMCTL_SWITCH. +dnl RRA_LIB_REMCTL_SWITCH. HAVE_REMCTL will always be defined if +dnl RRA_LIB_REMCTL is used. +dnl +dnl Provides the RRA_LIB_REMCTL_OPTIONAL macro, which should be used if +dnl Kerberos support is optional. This macro will still always est the +dnl substitution variables, but they'll be empty unless --with-remctl is +dnl given. HAVE_REMCTL will be defined if --with-remctl is given and +dnl $rra_use_remctl will be set to "true". dnl dnl Depends on RRA_ENABLE_REDUCED_DEPENDS, RRA_SET_LDFLAGS, and dnl RRA_LIB_GSSAPI. dnl +dnl The canonical version of this file is maintained in the rra-c-util +dnl package, available at . +dnl dnl Written by Russ Allbery -dnl Copyright 2008, 2009 Board of Trustees, Leland Stanford Jr. University +dnl Copyright 2008, 2009, 2011 +dnl The Board of Trustees of the Leland Stanford Junior University dnl -dnl See LICENSE for licensing terms. +dnl This file is free software; the authors give unlimited permission to copy +dnl and/or distribute it, with or without modifications, as long as this +dnl notice is preserved. dnl Save the current CPPFLAGS, LDFLAGS, and LIBS settings and switch to dnl versions that include the remctl flags. Used as a wrapper, with @@ -55,16 +68,34 @@ dnl Sanity-check the results of the remctl library search to be sure we can dnl really link a remctl program. AC_DEFUN([_RRA_LIB_REMCTL_CHECK], [RRA_LIB_REMCTL_SWITCH - AC_CHECK_FUNC([remctl_open], , - [AC_MSG_FAILURE([unable to link with remctl library])]) + AC_CHECK_FUNC([remctl_open], [], + [AS_IF([test x"$1" = xtrue], + [AC_MSG_FAILURE([unable to link with remctl library])]) + REMCTL_CPPFLAGS= + REMCTL_LDFLAGS= + REMCTL_LIBS=]) RRA_LIB_REMCTL_RESTORE]) -dnl The main macro. -AC_DEFUN([RRA_LIB_REMCTL], +dnl The core of the library checking, shared between RRA_LIB_REMCTL and +dnl RRA_LIB_REMCTL_OPTIONAL. The single argument, if "true", says to fail if +dnl remctl could not be found. +AC_DEFUN([_RRA_LIB_REMCTL_INTERNAL], [AC_REQUIRE([RRA_ENABLE_REDUCED_DEPENDS]) - rra_remctl_root= + _RRA_LIB_REMCTL_PATHS + AS_IF([test x"$rra_reduced_depends" = xtrue], + [REMCTL_LIBS="-lremctl"], + [RRA_LIB_GSSAPI + REMCTL_CPPFLAGS="$REMCTL_CPPFLAGS $GSSAPI_CPPFLAGS" + REMCTL_LDFLAGS="$REMCTL_LDFLAGS $GSSAPI_LDFLAGS" + REMCTL_LIBS="-lremctl $GSSAPI_LIBS"]) + _RRA_LIB_REMCTL_CHECK([$1])]) + +dnl The main macro for packages with mandatory remctl support. +AC_DEFUN([RRA_LIB_REMCTL], +[rra_remctl_root= rra_remctl_libdir= rra_remctl_includedir= + rra_use_remctl=true REMCTL_CPPFLAGS= REMCTL_LDFLAGS= REMCTL_LIBS= @@ -87,12 +118,43 @@ AC_DEFUN([RRA_LIB_REMCTL], [Location of remctl libraries])], [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], [rra_remctl_libdir="$withval"])]) + _RRA_LIB_REMCTL_INTERNAL([true]) + AC_DEFINE([HAVE_REMCTL], 1, [Define to enable remctl features.])]) - _RRA_LIB_REMCTL_PATHS - AS_IF([test x"$rra_reduced_depends" = xtrue], - [REMCTL_LIBS="-lremctl"], - [RRA_LIB_GSSAPI - REMCTL_CPPFLAGS="$REMCTL_CPPFLAGS $GSSAPI_CPPFLAGS" - REMCTL_LDFLAGS="$REMCTL_LDFLAGS $GSSAPI_LDFLAGS" - REMCTL_LIBS="-lremctl $GSSAPI_LIBS"]) - _RRA_LIB_REMCTL_CHECK]) +dnl The main macro for packages with optional remctl support. +AC_DEFUN([RRA_LIB_REMCTL_OPTIONAL], +[rra_remctl_root= + rra_remctl_libdir= + rra_remctl_includedir= + rra_use_remctl= + REMCTL_CPPFLAGS= + REMCTL_LDFLAGS= + REMCTL_LIBS= + AC_SUBST([REMCTL_CPPFLAGS]) + AC_SUBST([REMCTL_LDFLAGS]) + AC_SUBST([REMCTL_LIBS]) + + AC_ARG_WITH([remctl], + [AS_HELP_STRING([--with-remctl@<:@=DIR@:>@], + [Location of remctl headers and libraries])], + [AS_IF([test x"$withval" = xno], + [rra_use_remctl=false], + [AS_IF([test x"$withval" != xyes], [rra_remctl_root="$withval"]) + rra_use_remctl=true])]) + AC_ARG_WITH([remctl-include], + [AS_HELP_STRING([--with-remctl-include=DIR], + [Location of remctl headers])], + [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], + [rra_remctl_includedir="$withval"])]) + AC_ARG_WITH([remctl-lib], + [AS_HELP_STRING([--with-remctl-lib=DIR], + [Location of remctl libraries])], + [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], + [rra_remctl_libdir="$withval"])]) + AS_IF([test x"$rra_use_remctl" != xfalse], + [AS_IF([test x"$rra_use_remctl" = xtrue], + [_RRA_LIB_REMCTL_INTERNAL([true])], + [_RRA_LIB_REMCTL_INTERNAL([false])])]) + AS_IF([test x"$REMCTL_LIBS" != x], + [rra_use_remctl=true + AC_DEFINE([HAVE_REMCTL], 1, [Define to enable remctl features.])])]) diff --git a/m4/snprintf.m4 b/m4/snprintf.m4 index d933f55..cd585ef 100644 --- a/m4/snprintf.m4 +++ b/m4/snprintf.m4 @@ -9,11 +9,16 @@ dnl dnl Provides RRA_FUNC_SNPRINTF, which adds snprintf.o to LIBOBJS unless a dnl fully working snprintf is found. dnl +dnl The canonical version of this file is maintained in the rra-c-util +dnl package, available at . +dnl dnl Written by Russ Allbery dnl Copyright 2006, 2008, 2009 -dnl Board of Trustees, Leland Stanford Jr. University +dnl The Board of Trustees of the Leland Stanford Junior University dnl -dnl See LICENSE for licensing terms. +dnl This file is free software; the authors give unlimited permission to copy +dnl and/or distribute it, with or without modifications, as long as this +dnl notice is preserved. dnl Source used by RRA_FUNC_SNPRINTF. AC_DEFUN([_RRA_FUNC_SNPRINTF_SOURCE], [[ diff --git a/m4/vamacros.m4 b/m4/vamacros.m4 index 855bb40..af98f6a 100644 --- a/m4/vamacros.m4 +++ b/m4/vamacros.m4 @@ -13,11 +13,16 @@ dnl #define macro(args...) fprintf(stderr, args) dnl dnl They set HAVE_C99_VAMACROS or HAVE_GNU_VAMACROS as appropriate. dnl +dnl The canonical version of this file is maintained in the rra-c-util +dnl package, available at . +dnl dnl Written by Russ Allbery dnl Copyright 2006, 2008, 2009 -dnl Board of Trustees, Leland Stanford Jr. University +dnl The Board of Trustees of the Leland Stanford Junior University dnl -dnl See LICENSE for licensing terms. +dnl This file is free software; the authors give unlimited permission to copy +dnl and/or distribute it, with or without modifications, as long as this +dnl notice is preserved. AC_DEFUN([_RRA_C_C99_VAMACROS_SOURCE], [[ #include diff --git a/portable/asprintf.c b/portable/asprintf.c index 4219a19..0093070 100644 --- a/portable/asprintf.c +++ b/portable/asprintf.c @@ -4,8 +4,18 @@ * Provides the same functionality as the standard GNU library routines * asprintf and vasprintf for those platforms that don't have them. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * This work is hereby placed in the public domain by its author. + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #include diff --git a/portable/dummy.c b/portable/dummy.c index 8a0d54d..50052ec 100644 --- a/portable/dummy.c +++ b/portable/dummy.c @@ -5,8 +5,18 @@ * supply, Automake builds an empty library and then calls ar with nonsensical * arguments. Ensure that libportable always contains at least one symbol. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * This work is hereby placed in the public domain by its author. + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ /* Prototype to avoid gcc warnings. */ diff --git a/portable/krb5-extra.c b/portable/krb5-extra.c index 89ccbde..849842c 100644 --- a/portable/krb5-extra.c +++ b/portable/krb5-extra.c @@ -6,8 +6,18 @@ * Everything in this file will be protected by #ifndef. If the native * Kerberos libraries are fully capable, this file will be skipped. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * This work is hereby placed in the public domain by its author. + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #include diff --git a/portable/krb5.h b/portable/krb5.h index 3b5700b..b418ae7 100644 --- a/portable/krb5.h +++ b/portable/krb5.h @@ -16,8 +16,18 @@ * prefers the generic krb5_xfree(). In this case, this header provides * krb5_free_unparsed_name() for both APIs since it's the most specific call. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * This work is hereby placed in the public domain by its author. + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #ifndef PORTABLE_KRB5_H @@ -32,7 +42,11 @@ #endif #include -#include +#ifdef HAVE_KRB5_H +# include +#else +# include +#endif #include BEGIN_DECLS @@ -75,7 +89,7 @@ krb5_error_code krb5_get_init_creds_opt_alloc(krb5_context, /* Heimdal-specific. */ #ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_DEFAULT_FLAGS -#define krb5_get_init_creds_opt_set_default_flags(c, p, r, o) /* empty */ +# define krb5_get_init_creds_opt_set_default_flags(c, p, r, o) /* empty */ #endif /* diff --git a/portable/macros.h b/portable/macros.h index 8d5adbd..b33d064 100644 --- a/portable/macros.h +++ b/portable/macros.h @@ -1,8 +1,18 @@ /* * Portability macros used in include files. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * This work is hereby placed in the public domain by its author. + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #ifndef PORTABLE_MACROS_H @@ -19,6 +29,29 @@ # endif #endif +/* + * We use __alloc_size__, but it was only available in fairly recent versions + * of GCC. Suppress warnings about the unknown attribute if GCC is too old. + * We know that we're GCC at this point, so we can use the GCC variadic macro + * extension, which will still work with versions of GCC too old to have C99 + * variadic macro support. + */ +#if !defined(__attribute__) && !defined(__alloc_size__) +# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3) +# define __alloc_size__(spec, args...) /* empty */ +# endif +#endif + +/* + * LLVM and Clang pretend to be GCC but don't support all of the __attribute__ + * settings that GCC does. For them, suppress warnings about unknown + * attributes on declarations. This unfortunately will affect the entire + * compilation context, but there's no push and pop available. + */ +#if !defined(__attribute__) && (defined(__llvm__) || defined(__clang__)) +# pragma GCC diagnostic ignored "-Wattributes" +#endif + /* * BEGIN_DECLS is used at the beginning of declarations so that C++ * compilers don't mangle their names. END_DECLS is used at the end. diff --git a/portable/mkstemp.c b/portable/mkstemp.c index dd2a485..8668db1 100644 --- a/portable/mkstemp.c +++ b/portable/mkstemp.c @@ -4,8 +4,18 @@ * Provides the same functionality as the library function mkstemp for those * systems that don't have it. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * This work is hereby placed in the public domain by its author. + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #include @@ -27,7 +37,7 @@ int test_mkstemp(char *); #endif /* Pick the longest available integer type. */ -#if HAVE_LONG_LONG +#if HAVE_LONG_LONG_INT typedef unsigned long long long_int_type; #else typedef unsigned long long_int_type; diff --git a/portable/setenv.c b/portable/setenv.c index d66ddcd..fd2b10c 100644 --- a/portable/setenv.c +++ b/portable/setenv.c @@ -4,8 +4,18 @@ * Provides the same functionality as the standard library routine setenv for * those platforms that don't have it. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * This work is hereby placed in the public domain by its author. + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #include @@ -31,10 +41,9 @@ setenv(const char *name, const char *value, int overwrite) /* * Allocate memory for the environment string. We intentionally don't use - * concat here, or the xmalloc family of allocation routines, since the - * intention is to provide a replacement for the standard library function - * which sets errno and returns in the event of a memory allocation - * failure. + * the xmalloc family of allocation routines here, since the intention is + * to provide a replacement for the standard library function that sets + * errno and returns in the event of a memory allocation failure. */ size = strlen(name) + 1 + strlen(value) + 1; envstring = malloc(size); diff --git a/portable/snprintf.c b/portable/snprintf.c index ab3121c..91c8491 100644 --- a/portable/snprintf.c +++ b/portable/snprintf.c @@ -8,6 +8,9 @@ * Please do not reformat or otherwise change this file more than necessary so * that later merges with the original source are easy. Bug fixes and * improvements should be sent back to the original author. + * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . */ /* diff --git a/portable/stdbool.h b/portable/stdbool.h index bfbf4c4..045436f 100644 --- a/portable/stdbool.h +++ b/portable/stdbool.h @@ -5,13 +5,31 @@ * following the C99 specification, on hosts that don't have stdbool.h. This * logic is based heavily on the example in the Autoconf manual. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * This work is hereby placed in the public domain by its author. + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #ifndef PORTABLE_STDBOOL_H #define PORTABLE_STDBOOL_H 1 +/* + * Allow inclusion of config.h to be skipped, since sometimes we have to use a + * stripped-down version of config.h with a different name. + */ +#ifndef CONFIG_H_INCLUDED +# include +#endif + #if HAVE_STDBOOL_H # include #else diff --git a/portable/strlcat.c b/portable/strlcat.c index f696db3..3bee4ee 100644 --- a/portable/strlcat.c +++ b/portable/strlcat.c @@ -9,8 +9,18 @@ * space available in the destination buffer, not just the amount of space * remaining. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * This work is hereby placed in the public domain by its author. + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #include diff --git a/portable/strlcpy.c b/portable/strlcpy.c index 596e968..df75fd8 100644 --- a/portable/strlcpy.c +++ b/portable/strlcpy.c @@ -8,8 +8,18 @@ * total space required is returned. The destination string is not nul-filled * like strncpy does, just nul-terminated. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * This work is hereby placed in the public domain by its author. + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #include diff --git a/portable/system.h b/portable/system.h index 461601b..d1ccc94 100644 --- a/portable/system.h +++ b/portable/system.h @@ -13,13 +13,24 @@ * #include * #include * #include + * #include * #include * * Missing functions are provided via #define or prototyped if available from * the portable helper library. Also provides some standard #defines. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * This work is hereby placed in the public domain by its author. + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #ifndef PORTABLE_SYSTEM_H @@ -38,6 +49,9 @@ #include #include #include +#if HAVE_STRINGS_H +# include +#endif #if HAVE_INTTYPES_H # include #endif @@ -56,6 +70,38 @@ /* Get the bool type. */ #include +/* Windows provides snprintf under a different name. */ +#ifdef _WIN32 +# define snprintf _snprintf +#endif + +/* Windows does not define ssize_t. */ +#ifndef HAVE_SSIZE_T +typedef ptrdiff_t ssize_t; +#endif + +/* + * POSIX requires that these be defined in . If one of them has + * been defined, all the rest almost certainly have. + */ +#ifndef STDIN_FILENO +# define STDIN_FILENO 0 +# define STDOUT_FILENO 1 +# define STDERR_FILENO 2 +#endif + +/* + * C99 requires va_copy. Older versions of GCC provide __va_copy. Per the + * Autoconf manual, memcpy is a generally portable fallback. + */ +#ifndef va_copy +# ifdef __va_copy +# define va_copy(d, s) __va_copy((d), (s)) +# else +# define va_copy(d, s) memcpy(&(d), &(s), sizeof(va_list)) +# endif +#endif + BEGIN_DECLS /* Default to a hidden visibility for all portability functions. */ @@ -96,31 +142,4 @@ extern size_t strlcpy(char *, const char *, size_t); END_DECLS -/* Windows provides snprintf under a different name. */ -#ifdef _WIN32 -# define snprintf _snprintf -#endif - -/* - * POSIX requires that these be defined in . If one of them has - * been defined, all the rest almost certainly have. - */ -#ifndef STDIN_FILENO -# define STDIN_FILENO 0 -# define STDOUT_FILENO 1 -# define STDERR_FILENO 2 -#endif - -/* - * C99 requires va_copy. Older versions of GCC provide __va_copy. Per the - * Autoconf manual, memcpy is a generally portable fallback. - */ -#ifndef va_copy -# ifdef __va_copy -# define va_copy(d, s) __va_copy((d), (s)) -# else -# define va_copy(d, s) memcpy(&(d), &(s), sizeof(va_list)) -# endif -#endif - #endif /* !PORTABLE_SYSTEM_H */ diff --git a/portable/uio.h b/portable/uio.h index 3c9e840..3bd1f96 100644 --- a/portable/uio.h +++ b/portable/uio.h @@ -5,8 +5,18 @@ * (primarily Windows). Currently, the corresponding readv and writev * functions are not provided or prototyped here. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * This work is hereby placed in the public domain by its author. + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #ifndef PORTABLE_UIO_H diff --git a/tests/client/full-t.in b/tests/client/full-t.in index ce2789d..680e78f 100644 --- a/tests/client/full-t.in +++ b/tests/client/full-t.in @@ -56,19 +56,19 @@ chdir "$ENV{SOURCE}" or die "Cannot chdir to $ENV{SOURCE}: $!\n"; SKIP: { skip 'no keytab configuration', $total - unless -f "$ENV{BUILD}/data/test.keytab"; + unless -f "$ENV{BUILD}/config/keytab"; my $remctld = '@REMCTLD@'; skip 'remctld not found', $total unless $remctld; # Spawn remctld and get local tickets. Don't destroy the user's Kerberos # ticket cache. unlink ('krb5cc_test', 'test-pid'); - my $principal = contents ("$ENV{BUILD}/data/test.principal"); + my $principal = contents ("$ENV{BUILD}/config/principal"); remctld_spawn ($remctld, $principal, - "$ENV{BUILD}/data/test.keytab", + "$ENV{BUILD}/config/keytab", "$ENV{SOURCE}/data/full.conf"); $ENV{KRB5CCNAME} = 'krb5cc_test'; - getcreds ("$ENV{BUILD}/data/test.keytab", $principal); + getcreds ("$ENV{BUILD}/config/keytab", $principal); # Use Wallet::Admin to set up the database. db_setup; diff --git a/tests/client/prompt-t.in b/tests/client/prompt-t.in index 1d8b079..682cd70 100644 --- a/tests/client/prompt-t.in +++ b/tests/client/prompt-t.in @@ -21,11 +21,11 @@ chdir "$ENV{SOURCE}" or die "Cannot chdir to $ENV{SOURCE}: $!\n"; SKIP: { skip 'no password configuration', $total - unless -f "$ENV{BUILD}/data/test.password"; + unless -f "$ENV{BUILD}/config/password"; my $remctld = '@REMCTLD@'; skip 'remctld not found', $total unless $remctld; eval { require Expect }; - skip 'Exepct module not found', $total if $@; + skip 'Expect module not found', $total if $@; # Disable sending of wallet's output to our standard output. Do this # twice to avoid Perl warnings. @@ -34,14 +34,14 @@ SKIP: { # Spawn remctld and set up with a different ticket cache. unlink ('krb5cc_test', 'test-pid'); - my $principal = contents ("$ENV{BUILD}/data/test.principal"); - remctld_spawn ($remctld, $principal, "$ENV{BUILD}/data/test.keytab", + my $principal = contents ("$ENV{BUILD}/config/principal"); + remctld_spawn ($remctld, $principal, "$ENV{BUILD}/config/keytab", "$ENV{SOURCE}/data/basic.conf"); $ENV{KRB5CCNAME} = 'krb5cc_test'; # Read in the principal and password. - open (PASS, '<', "$ENV{BUILD}/data/test.password") - or die "Cannot open $ENV{BUILD}/data/test.password: $!\n"; + open (PASS, '<', "$ENV{BUILD}/config/password") + or die "Cannot open $ENV{BUILD}/config/password: $!\n"; my $user = ; my $password = ; close PASS; diff --git a/tests/config/README b/tests/config/README new file mode 100644 index 0000000..2992a11 --- /dev/null +++ b/tests/config/README @@ -0,0 +1,24 @@ +This directory contains configuration required to run the complete wallet +test suite. If there is no configuration in this directory, some of the +tests will be skipped. To enable the full test suite, create the +following files: + +keytab + + A valid Kerberos keytab for a principal, preferrably in your local + realm. This will be used to test network interactions that require + Kerberos authentication. + +principal + + The name of the Kerberos principal whose keys are stored in keytab. + +password + + This file should contain two lines. The first line is the + fully-qualified principal (including the realm) of a Kerberos + principal to use for testing authentication. The second line is + the password for that principal. The realm of the principal must + be configured in your system krb5.conf file or in DNS configuration + picked up by your Kerberos libraries and must be in the same realm as + the keytab above or have valid cross-realm trust to it. diff --git a/tests/data/perl.conf b/tests/data/perl.conf new file mode 100644 index 0000000..eaf7443 --- /dev/null +++ b/tests/data/perl.conf @@ -0,0 +1,6 @@ +# Configuration for Perl tests. -*- perl -*- + +# No special configuration yet. + +# File must end with this line. +1; diff --git a/tests/docs/pod-spelling-t b/tests/docs/pod-spelling-t index eaa7dd6..e1a95cd 100755 --- a/tests/docs/pod-spelling-t +++ b/tests/docs/pod-spelling-t @@ -1,80 +1,52 @@ #!/usr/bin/perl # -# Check for spelling errors in POD documentation +# Checks all POD files in the tree for spelling errors using Test::Spelling. +# This test is disabled unless RRA_MAINTAINER_TESTS is set, since spelling +# dictionaries vary too much between environments. # -# Checks all POD files in the tree for spelling problems using Pod::Spell and -# either aspell or ispell. aspell is preferred. This test is disabled unless -# RRA_MAINTAINER_TESTS is set, since spelling dictionaries vary too much -# between environments. +# The canonical version of this file is maintained in the rra-c-util package, +# which can be found at . # -# Copyright 2008, 2009 Russ Allbery +# Written by Russ Allbery +# Copyright 2012, 2013 +# The Board of Trustees of the Leland Stanford Junior University # -# See LICENSE for licensing terms. +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +use 5.006; use strict; -use Test::More; +use warnings; -# Skip all spelling tests unless the maintainer environment variable is set. -plan skip_all => 'spelling tests only run for maintainer' - unless $ENV{RRA_MAINTAINER_TESTS}; +use lib "$ENV{SOURCE}/tap/perl"; + +use Test::More; +use Test::RRA qw(skip_unless_maintainer use_prereq); +use Test::RRA::Automake qw(automake_setup perl_dirs); -# Load required Perl modules. -eval 'use Test::Pod 1.00'; -plan skip_all => 'Test::Pod 1.00 required for testing POD' if $@; -eval 'use Pod::Spell'; -plan skip_all => 'Pod::Spell required to test POD spelling' if $@; +# Only run this test for the maintainer. +skip_unless_maintainer('Spelling tests'); -# Locate a spell-checker. hunspell is not currently supported due to its lack -# of support for contractions (at least in the version in Debian). -my @spell; -my %options = (aspell => [ qw(-d en_US --home-dir=./ list) ], - ispell => [ qw(-d american -l -p /dev/null) ]); -SEARCH: for my $program (qw/aspell ispell/) { - for my $dir (split ':', $ENV{PATH}) { - if (-x "$dir/$program") { - @spell = ("$dir/$program", @{ $options{$program} }); - } - last SEARCH if @spell; - } -} -plan skip_all => 'aspell or ispell required to test POD spelling' - unless @spell; +# Load prerequisite modules. +use_prereq('Test::Spelling'); -# Prerequisites are satisfied, so we're going to do some testing. Figure out -# what POD files we have and from that develop our plan. -$| = 1; -my @pod = map { - my $pod = "$ENV{SOURCE}/../" . $_; - $pod =~ s,[^/.][^/]*/../,,g; - $pod; -} qw(client/wallet.pod client/wallet-rekey.pod server/keytab-backend - server/wallet-admin server/wallet-backend server/wallet-report); -plan tests => scalar @pod; +# Set up Automake testing. +automake_setup(); -# Finally, do the checks. -for my $pod (@pod) { - my $child = open (CHILD, '-|'); - if (not defined $child) { - BAIL_OUT ("cannot fork: $!"); - } elsif ($child == 0) { - my $pid = open (SPELL, '|-', @spell) - or BAIL_OUT ("cannot run @spell: $!"); - open (POD, '<', $pod) or BAIL_OUT ("cannot open $pod: $!"); - my $parser = Pod::Spell->new; - $parser->parse_from_filehandle (\*POD, \*SPELL); - close POD; - close SPELL; - exit ($? >> 8); - } else { - my @words = ; - close CHILD; - SKIP: { - skip "@spell failed for $pod", 1 unless $? == 0; - for (@words) { - s/^\s+//; - s/\s+$//; - } - is ("@words", '', $pod); - } - } -} +# Run the tests. +all_pod_files_spelling_ok(perl_dirs()); diff --git a/tests/docs/pod-t b/tests/docs/pod-t index e25ade2..2743287 100755 --- a/tests/docs/pod-t +++ b/tests/docs/pod-t @@ -1,22 +1,48 @@ #!/usr/bin/perl -w # -# Test POD formatting for documentation. +# Check all POD documents in the tree, except for any embedded Perl module +# distribution, for POD formatting errors. +# +# The canonical version of this file is maintained in the rra-c-util package, +# which can be found at . # # Written by Russ Allbery -# Copyright 2008, 2010 Board of Trustees, Leland Stanford Jr. University +# Copyright 2012, 2013 +# The Board of Trustees of the Leland Stanford Junior University +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: # -# See LICENSE for licensing terms. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +use 5.006; use strict; +use warnings; + +use lib "$ENV{SOURCE}/tap/perl"; + use Test::More; -eval 'use Test::Pod 1.00'; -plan skip_all => 'Test::Pod 1.00 required for testing POD' if $@; +use Test::RRA qw(use_prereq); +use Test::RRA::Automake qw(automake_setup perl_dirs); + +# Load prerequisite modules. +use_prereq('Test::Pod'); + +# Set up Automake testing. +automake_setup(); -my @files = qw(client/wallet.pod client/wallet-rekey.pod server/keytab-backend - server/wallet-admin server/wallet-backend - server/wallet-report); -my $total = scalar (@files); -plan tests => $total; -for my $file (@files) { - pod_file_ok ("$ENV{SOURCE}/../$file", $file); -} +# Run the tests. +all_pod_files_ok(perl_dirs()); diff --git a/tests/portable/asprintf-t.c b/tests/portable/asprintf-t.c index 04fbd1b..4513a85 100644 --- a/tests/portable/asprintf-t.c +++ b/tests/portable/asprintf-t.c @@ -1,11 +1,18 @@ /* * asprintf and vasprintf test suite. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * Copyright 2006, 2008, 2009 - * Board of Trustees, Leland Stanford Jr. University * - * See LICENSE for licensing terms. + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #include diff --git a/tests/portable/mkstemp-t.c b/tests/portable/mkstemp-t.c index 54701f7..98c708e 100644 --- a/tests/portable/mkstemp-t.c +++ b/tests/portable/mkstemp-t.c @@ -1,11 +1,18 @@ /* * mkstemp test suite. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * Copyright 2002, 2004, 2008, 2009 - * Board of Trustees, Leland Stanford Jr. University * - * See LICENSE for licensing terms. + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #include diff --git a/tests/portable/setenv-t.c b/tests/portable/setenv-t.c index 5bc59ce..a1aecb5 100644 --- a/tests/portable/setenv-t.c +++ b/tests/portable/setenv-t.c @@ -1,14 +1,18 @@ /* * setenv test suite. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * Copyright 2009 Board of Trustees, Leland Stanford Jr. University - * Copyright (c) 2004, 2005, 2006 - * by Internet Systems Consortium, Inc. ("ISC") - * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, - * 2002, 2003 by The Internet Software Consortium and Rich Salz * - * See LICENSE for licensing terms. + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #include diff --git a/tests/portable/snprintf-t.c b/tests/portable/snprintf-t.c index 4b64f5b..927de96 100644 --- a/tests/portable/snprintf-t.c +++ b/tests/portable/snprintf-t.c @@ -1,14 +1,20 @@ /* * snprintf test suite. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * Copyright 2009, 2010 Board of Trustees, Leland Stanford Jr. University - * Copyright (c) 2004, 2005, 2006 - * by Internet Systems Consortium, Inc. ("ISC") - * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, - * 2002, 2003 by The Internet Software Consortium and Rich Salz + * Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006 + * Russ Allbery + * Copyright 2009, 2010 + * The Board of Trustees of the Leland Stanford Junior University + * Copyright 1995 Patrick Powell + * Copyright 2001 Hrvoje Niksic * - * See LICENSE for licensing terms. + * This code is based on code written by Patrick Powell (papowell@astart.com) + * It may be used for any purpose as long as this notice remains intact + * on all source code distributions */ #include diff --git a/tests/portable/strlcat-t.c b/tests/portable/strlcat-t.c index e02c277..54d0d40 100644 --- a/tests/portable/strlcat-t.c +++ b/tests/portable/strlcat-t.c @@ -1,14 +1,18 @@ /* * strlcat test suite. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * Copyright 2009 Board of Trustees, Leland Stanford Jr. University - * Copyright (c) 2004, 2005, 2006 - * by Internet Systems Consortium, Inc. ("ISC") - * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, - * 2002, 2003 by The Internet Software Consortium and Rich Salz * - * See LICENSE for licensing terms. + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #include diff --git a/tests/portable/strlcpy-t.c b/tests/portable/strlcpy-t.c index ba224ba..26aa8f2 100644 --- a/tests/portable/strlcpy-t.c +++ b/tests/portable/strlcpy-t.c @@ -1,14 +1,18 @@ /* * strlcpy test suite. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * Copyright 2009 Board of Trustees, Leland Stanford Jr. University - * Copyright (c) 2004, 2005, 2006 - * by Internet Systems Consortium, Inc. ("ISC") - * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, - * 2002, 2003 by The Internet Software Consortium and Rich Salz * - * See LICENSE for licensing terms. + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #include diff --git a/tests/runtests.c b/tests/runtests.c index ab77629..4249875 100644 --- a/tests/runtests.c +++ b/tests/runtests.c @@ -3,14 +3,15 @@ * * Usage: * - * runtests + * runtests [-b ] [-s ] + * runtests -o [-b ] [-s ] * - * Expects a list of executables located in the given file, one line per - * executable. For each one, runs it as part of a test suite, reporting - * results. Test output should start with a line containing the number of - * tests (numbered from 1 to this number), optionally preceded by "1..", - * although that line may be given anywhere in the output. Each additional - * line should be in the following format: + * In the first case, expects a list of executables located in the given file, + * one line per executable. For each one, runs it as part of a test suite, + * reporting results. Test output should start with a line containing the + * number of tests (numbered from 1 to this number), optionally preceded by + * "1..", although that line may be given anywhere in the output. Each + * additional line should be in the following format: * * ok * not ok @@ -39,10 +40,21 @@ * This is a subset of TAP as documented in Test::Harness::TAP or * TAP::Parser::Grammar, which comes with Perl. * + * If the -o option is given, instead run a single test and display all of its + * output. This is intended for use with failing tests so that the person + * running the test suite can get more details about what failed. + * + * If built with the C preprocessor symbols SOURCE and BUILD defined, C TAP + * Harness will export those values in the environment so that tests can find + * the source and build directory and will look for tests under both + * directories. These paths can also be set with the -b and -s command-line + * options, which will override anything set at build time. + * * Any bug reports, bug fixes, and improvements are very much welcome and - * should be sent to the e-mail address below. + * should be sent to the e-mail address below. This program is part of C TAP + * Harness . * - * Copyright 2000, 2001, 2004, 2006, 2007, 2008, 2009, 2010 + * Copyright 2000, 2001, 2004, 2006, 2007, 2008, 2009, 2010, 2011 * Russ Allbery * * Permission is hereby granted, free of charge, to any person obtaining a @@ -64,6 +76,13 @@ * DEALINGS IN THE SOFTWARE. */ +/* Required for fdopen(), getopt(), and putenv(). */ +#if defined(__STRICT_ANSI__) || defined(PEDANTIC) +# ifndef _XOPEN_SOURCE +# define _XOPEN_SOURCE 500 +# endif +#endif + #include #include #include @@ -71,6 +90,7 @@ #include #include #include +#include #include #include #include @@ -133,10 +153,10 @@ struct testset { unsigned long skipped; /* Count of skipped tests (passed). */ unsigned long allocated; /* The size of the results table. */ enum test_status *results; /* Table of results by test number. */ - int aborted; /* Whether the set as aborted. */ + unsigned int aborted; /* Whether the set as aborted. */ int reported; /* Whether the results were reported. */ int status; /* The exit status of the test. */ - int all_skipped; /* Whether all tests were skipped. */ + unsigned int all_skipped; /* Whether all tests were skipped. */ char *reason; /* Why all tests were skipped. */ }; @@ -146,6 +166,23 @@ struct testlist { struct testlist *next; }; +/* + * Usage message. Should be used as a printf format with two arguments: the + * path to runtests, given twice. + */ +static const char usage_message[] = "\ +Usage: %s [-b ] [-s ] \n\ + %s -o [-b ] [-s ] \n\ +\n\ +Options:\n\ + -b Set the build directory to \n\ + -o Run a single test rather than a list of tests\n\ + -s Set the source directory to \n\ +\n\ +runtests normally runs each test listed in a file whose path is given as\n\ +its command-line argument. With the -o option, it instead runs a single\n\ +test and shows its complete output.\n"; + /* * Header used for test output. %s is replaced by the file name of the list * of tests. @@ -367,7 +404,9 @@ test_plan(const char *line, struct testset *ts) * Get the count, check it for validity, and initialize the struct. If we * have something of the form "1..0 # skip foo", the whole file was * skipped; record that. If we do skip the whole file, zero out all of - * our statistics, since they're no longer relevant. + * our statistics, since they're no longer relevant. strtol is called + * with a second argument to advance the line pointer past the count to + * make it simpler to detect the # skip case. */ n = strtol(line, (char **) &line, 10); if (n == 0) { @@ -437,6 +476,7 @@ test_checkline(const char *line, struct testset *ts) char *end; long number; unsigned long i, current; + int outlen; /* Before anything, check for a test abort. */ bail = strstr(line, "Bail out!"); @@ -557,7 +597,8 @@ test_checkline(const char *line, struct testset *ts) ts->results[current - 1] = status; test_backspace(ts); if (isatty(STDOUT_FILENO)) { - ts->length = printf("%lu/%lu", current, ts->count); + outlen = printf("%lu/%lu", current, ts->count); + ts->length = (outlen >= 0) ? outlen : 0; fflush(stdout); } } @@ -565,23 +606,20 @@ test_checkline(const char *line, struct testset *ts) /* * Print out a range of test numbers, returning the number of characters it - * took up. Add a comma and a space before the range if chars indicates that + * took up. Takes the first number, the last number, the number of characters + * already printed on the line, and the limit of number of characters the line + * can hold. Add a comma and a space before the range if chars indicates that * something has already been printed on the line, and print ... instead if * chars plus the space needed would go over the limit (use a limit of 0 to - * disable this. + * disable this). */ static unsigned int test_print_range(unsigned long first, unsigned long last, unsigned int chars, unsigned int limit) { unsigned int needed = 0; - unsigned int out = 0; unsigned long n; - if (chars > 0) { - needed += 2; - if (!limit || chars <= limit) out += printf(", "); - } for (n = first; n > 0; n /= 10) needed++; if (last > first) { @@ -589,15 +627,26 @@ test_print_range(unsigned long first, unsigned long last, unsigned int chars, needed++; needed++; } - if (limit && chars + needed > limit) { - if (chars <= limit) - out += printf("..."); + if (chars > 0) + needed += 2; + if (limit > 0 && chars + needed > limit) { + needed = 0; + if (chars <= limit) { + if (chars > 0) { + printf(", "); + needed += 2; + } + printf("..."); + needed += 3; + } } else { + if (chars > 0) + printf(", "); if (last > first) - out += printf("%lu-", first); - out += printf("%lu", last); + printf("%lu-", first); + printf("%lu", last); } - return out; + return needed; } @@ -825,14 +874,14 @@ test_fail_summary(const struct testlist *fails) last = i + 1; else { if (first != 0) - chars += test_print_range(first, last, chars, 20); + chars += test_print_range(first, last, chars, 19); first = i + 1; last = i + 1; } } } if (first != 0) - test_print_range(first, last, chars, 20); + test_print_range(first, last, chars, 19); putchar('\n'); free(ts->file); free(ts->path); @@ -861,10 +910,17 @@ find_test(const char *name, struct testset *ts, const char *source, const char *build) { char *path; - const char *bases[] = { ".", build, source, NULL }; + const char *bases[4]; unsigned int i; - for (i = 0; bases[i] != NULL; i++) { + bases[0] = "."; + bases[1] = build; + bases[2] = source; + bases[3] = NULL; + + for (i = 0; i < 3; i++) { + if (bases[i] == NULL) + continue; path = xmalloc(strlen(bases[i]) + strlen(name) + 4); sprintf(path, "%s/%s-t", bases[i], name); if (access(path, X_OK) != 0) @@ -993,6 +1049,7 @@ test_batch(const char *testlist, const char *source, const char *build) failed += ts.failed; } total -= skipped; + fclose(tests); /* Stop the timer and get our child resource statistics. */ gettimeofday(&end, NULL); @@ -1060,17 +1117,23 @@ int main(int argc, char *argv[]) { int option; + int status = 0; int single = 0; - char *setting; + char *source_env = NULL; + char *build_env = NULL; const char *list; const char *source = SOURCE; const char *build = BUILD; - while ((option = getopt(argc, argv, "b:os:")) != EOF) { + while ((option = getopt(argc, argv, "b:hos:")) != EOF) { switch (option) { case 'b': build = optarg; break; + case 'h': + printf(usage_message, argv[0], argv[0]); + exit(0); + break; case 'o': single = 1; break; @@ -1081,36 +1144,46 @@ main(int argc, char *argv[]) exit(1); } } - argc -= optind; - argv += optind; - if (argc != 1) { - fprintf(stderr, "Usage: runtests \n"); + if (argc - optind != 1) { + fprintf(stderr, usage_message, argv[0], argv[0]); exit(1); } + argc -= optind; + argv += optind; if (source != NULL) { - setting = xmalloc(strlen("SOURCE=") + strlen(source) + 1); - sprintf(setting, "SOURCE=%s", source); - if (putenv(setting) != 0) + source_env = xmalloc(strlen("SOURCE=") + strlen(source) + 1); + sprintf(source_env, "SOURCE=%s", source); + if (putenv(source_env) != 0) sysdie("cannot set SOURCE in the environment"); } if (build != NULL) { - setting = xmalloc(strlen("BUILD=") + strlen(build) + 1); - sprintf(setting, "BUILD=%s", build); - if (putenv(setting) != 0) + build_env = xmalloc(strlen("BUILD=") + strlen(build) + 1); + sprintf(build_env, "BUILD=%s", build); + if (putenv(build_env) != 0) sysdie("cannot set BUILD in the environment"); } - if (single) { + if (single) test_single(argv[0], source, build); - exit(0); - } else { + else { list = strrchr(argv[0], '/'); if (list == NULL) list = argv[0]; else list++; printf(banner, list); - exit(test_batch(argv[0], source, build) ? 0 : 1); + status = test_batch(argv[0], source, build) ? 0 : 1; + } + + /* For valgrind cleanliness. */ + if (source_env != NULL) { + putenv((char *) "SOURCE="); + free(source_env); + } + if (build_env != NULL) { + putenv((char *) "BUILD="); + free(build_env); } + exit(status); } diff --git a/tests/tap/basic.c b/tests/tap/basic.c index 829f91a..e8196fc 100644 --- a/tests/tap/basic.c +++ b/tests/tap/basic.c @@ -1,22 +1,38 @@ /* * Some utility routines for writing tests. * - * Herein are a variety of utility routines for writing tests. All routines - * of the form ok() or is*() take a test number and some number of appropriate - * arguments, check to be sure the results match the expected output using the - * arguments, and print out something appropriate for that test number. Other - * utility routines help in constructing more complex tests, skipping tests, - * or setting up the TAP output format. + * Here are a variety of utility routines for writing tests compatible with + * the TAP protocol. All routines of the form ok() or is*() take a test + * number and some number of appropriate arguments, check to be sure the + * results match the expected output using the arguments, and print out + * something appropriate for that test number. Other utility routines help in + * constructing more complex tests, skipping tests, reporting errors, setting + * up the TAP output format, or finding things in the test environment. * - * Copyright 2009, 2010 Russ Allbery - * Copyright 2006, 2007, 2008 - * Board of Trustees, Leland Stanford Jr. University - * Copyright (c) 2004, 2005, 2006 - * by Internet Systems Consortium, Inc. ("ISC") - * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, - * 2002, 2003 by The Internet Software Consortium and Rich Salz + * This file is part of C TAP Harness. The current version plus supporting + * documentation is at . * - * See LICENSE for licensing terms. + * Copyright 2009, 2010, 2011, 2012 Russ Allbery + * Copyright 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2011, 2012 + * The Board of Trustees of the Leland Stanford Junior University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. */ #include @@ -24,12 +40,21 @@ #include #include #include +#ifdef _WIN32 +# include +#else +# include +#endif #include -#include -#include #include -#include +#include + +/* Windows provides mkdir and rmdir under different names. */ +#ifdef _WIN32 +# define mkdir(p, m) _mkdir(p) +# define rmdir(p) _rmdir(p) +#endif /* * The test count. Always contains the number that will be used for the next @@ -57,7 +82,9 @@ static int _lazy = 0; /* * Our exit handler. Called on completion of the test to report a summary of - * results provided we're still in the original process. + * results provided we're still in the original process. This also handles + * printing out the plan if we used plan_lazy(), although that's suppressed if + * we never ran a test (due to an early bail, for example). */ static void finish(void) @@ -66,8 +93,9 @@ finish(void) if (_planned == 0 && !_lazy) return; + fflush(stderr); if (_process != 0 && getpid() == _process) { - if (_lazy) { + if (_lazy && highest > 0) { printf("1..%lu\n", highest); _planned = highest; } @@ -98,6 +126,7 @@ plan(unsigned long count) if (setvbuf(stdout, NULL, _IOLBF, BUFSIZ) != 0) fprintf(stderr, "# cannot set stdout to line buffered: %s\n", strerror(errno)); + fflush(stderr); printf("1..%lu\n", count); testnum = 1; _planned = count; @@ -130,6 +159,7 @@ plan_lazy(void) void skip_all(const char *format, ...) { + fflush(stderr); printf("1..0 # skip"); if (format != NULL) { va_list args; @@ -162,6 +192,7 @@ print_desc(const char *format, va_list args) void ok(int success, const char *format, ...) { + fflush(stderr); printf("%sok %lu", success ? "" : "not ", testnum++); if (!success) _failed++; @@ -182,6 +213,7 @@ ok(int success, const char *format, ...) void okv(int success, const char *format, va_list args) { + fflush(stderr); printf("%sok %lu", success ? "" : "not ", testnum++); if (!success) _failed++; @@ -197,6 +229,7 @@ okv(int success, const char *format, va_list args) void skip(const char *reason, ...) { + fflush(stderr); printf("ok %lu # skip", testnum++); if (reason != NULL) { va_list args; @@ -218,6 +251,7 @@ ok_block(unsigned long count, int status, const char *format, ...) { unsigned long i; + fflush(stderr); for (i = 0; i < count; i++) { printf("%sok %lu", status ? "" : "not ", testnum++); if (!status) @@ -242,6 +276,7 @@ skip_block(unsigned long count, const char *reason, ...) { unsigned long i; + fflush(stderr); for (i = 0; i < count; i++) { printf("ok %lu # skip", testnum++); if (reason != NULL) { @@ -264,6 +299,7 @@ skip_block(unsigned long count, const char *reason, ...) void is_int(long wanted, long seen, const char *format, ...) { + fflush(stderr); if (wanted == seen) printf("ok %lu", testnum++); else { @@ -293,6 +329,7 @@ is_string(const char *wanted, const char *seen, const char *format, ...) wanted = "(null)"; if (seen == NULL) seen = "(null)"; + fflush(stderr); if (strcmp(wanted, seen) == 0) printf("ok %lu", testnum++); else { @@ -311,31 +348,6 @@ is_string(const char *wanted, const char *seen, const char *format, ...) } -/* - * Takes an expected double and a seen double and assumes the test passes if - * those two numbers match. - */ -void -is_double(double wanted, double seen, const char *format, ...) -{ - if (wanted == seen) - printf("ok %lu", testnum++); - else { - printf("# wanted: %g\n# seen: %g\n", wanted, seen); - printf("not ok %lu", testnum++); - _failed++; - } - if (format != NULL) { - va_list args; - - va_start(args, format); - print_desc(format, args); - va_end(args); - } - putchar('\n'); -} - - /* * Takes an expected unsigned long and a seen unsigned long and assumes the * test passes if the two numbers match. Otherwise, reports them in hex. @@ -343,6 +355,7 @@ is_double(double wanted, double seen, const char *format, ...) void is_hex(unsigned long wanted, unsigned long seen, const char *format, ...) { + fflush(stderr); if (wanted == seen) printf("ok %lu", testnum++); else { @@ -370,6 +383,7 @@ bail(const char *format, ...) { va_list args; + fflush(stderr); fflush(stdout); printf("Bail out! "); va_start(args, format); @@ -389,6 +403,7 @@ sysbail(const char *format, ...) va_list args; int oerrno = errno; + fflush(stderr); fflush(stdout); printf("Bail out! "); va_start(args, format); @@ -407,6 +422,7 @@ diag(const char *format, ...) { va_list args; + fflush(stderr); fflush(stdout); printf("# "); va_start(args, format); @@ -425,6 +441,7 @@ sysdiag(const char *format, ...) va_list args; int oerrno = errno; + fflush(stderr); fflush(stdout); printf("# "); va_start(args, format); @@ -434,6 +451,92 @@ sysdiag(const char *format, ...) } +/* + * Allocate cleared memory, reporting a fatal error with bail on failure. + */ +void * +bcalloc(size_t n, size_t size) +{ + void *p; + + p = calloc(n, size); + if (p == NULL) + sysbail("failed to calloc %lu", (unsigned long)(n * size)); + return p; +} + + +/* + * Allocate memory, reporting a fatal error with bail on failure. + */ +void * +bmalloc(size_t size) +{ + void *p; + + p = malloc(size); + if (p == NULL) + sysbail("failed to malloc %lu", (unsigned long) size); + return p; +} + + +/* + * Reallocate memory, reporting a fatal error with bail on failure. + */ +void * +brealloc(void *p, size_t size) +{ + p = realloc(p, size); + if (p == NULL) + sysbail("failed to realloc %lu bytes", (unsigned long) size); + return p; +} + + +/* + * Copy a string, reporting a fatal error with bail on failure. + */ +char * +bstrdup(const char *s) +{ + char *p; + size_t len; + + len = strlen(s) + 1; + p = malloc(len); + if (p == NULL) + sysbail("failed to strdup %lu bytes", (unsigned long) len); + memcpy(p, s, len); + return p; +} + + +/* + * Copy up to n characters of a string, reporting a fatal error with bail on + * failure. Don't use the system strndup function, since it may not exist and + * the TAP library doesn't assume any portability support. + */ +char * +bstrndup(const char *s, size_t n) +{ + const char *p; + char *copy; + size_t length; + + /* Don't assume that the source string is nul-terminated. */ + for (p = s; (size_t) (p - s) < n && *p != '\0'; p++) + ; + length = p - s; + copy = malloc(length + 1); + if (p == NULL) + sysbail("failed to strndup %lu bytes", (unsigned long) length); + memcpy(copy, s, length); + copy[length] = '\0'; + return copy; +} + + /* * Locate a test file. Given the partial path to a file, look under BUILD and * then SOURCE for the file and return the full path to the file. Returns @@ -458,9 +561,7 @@ test_file_path(const char *file) if (base == NULL) continue; length = strlen(base) + 1 + strlen(file) + 1; - path = malloc(length); - if (path == NULL) - sysbail("cannot allocate memory"); + path = bmalloc(length); sprintf(path, "%s/%s", base, file); if (access(path, R_OK) == 0) break; @@ -482,3 +583,47 @@ test_file_path_free(char *path) if (path != NULL) free(path); } + + +/* + * Create a temporary directory, tmp, under BUILD if set and the current + * directory if it does not. Returns the path to the temporary directory in + * newly allocated memory, and calls bail on any failure. The return value + * should be freed with test_tmpdir_free. + * + * This function uses sprintf because it attempts to be independent of all + * other portability layers. The use immediately after a memory allocation + * should be safe without using snprintf or strlcpy/strlcat. + */ +char * +test_tmpdir(void) +{ + const char *build; + char *path = NULL; + size_t length; + + build = getenv("BUILD"); + if (build == NULL) + build = "."; + length = strlen(build) + strlen("/tmp") + 1; + path = bmalloc(length); + sprintf(path, "%s/tmp", build); + if (access(path, X_OK) < 0) + if (mkdir(path, 0777) < 0) + sysbail("error creating temporary directory %s", path); + return path; +} + + +/* + * Free a path returned from test_tmpdir() and attempt to remove the + * directory. If we can't delete the directory, don't worry; something else + * that hasn't yet cleaned up may still be using it. + */ +void +test_tmpdir_free(char *path) +{ + rmdir(path); + if (path != NULL) + free(path); +} diff --git a/tests/tap/basic.h b/tests/tap/basic.h index 9602db4..fa4adaf 100644 --- a/tests/tap/basic.h +++ b/tests/tap/basic.h @@ -1,47 +1,38 @@ /* * Basic utility routines for the TAP protocol. * - * Copyright 2009, 2010 Russ Allbery - * Copyright 2006, 2007, 2008 - * Board of Trustees, Leland Stanford Jr. University - * Copyright (c) 2004, 2005, 2006 - * by Internet Systems Consortium, Inc. ("ISC") - * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, - * 2002, 2003 by The Internet Software Consortium and Rich Salz + * This file is part of C TAP Harness. The current version plus supporting + * documentation is at . * - * See LICENSE for licensing terms. + * Copyright 2009, 2010, 2011, 2012 Russ Allbery + * Copyright 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2011, 2012 + * The Board of Trustees of the Leland Stanford Junior University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. */ #ifndef TAP_BASIC_H #define TAP_BASIC_H 1 +#include #include /* va_list */ -#include /* pid_t */ - -/* - * __attribute__ is available in gcc 2.5 and later, but only with gcc 2.7 - * could you use the __format__ form of the attributes, which is what we use - * (to avoid confusion with other macros). - */ -#ifndef __attribute__ -# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) -# define __attribute__(spec) /* empty */ -# endif -#endif - -/* - * BEGIN_DECLS is used at the beginning of declarations so that C++ - * compilers don't mangle their names. END_DECLS is used at the end. - */ -#undef BEGIN_DECLS -#undef END_DECLS -#ifdef __cplusplus -# define BEGIN_DECLS extern "C" { -# define END_DECLS } -#else -# define BEGIN_DECLS /* empty */ -# define END_DECLS /* empty */ -#endif +#include /* size_t */ /* * Used for iterating through arrays. ARRAY_SIZE returns the number of @@ -93,8 +84,6 @@ void skip_block(unsigned long count, const char *reason, ...) /* Check an expected value against a seen value. */ void is_int(long wanted, long seen, const char *format, ...) __attribute__((__format__(printf, 3, 4))); -void is_double(double wanted, double seen, const char *format, ...) - __attribute__((__format__(printf, 3, 4))); void is_string(const char *wanted, const char *seen, const char *format, ...) __attribute__((__format__(printf, 3, 4))); void is_hex(unsigned long wanted, unsigned long seen, const char *format, ...) @@ -112,6 +101,18 @@ void diag(const char *format, ...) void sysdiag(const char *format, ...) __attribute__((__nonnull__, __format__(printf, 1, 2))); +/* Allocate memory, reporting a fatal error with bail on failure. */ +void *bcalloc(size_t, size_t) + __attribute__((__alloc_size__(1, 2), __malloc__)); +void *bmalloc(size_t) + __attribute__((__alloc_size__(1), __malloc__)); +void *brealloc(void *, size_t) + __attribute__((__alloc_size__(2), __malloc__)); +char *bstrdup(const char *) + __attribute__((__malloc__, __nonnull__)); +char *bstrndup(const char *, size_t) + __attribute__((__malloc__, __nonnull__)); + /* * Find a test file under BUILD or SOURCE, returning the full path. The * returned path should be freed with test_file_path_free(). @@ -120,6 +121,14 @@ char *test_file_path(const char *file) __attribute__((__malloc__, __nonnull__)); void test_file_path_free(char *path); +/* + * Create a temporary directory relative to BUILD and return the path. The + * returned path should be freed with test_tmpdir_free. + */ +char *test_tmpdir(void) + __attribute__((__malloc__)); +void test_tmpdir_free(char *path); + END_DECLS #endif /* TAP_BASIC_H */ diff --git a/tests/tap/kerberos.c b/tests/tap/kerberos.c index a17d980..474cf4f 100644 --- a/tests/tap/kerberos.c +++ b/tests/tap/kerberos.c @@ -1,47 +1,90 @@ /* * Utility functions for tests that use Kerberos. * - * Currently only provides kerberos_setup(), which assumes a particular set of - * data files in either the SOURCE or BUILD directories and, using those, - * obtains Kerberos credentials, sets up a ticket cache, and sets the - * environment variable pointing to the Kerberos keytab to use for testing. + * The core function is kerberos_setup, which loads Kerberos test + * configuration and returns a struct of information. It also supports + * obtaining initial tickets from the configured keytab and setting up + * KRB5CCNAME and KRB5_KTNAME if a Kerberos keytab is present. Also included + * are utility functions for setting up a krb5.conf file and reporting + * Kerberos errors or warnings during testing. * - * Copyright 2006, 2007, 2009, 2010 - * Board of Trustees, Leland Stanford Jr. University + * Some of the functionality here is only available if the Kerberos libraries + * are available. * - * See LICENSE for licensing terms. + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * + * Written by Russ Allbery + * Copyright 2006, 2007, 2009, 2010, 2011, 2012 + * The Board of Trustees of the Leland Stanford Junior University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. */ #include -#include +#ifdef HAVE_KERBEROS +# include +#endif #include +#include + #include #include -#include -#include +#include +#include + +/* + * Disable the requirement that format strings be literals, since it's easier + * to handle the possible patterns for kinit commands as an array. + */ +#pragma GCC diagnostic ignored "-Wformat-nonliteral" /* - * Obtain Kerberos tickets for the principal specified in test.principal using - * the keytab specified in test.keytab, both of which are presumed to be in - * tests/data in either the build or the source tree. - * - * Returns the contents of test.principal in newly allocated memory or NULL if - * Kerberos tests are apparently not configured. If Kerberos tests are - * configured but something else fails, calls bail(). + * These variables hold the allocated configuration struct, the environment to + * point to a different Kerberos ticket cache, keytab, and configuration file, + * and the temporary directories used. We store them so that we can free them + * on exit for cleaner valgrind output, making it easier to find real memory + * leaks in the tested programs. + */ +static struct kerberos_config *config = NULL; +static char *krb5ccname = NULL; +static char *krb5_ktname = NULL; +static char *krb5_config = NULL; +static char *tmpdir_ticket = NULL; +static char *tmpdir_conf = NULL; + + +/* + * Obtain Kerberos tickets and fill in the principal config entry. * - * The error handling here is not great. We should have a bail_krb5 that uses - * the same logic as messages-krb5.c, which hasn't yet been imported into - * rra-c-util. + * There are two implementations of this function, one if we have native + * Kerberos libraries available and one if we don't. Uses keytab to obtain + * credentials, and fills in the cache member of the provided config struct. */ -char * -kerberos_setup(void) +#ifdef HAVE_KERBEROS + +static void +kerberos_kinit(void) { - char *path, *krbtgt; - const char *build, *realm; - FILE *file; - char principal[BUFSIZ]; + char *name, *krbtgt; krb5_error_code code; krb5_context ctx; krb5_ccache ccache; @@ -49,89 +92,397 @@ kerberos_setup(void) krb5_keytab keytab; krb5_get_init_creds_opt *opts; krb5_creds creds; + const char *realm; - /* Read the principal name and find the keytab file. */ - path = test_file_path("data/test.principal"); - if (path == NULL) - return NULL; - file = fopen(path, "r"); - if (file == NULL) { - free(path); - return NULL; - } - if (fgets(principal, sizeof(principal), file) == NULL) { - fclose(file); - bail("cannot read %s", path); - } - fclose(file); - if (principal[strlen(principal) - 1] != '\n') - bail("no newline in %s", path); - free(path); - principal[strlen(principal) - 1] = '\0'; - path = test_file_path("data/test.keytab"); - if (path == NULL) - return NULL; - - /* Set the KRB5CCNAME and KRB5_KTNAME environment variables. */ - build = getenv("BUILD"); - if (build == NULL) - build = "."; - putenv(concat("KRB5CCNAME=", build, "/data/test.cache", (char *) 0)); - putenv(concat("KRB5_KTNAME=", path, (char *) 0)); - - /* Now do the Kerberos initialization. */ + /* + * Determine the principal corresponding to that keytab. We copy the + * memory to ensure that it's allocated in the right memory domain on + * systems where that may matter (like Windows). + */ code = krb5_init_context(&ctx); if (code != 0) - bail("error initializing Kerberos"); + bail_krb5(ctx, code, "error initializing Kerberos"); + kprinc = kerberos_keytab_principal(ctx, config->keytab); + code = krb5_unparse_name(ctx, kprinc, &name); + if (code != 0) + bail_krb5(ctx, code, "error unparsing name"); + krb5_free_principal(ctx, kprinc); + config->principal = bstrdup(name); + krb5_free_unparsed_name(ctx, name); + + /* Now do the Kerberos initialization. */ code = krb5_cc_default(ctx, &ccache); if (code != 0) - bail("error setting ticket cache"); - code = krb5_parse_name(ctx, principal, &kprinc); + bail_krb5(ctx, code, "error setting ticket cache"); + code = krb5_parse_name(ctx, config->principal, &kprinc); if (code != 0) - bail("error parsing principal %s", principal); + bail_krb5(ctx, code, "error parsing principal %s", config->principal); realm = krb5_principal_get_realm(ctx, kprinc); - krbtgt = concat("krbtgt/", realm, "@", realm, (char *) 0); - code = krb5_kt_resolve(ctx, path, &keytab); + basprintf(&krbtgt, "krbtgt/%s@%s", realm, realm); + code = krb5_kt_resolve(ctx, config->keytab, &keytab); if (code != 0) - bail("cannot open keytab %s", path); + bail_krb5(ctx, code, "cannot open keytab %s", config->keytab); code = krb5_get_init_creds_opt_alloc(ctx, &opts); if (code != 0) - bail("cannot allocate credential options"); + bail_krb5(ctx, code, "cannot allocate credential options"); krb5_get_init_creds_opt_set_default_flags(ctx, NULL, realm, opts); krb5_get_init_creds_opt_set_forwardable(opts, 0); krb5_get_init_creds_opt_set_proxiable(opts, 0); code = krb5_get_init_creds_keytab(ctx, &creds, kprinc, keytab, 0, krbtgt, opts); if (code != 0) - bail("cannot get Kerberos tickets"); + bail_krb5(ctx, code, "cannot get Kerberos tickets"); code = krb5_cc_initialize(ctx, ccache, kprinc); if (code != 0) - bail("error initializing ticket cache"); + bail_krb5(ctx, code, "error initializing ticket cache"); code = krb5_cc_store_cred(ctx, ccache, &creds); if (code != 0) - bail("error storing credentials"); + bail_krb5(ctx, code, "error storing credentials"); krb5_cc_close(ctx, ccache); krb5_free_cred_contents(ctx, &creds); krb5_kt_close(ctx, keytab); krb5_free_principal(ctx, kprinc); + krb5_get_init_creds_opt_free(ctx, opts); krb5_free_context(ctx); free(krbtgt); - free(path); +} - return xstrdup(principal); +#else /* !HAVE_KERBEROS */ + +static void +kerberos_kinit(void) +{ + static const char * const format[] = { + "kinit --no-afslog -k -t %s %s >/dev/null 2>&1 /dev/null 2>&1 /dev/null 2>&1 /dev/null 2>&1 keytab); + config->keytab = NULL; + return; + } + file = fopen(path, "r"); + if (file == NULL) { + test_file_path_free(path); + return; + } + test_file_path_free(path); + if (fgets(principal, sizeof(principal), file) == NULL) + bail("cannot read %s", path); + fclose(file); + if (principal[strlen(principal) - 1] != '\n') + bail("no newline in %s", path); + principal[strlen(principal) - 1] = '\0'; + config->principal = bstrdup(principal); + + /* Now do the Kerberos initialization. */ + for (i = 0; i < ARRAY_SIZE(format); i++) { + basprintf(&command, format[i], config->keytab, principal); + status = system(command); + free(command); + if (status != -1 && WEXITSTATUS(status) == 0) + break; + } + if (status == -1 || WEXITSTATUS(status) != 0) + bail("cannot get Kerberos tickets"); } +#endif /* !HAVE_KERBEROS */ + /* - * Clean up at the end of a test. Currently, all this does is remove the - * ticket cache. + * Clean up at the end of a test. This removes the ticket cache and resets + * and frees the memory allocated for the environment variables so that + * valgrind output on test suites is cleaner. */ void kerberos_cleanup(void) { char *path; - path = concatpath(getenv("BUILD"), "data/test.cache"); - unlink(path); - free(path); + if (tmpdir_ticket != NULL) { + basprintf(&path, "%s/krb5cc_test", tmpdir_ticket); + unlink(path); + free(path); + test_tmpdir_free(tmpdir_ticket); + tmpdir_ticket = NULL; + } + if (config != NULL) { + if (config->keytab != NULL) { + test_file_path_free(config->keytab); + free(config->principal); + free(config->cache); + } + if (config->userprinc != NULL) { + free(config->userprinc); + free(config->username); + free(config->password); + } + free(config); + config = NULL; + } + if (krb5ccname != NULL) { + putenv((char *) "KRB5CCNAME="); + free(krb5ccname); + krb5ccname = NULL; + } + if (krb5_ktname != NULL) { + putenv((char *) "KRB5_KTNAME="); + free(krb5_ktname); + krb5_ktname = NULL; + } +} + + +/* + * Obtain Kerberos tickets for the principal specified in config/principal + * using the keytab specified in config/keytab, both of which are presumed to + * be in tests in either the build or the source tree. Also sets KRB5_KTNAME + * and KRB5CCNAME. + * + * Returns the contents of config/principal in newly allocated memory or NULL + * if Kerberos tests are apparently not configured. If Kerberos tests are + * configured but something else fails, calls bail. + */ +struct kerberos_config * +kerberos_setup(enum kerberos_needs needs) +{ + char *path; + char buffer[BUFSIZ]; + FILE *file = NULL; + + /* If we were called before, clean up after the previous run. */ + if (config != NULL) + kerberos_cleanup(); + config = bcalloc(1, sizeof(struct kerberos_config)); + + /* + * If we have a config/keytab file, set the KRB5CCNAME and KRB5_KTNAME + * environment variables and obtain initial tickets. + */ + config->keytab = test_file_path("config/keytab"); + if (config->keytab == NULL) { + if (needs == TAP_KRB_NEEDS_KEYTAB || needs == TAP_KRB_NEEDS_BOTH) + skip_all("Kerberos tests not configured"); + } else { + tmpdir_ticket = test_tmpdir(); + basprintf(&config->cache, "%s/krb5cc_test", tmpdir_ticket); + basprintf(&krb5ccname, "KRB5CCNAME=%s/krb5cc_test", tmpdir_ticket); + basprintf(&krb5_ktname, "KRB5_KTNAME=%s", config->keytab); + putenv(krb5ccname); + putenv(krb5_ktname); + kerberos_kinit(); + } + + /* + * If we have a config/password file, read it and fill out the relevant + * members of our config struct. + */ + path = test_file_path("config/password"); + if (path != NULL) + file = fopen(path, "r"); + if (file == NULL) { + if (needs == TAP_KRB_NEEDS_PASSWORD || needs == TAP_KRB_NEEDS_BOTH) + skip_all("Kerberos tests not configured"); + } else { + if (fgets(buffer, sizeof(buffer), file) == NULL) + bail("cannot read %s", path); + if (buffer[strlen(buffer) - 1] != '\n') + bail("no newline in %s", path); + buffer[strlen(buffer) - 1] = '\0'; + config->userprinc = bstrdup(buffer); + if (fgets(buffer, sizeof(buffer), file) == NULL) + bail("cannot read password from %s", path); + fclose(file); + if (buffer[strlen(buffer) - 1] != '\n') + bail("password too long in %s", path); + buffer[strlen(buffer) - 1] = '\0'; + config->password = bstrdup(buffer); + + /* + * Strip the realm from the principal and set realm and username. + * This is not strictly correct; it doesn't cope with escaped @-signs + * or enterprise names. + */ + config->username = bstrdup(config->userprinc); + config->realm = strchr(config->username, '@'); + if (config->realm == NULL) + bail("test principal has no realm"); + *config->realm = '\0'; + config->realm++; + } + if (path != NULL) + test_file_path_free(path); + + /* + * Register the cleanup function as an atexit handler so that the caller + * doesn't have to worry about cleanup. + */ + if (atexit(kerberos_cleanup) != 0) + sysdiag("cannot register cleanup function"); + + /* Return the configuration. */ + return config; +} + + +/* + * Clean up the krb5.conf file generated by kerberos_generate_conf and free + * the memory used to set the environment variable. This doesn't fail if the + * file and variable are already gone, allowing it to be harmlessly run + * multiple times. + * + * Normally called via an atexit handler. + */ +void +kerberos_cleanup_conf(void) +{ + char *path; + + if (tmpdir_conf != NULL) { + basprintf(&path, "%s/krb5.conf", tmpdir_conf); + unlink(path); + free(path); + test_tmpdir_free(tmpdir_conf); + tmpdir_conf = NULL; + } + putenv((char *) "KRB5_CONFIG="); + if (krb5_config != NULL) { + free(krb5_config); + krb5_config = NULL; + } } + + +/* + * Generate a krb5.conf file for testing and set KRB5_CONFIG to point to it. + * The [appdefaults] section will be stripped out and the default realm will + * be set to the realm specified, if not NULL. This will use config/krb5.conf + * in preference, so users can configure the tests by creating that file if + * the system file isn't suitable. + * + * Depends on data/generate-krb5-conf being present in the test suite. + */ +void +kerberos_generate_conf(const char *realm) +{ + char *path; + const char *argv[3]; + + if (tmpdir_conf != NULL) + kerberos_cleanup_conf(); + path = test_file_path("data/generate-krb5-conf"); + if (path == NULL) + bail("cannot find generate-krb5-conf"); + argv[0] = path; + argv[1] = realm; + argv[2] = NULL; + run_setup(argv); + test_file_path_free(path); + tmpdir_conf = test_tmpdir(); + basprintf(&krb5_config, "KRB5_CONFIG=%s/krb5.conf", tmpdir_conf); + putenv(krb5_config); + if (atexit(kerberos_cleanup_conf) != 0) + sysdiag("cannot register cleanup function"); +} + + +/* + * The remaining functions in this file are only available if Kerberos + * libraries are available. + */ +#ifdef HAVE_KERBEROS + + +/* + * Report a Kerberos error and bail out. + */ +void +bail_krb5(krb5_context ctx, krb5_error_code code, const char *format, ...) +{ + const char *k5_msg = NULL; + char *message; + va_list args; + + if (ctx != NULL) + k5_msg = krb5_get_error_message(ctx, code); + va_start(args, format); + bvasprintf(&message, format, args); + va_end(args); + if (k5_msg == NULL) + bail("%s", message); + else + bail("%s: %s", message, k5_msg); +} + + +/* + * Report a Kerberos error as a diagnostic to stderr. + */ +void +diag_krb5(krb5_context ctx, krb5_error_code code, const char *format, ...) +{ + const char *k5_msg = NULL; + char *message; + va_list args; + + if (ctx != NULL) + k5_msg = krb5_get_error_message(ctx, code); + va_start(args, format); + bvasprintf(&message, format, args); + va_end(args); + if (k5_msg == NULL) + diag("%s", message); + else + diag("%s: %s", message, k5_msg); + free(message); + if (k5_msg != NULL) + krb5_free_error_message(ctx, k5_msg); +} + + +/* + * Find the principal of the first entry of a keytab and return it. The + * caller is responsible for freeing the result with krb5_free_principal. + * Exit on error. + */ +krb5_principal +kerberos_keytab_principal(krb5_context ctx, const char *path) +{ + krb5_keytab keytab; + krb5_kt_cursor cursor; + krb5_keytab_entry entry; + krb5_principal princ; + krb5_error_code status; + + status = krb5_kt_resolve(ctx, path, &keytab); + if (status != 0) + bail_krb5(ctx, status, "error opening %s", path); + status = krb5_kt_start_seq_get(ctx, keytab, &cursor); + if (status != 0) + bail_krb5(ctx, status, "error reading %s", path); + status = krb5_kt_next_entry(ctx, keytab, &entry, &cursor); + if (status == 0) { + status = krb5_copy_principal(ctx, entry.principal, &princ); + if (status != 0) + bail_krb5(ctx, status, "error copying principal from %s", path); + krb5_kt_free_entry(ctx, &entry); + } + if (status != 0) + bail("no principal found in keytab file %s", path); + krb5_kt_end_seq_get(ctx, keytab, &cursor); + krb5_kt_close(ctx, keytab); + return princ; +} + +#endif /* HAVE_KERBEROS */ diff --git a/tests/tap/kerberos.h b/tests/tap/kerberos.h index 1c64f70..31b6343 100644 --- a/tests/tap/kerberos.h +++ b/tests/tap/kerberos.h @@ -1,32 +1,125 @@ /* * Utility functions for tests that use Kerberos. * - * Copyright 2006, 2007, 2009 - * Board of Trustees, Leland Stanford Jr. University + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . * - * See LICENSE for licensing terms. + * Written by Russ Allbery + * Copyright 2006, 2007, 2009, 2011, 2012 + * The Board of Trustees of the Leland Stanford Junior University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. */ #ifndef TAP_KERBEROS_H #define TAP_KERBEROS_H 1 #include -#include +#include + +#ifdef HAVE_KERBEROS +# include +#endif + +/* Holds the information parsed from the Kerberos test configuration. */ +struct kerberos_config { + char *keytab; /* Path to the keytab. */ + char *principal; /* Principal whose keys are in the keytab. */ + char *cache; /* Path to the Kerberos ticket cache. */ + char *userprinc; /* The fully-qualified principal. */ + char *username; /* The local (non-realm) part of principal. */ + char *realm; /* The realm part of the principal. */ + char *password; /* The password. */ +}; + +/* + * Whether to skip all tests (by calling skip_all) in kerberos_setup if + * certain configuration information isn't available. + */ +enum kerberos_needs { + TAP_KRB_NEEDS_NONE, + TAP_KRB_NEEDS_KEYTAB, + TAP_KRB_NEEDS_PASSWORD, + TAP_KRB_NEEDS_BOTH +}; BEGIN_DECLS /* - * Set up Kerberos, returning the test principal in newly allocated memory if - * we were successful. If there is no principal in tests/data/test.principal - * or no keytab in tests/data/test.keytab, return NULL. Otherwise, on - * failure, calls bail(). + * Set up Kerberos, returning the test configuration information. This + * obtains Kerberos tickets from config/keytab, if one is present, and stores + * them in a Kerberos ticket cache, sets KRB5_KTNAME and KRB5CCNAME. It also + * loads the principal and password from config/password, if it exists, and + * stores the principal, password, username, and realm in the returned struct. + * + * If there is no config/keytab file, KRB5_KTNAME and KRB5CCNAME won't be set + * and the keytab field will be NULL. If there is no config/password file, + * the principal field will be NULL. If the files exist but loading them + * fails, or authentication fails, kerberos_setup calls bail. + * + * kerberos_cleanup will be set up to run from an atexit handler. This means + * that any child processes that should not remove the Kerberos ticket cache + * should call _exit instead of exit. The principal will be automatically + * freed when kerberos_cleanup is called or if kerberos_setup is called again. + * The caller doesn't need to worry about it. */ -char *kerberos_setup(void) +struct kerberos_config *kerberos_setup(enum kerberos_needs) __attribute__((__malloc__)); - -/* Clean up at the end of a test. */ void kerberos_cleanup(void); +/* + * Generate a krb5.conf file for testing and set KRB5_CONFIG to point to it. + * The [appdefaults] section will be stripped out and the default realm will + * be set to the realm specified, if not NULL. This will use config/krb5.conf + * in preference, so users can configure the tests by creating that file if + * the system file isn't suitable. + * + * Depends on data/generate-krb5-conf being present in the test suite. + * + * kerberos_cleanup_conf will clean up after this function, but usually + * doesn't need to be called directly since it's registered as an atexit + * handler. + */ +void kerberos_generate_conf(const char *realm); +void kerberos_cleanup_conf(void); + +/* Thes interfaces are only available with native Kerberos support. */ +#ifdef HAVE_KERBEROS + +/* Bail out with an error, appending the Kerberos error message. */ +void bail_krb5(krb5_context, krb5_error_code, const char *format, ...) + __attribute__((__noreturn__, __nonnull__, __format__(printf, 3, 4))); + +/* Report a diagnostic with Kerberos error to stderr prefixed with #. */ +void diag_krb5(krb5_context, krb5_error_code, const char *format, ...) + __attribute__((__nonnull__, __format__(printf, 3, 4))); + +/* + * Given a Kerberos context and the path to a keytab, retrieve the principal + * for the first entry in the keytab and return it. Calls bail on failure. + * The returned principal should be freed with krb5_free_principal. + */ +krb5_principal kerberos_keytab_principal(krb5_context, const char *path) + __attribute__((__nonnull__)); + +#endif /* HAVE_KERBEROS */ + END_DECLS #endif /* !TAP_MESSAGES_H */ diff --git a/tests/tap/kerberos.sh b/tests/tap/kerberos.sh index 904cae5..d2f174d 100644 --- a/tests/tap/kerberos.sh +++ b/tests/tap/kerberos.sh @@ -1,30 +1,61 @@ # Shell function library to initialize Kerberos credentials # +# Note that while many of the functions in this library could benefit from +# using "local" to avoid possibly hammering global variables, Solaris /bin/sh +# doesn't support local and this library aspires to be portable to Solaris +# Bourne shell. Instead, all private variables are prefixed with "tap_". +# +# The canonical version of this file is maintained in the rra-c-util package, +# which can be found at . +# # Written by Russ Allbery -# Copyright 2009, 2010 Board of Trustees, Leland Stanford Jr. University +# Copyright 2009, 2010, 2011, 2012 +# The Board of Trustees of the Leland Stanford Junior University +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. # -# See LICENSE for licensing terms. +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +# We use test_tmpdir. +. "${SOURCE}/tap/libtap.sh" # Set up Kerberos, including the ticket cache environment variable. Bail out # if not successful, return 0 if successful, and return 1 if Kerberos is not # configured. Sets the global principal variable to the principal to use. kerberos_setup () { - local keytab - keytab=`test_file_path data/test.keytab` - principal=`test_file_path data/test.principal` + tap_keytab=`test_file_path config/keytab` + principal=`test_file_path config/principal` principal=`cat "$principal" 2>/dev/null` - if [ -z "$keytab" ] || [ -z "$principal" ] ; then + if [ -z "$tap_keytab" ] || [ -z "$principal" ] ; then return 1 fi - KRB5CCNAME="$BUILD/data/test.cache"; export KRB5CCNAME - kinit -k -t "$keytab" "$principal" >/dev/null /dev/null /dev/null /dev/null /dev/null /dev/null /dev/null ktutil-tmp 2>/dev/null ; then + tap_tmp=`test_tmpdir` + if klist -keK "$1" > "$tap_tmp"/ktutil-tmp 2>/dev/null ; then : else - ktutil -k "$1" list --keys > ktutil-tmp < /dev/null 2>/dev/null + ktutil -k "$1" list --keys > "$tap_tmp"/ktutil-tmp /dev/null fi - sed -e '/Keytab name:/d' -e "/^[^ ]*:/d" ktutil-tmp > "$2" - rm -f ktutil-tmp + sed -e '/Keytab name:/d' -e "/^[^ ]*:/d" "$tap_tmp"/ktutil-tmp > "$2" + rm -f "$tap_tmp"/ktutil-tmp } diff --git a/tests/tap/libtap.sh b/tests/tap/libtap.sh index a9b46d4..f9347d8 100644 --- a/tests/tap/libtap.sh +++ b/tests/tap/libtap.sh @@ -1,10 +1,36 @@ # Shell function library for test cases. # +# Note that while many of the functions in this library could benefit from +# using "local" to avoid possibly hammering global variables, Solaris /bin/sh +# doesn't support local and this library aspires to be portable to Solaris +# Bourne shell. Instead, all private variables are prefixed with "tap_". +# +# This file provides a TAP-compatible shell function library useful for +# writing test cases. It is part of C TAP Harness, which can be found at +# . +# # Written by Russ Allbery -# Copyright 2009, 2010 Russ Allbery -# Copyright 2006, 2007, 2008 Board of Trustees, Leland Stanford Jr. University +# Copyright 2009, 2010, 2011, 2012 Russ Allbery +# Copyright 2006, 2007, 2008 +# The Board of Trustees of the Leland Stanford Junior University +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: # -# See LICENSE for licensing terms. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. # Print out the number of test cases we expect to run. plan () { @@ -25,33 +51,35 @@ plan_lazy () { # Report the test status on exit. finish () { - local highest looks - highest=`expr "$count" - 1` + tap_highest=`expr "$count" - 1` if [ "$planned" = 0 ] ; then - echo "1..$highest" - planned="$highest" + echo "1..$tap_highest" + planned="$tap_highest" fi - looks='# Looks like you' + tap_looks='# Looks like you' if [ "$planned" -gt 0 ] ; then - if [ "$planned" -gt "$highest" ] ; then + if [ "$planned" -gt "$tap_highest" ] ; then if [ "$planned" -gt 1 ] ; then - echo "$looks planned $planned tests but only ran $highest" + echo "$tap_looks planned $planned tests but only ran" \ + "$tap_highest" else - echo "$looks planned $planned test but only ran $highest" + echo "$tap_looks planned $planned test but only ran" \ + "$tap_highest" fi - elif [ "$planned" -lt "$highest" ] ; then - local extra - extra=`expr "$highest" - "$planned"` + elif [ "$planned" -lt "$tap_highest" ] ; then + tap_extra=`expr "$tap_highest" - "$planned"` if [ "$planned" -gt 1 ] ; then - echo "$looks planned $planned tests but ran $extra extra" + echo "$tap_looks planned $planned tests but ran" \ + "$tap_extra extra" else - echo "$looks planned $planned test but ran $extra extra" + echo "$tap_looks planned $planned test but ran" \ + "$tap_extra extra" fi elif [ "$failed" -gt 0 ] ; then if [ "$failed" -gt 1 ] ; then - echo "$looks failed $failed tests of $planned" + echo "$tap_looks failed $failed tests of $planned" else - echo "$looks failed $failed test of $planned" + echo "$tap_looks failed $failed test of $planned" fi elif [ "$planned" -gt 1 ] ; then echo "# All $planned tests successful or skipped" @@ -63,10 +91,9 @@ finish () { # Skip the entire test suite. Should be run instead of plan. skip_all () { - local desc - desc="$1" - if [ -n "$desc" ] ; then - echo "1..0 # skip $desc" + tap_desc="$1" + if [ -n "$tap_desc" ] ; then + echo "1..0 # skip $tap_desc" else echo "1..0 # skip" fi @@ -77,16 +104,15 @@ skip_all () { # command is successful, false otherwise. The count starts at 1 and is # updated each time ok is printed. ok () { - local desc - desc="$1" - if [ -n "$desc" ] ; then - desc=" - $desc" + tap_desc="$1" + if [ -n "$tap_desc" ] ; then + tap_desc=" - $tap_desc" fi shift if "$@" ; then - echo ok $count$desc + echo ok "$count$tap_desc" else - echo not ok $count$desc + echo not ok "$count$tap_desc" failed=`expr $failed + 1` fi count=`expr $count + 1` @@ -101,58 +127,80 @@ skip () { # Report the same status on a whole set of tests. Takes the count of tests, # the description, and then the command to run to determine the status. ok_block () { - local end i desc - i=$count - end=`expr $count + $1` - shift - desc="$1" + tap_i=$count + tap_end=`expr $count + $1` shift - while [ "$i" -lt "$end" ] ; do - ok "$desc" "$@" - i=`expr $i + 1` + while [ "$tap_i" -lt "$tap_end" ] ; do + ok "$@" + tap_i=`expr $tap_i + 1` done } # Skip a whole set of tests. Takes the count and then the reason for skipping # the test. skip_block () { - local i end - i=$count - end=`expr $count + $1` + tap_i=$count + tap_end=`expr $count + $1` shift - while [ "$i" -lt "$end" ] ; do + while [ "$tap_i" -lt "$tap_end" ] ; do skip "$@" - i=`expr $i + 1` + tap_i=`expr $tap_i + 1` done } +# Portable variant of printf '%s\n' "$*". In the majority of cases, this +# function is slower than printf, because the latter is often implemented +# as a builtin command. The value of the variable IFS is ignored. +# +# This macro must not be called via backticks inside double quotes, since this +# will result in bizarre escaping behavior and lots of extra backslashes on +# Solaris. +puts () { + cat << EOH +$@ +EOH +} + # Run a program expected to succeed, and print ok if it does and produces the # correct output. Takes the description, expected exit status, the expected -# output, the command to run, and then any arguments for that command. Strip -# a colon and everything after it off the output if the expected status is -# non-zero, since this is probably a system-specific error message. +# output, the command to run, and then any arguments for that command. +# Standard output and standard error are combined when analyzing the output of +# the command. +# +# If the command may contain system-specific error messages in its output, +# add strip_colon_error before the command to post-process its output. ok_program () { - local desc w_status w_output output status - desc="$1" + tap_desc="$1" shift - w_status="$1" + tap_w_status="$1" shift - w_output="$1" + tap_w_output="$1" shift - output=`"$@" 2>&1` - status=$? - if [ "$w_status" -ne 0 ] ; then - output=`echo "$output" | sed 's/^\([^:]* [^:]*\):.*/\1/'` - fi - if [ $status = $w_status ] && [ x"$output" = x"$w_output" ] ; then - ok "$desc" true + tap_output=`"$@" 2>&1` + tap_status=$? + if [ $tap_status = $tap_w_status ] \ + && [ x"$tap_output" = x"$tap_w_output" ] ; then + ok "$tap_desc" true else - echo "# saw: ($status) $output" - echo "# not: ($w_status) $w_output" - ok "$desc" false + echo "# saw: ($tap_status) $tap_output" + echo "# not: ($tap_w_status) $tap_w_output" + ok "$tap_desc" false fi } +# Strip a colon and everything after it off the output of a command, as long +# as that colon comes after at least one whitespace character. (This is done +# to avoid stripping the name of the program from the start of an error +# message.) This is used to remove system-specific error messages (coming +# from strerror, for example). +strip_colon_error() { + tap_output=`"$@" 2>&1` + tap_status=$? + tap_output=`puts "$tap_output" | sed 's/^\([^ ]* [^:]*\):.*/\1/'` + puts "$tap_output" + return $tap_status +} + # Bail out with an error message. bail () { echo 'Bail out!' "$@" @@ -167,12 +215,32 @@ diag () { # Search for the given file first in $BUILD and then in $SOURCE and echo the # path where the file was found, or the empty string if the file wasn't # found. +# +# This macro uses puts, so don't run it using backticks inside double quotes +# or bizarre quoting behavior will happen with Solaris sh. test_file_path () { - if [ -f "$BUILD/$1" ] ; then - echo "$BUILD/$1" - elif [ -f "$SOURCE/$1" ] ; then - echo "$SOURCE/$1" + if [ -n "$BUILD" ] && [ -f "$BUILD/$1" ] ; then + puts "$BUILD/$1" + elif [ -n "$SOURCE" ] && [ -f "$SOURCE/$1" ] ; then + puts "$SOURCE/$1" else echo '' fi } + +# Create $BUILD/tmp for use by tests for storing temporary files and return +# the path (via standard output). +# +# This macro uses puts, so don't run it using backticks inside double quotes +# or bizarre quoting behavior will happen with Solaris sh. +test_tmpdir () { + if [ -z "$BUILD" ] ; then + tap_tmpdir="./tmp" + else + tap_tmpdir="$BUILD"/tmp + fi + if [ ! -d "$tap_tmpdir" ] ; then + mkdir "$tap_tmpdir" || bail "Error creating $tap_tmpdir" + fi + puts "$tap_tmpdir" +} diff --git a/tests/tap/macros.h b/tests/tap/macros.h new file mode 100644 index 0000000..33fee42 --- /dev/null +++ b/tests/tap/macros.h @@ -0,0 +1,88 @@ +/* + * Helpful macros for TAP header files. + * + * This is not, strictly speaking, related to TAP, but any TAP add-on is + * probably going to need these macros, so define them in one place so that + * everyone can pull them in. + * + * This file is part of C TAP Harness. The current version plus supporting + * documentation is at . + * + * Copyright 2008, 2012 Russ Allbery + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef TAP_MACROS_H +#define TAP_MACROS_H 1 + +/* + * __attribute__ is available in gcc 2.5 and later, but only with gcc 2.7 + * could you use the __format__ form of the attributes, which is what we use + * (to avoid confusion with other macros), and only with gcc 2.96 can you use + * the attribute __malloc__. 2.96 is very old, so don't bother trying to get + * the other attributes to work with GCC versions between 2.7 and 2.96. + */ +#ifndef __attribute__ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 96) +# define __attribute__(spec) /* empty */ +# endif +#endif + +/* + * We use __alloc_size__, but it was only available in fairly recent versions + * of GCC. Suppress warnings about the unknown attribute if GCC is too old. + * We know that we're GCC at this point, so we can use the GCC variadic macro + * extension, which will still work with versions of GCC too old to have C99 + * variadic macro support. + */ +#if !defined(__attribute__) && !defined(__alloc_size__) +# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3) +# define __alloc_size__(spec, args...) /* empty */ +# endif +#endif + +/* + * LLVM and Clang pretend to be GCC but don't support all of the __attribute__ + * settings that GCC does. For them, suppress warnings about unknown + * attributes on declarations. This unfortunately will affect the entire + * compilation context, but there's no push and pop available. + */ +#if !defined(__attribute__) && (defined(__llvm__) || defined(__clang__)) +# pragma GCC diagnostic ignored "-Wattributes" +#endif + +/* Used for unused parameters to silence gcc warnings. */ +#define UNUSED __attribute__((__unused__)) + +/* + * BEGIN_DECLS is used at the beginning of declarations so that C++ + * compilers don't mangle their names. END_DECLS is used at the end. + */ +#undef BEGIN_DECLS +#undef END_DECLS +#ifdef __cplusplus +# define BEGIN_DECLS extern "C" { +# define END_DECLS } +#else +# define BEGIN_DECLS /* empty */ +# define END_DECLS /* empty */ +#endif + +#endif /* TAP_MACROS_H */ diff --git a/tests/tap/messages.c b/tests/tap/messages.c index 3bb9a1a..abc2c49 100644 --- a/tests/tap/messages.c +++ b/tests/tap/messages.c @@ -5,24 +5,39 @@ * into a buffer that can be inspected later, allowing testing of error * handling. * - * Copyright 2006, 2007, 2009 - * Board of Trustees, Leland Stanford Jr. University - * Copyright (c) 2004, 2005, 2006 - * by Internet Systems Consortium, Inc. ("ISC") - * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, - * 2002, 2003 by The Internet Software Consortium and Rich Salz + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . * - * See LICENSE for licensing terms. + * Copyright 2002, 2004, 2005 Russ Allbery + * Copyright 2006, 2007, 2009, 2012 + * The Board of Trustees of the Leland Stanford Junior University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. */ #include #include +#include #include -#include -#include +#include #include -#include /* A global buffer into which message_log_buffer stores error messages. */ char *errors = NULL; @@ -33,18 +48,18 @@ char *errors = NULL; * error_capture. */ static void -message_log_buffer(int len, const char *fmt, va_list args, int error UNUSED) +message_log_buffer(int len UNUSED, const char *fmt, va_list args, + int error UNUSED) { char *message; - message = xmalloc(len + 1); - vsnprintf(message, len + 1, fmt, args); - if (errors == NULL) { - errors = concat(message, "\n", (char *) 0); - } else { + bvasprintf(&message, fmt, args); + if (errors == NULL) + basprintf(&errors, "%s\n", message); + else { char *new_errors; - new_errors = concat(errors, message, "\n", (char *) 0); + basprintf(&new_errors, "%s%s\n", errors, message); free(errors); errors = new_errors; } diff --git a/tests/tap/messages.h b/tests/tap/messages.h index 2b9a7db..0544f2d 100644 --- a/tests/tap/messages.h +++ b/tests/tap/messages.h @@ -1,21 +1,37 @@ /* * Utility functions to test message handling. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * + * Copyright 2002 Russ Allbery * Copyright 2006, 2007, 2009 - * Board of Trustees, Leland Stanford Jr. University - * Copyright (c) 2004, 2005, 2006 - * by Internet Systems Consortium, Inc. ("ISC") - * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, - * 2002, 2003 by The Internet Software Consortium and Rich Salz + * The Board of Trustees of the Leland Stanford Junior University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * - * See LICENSE for licensing terms. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. */ #ifndef TAP_MESSAGES_H #define TAP_MESSAGES_H 1 #include -#include +#include /* A global buffer into which errors_capture stores errors. */ extern char *errors; diff --git a/tests/tap/perl/Test/RRA.pm b/tests/tap/perl/Test/RRA.pm new file mode 100644 index 0000000..2d119f4 --- /dev/null +++ b/tests/tap/perl/Test/RRA.pm @@ -0,0 +1,222 @@ +# Helper functions for test programs written in Perl. +# +# This module provides a collection of helper functions used by test programs +# written in Perl. This is a general collection of functions that can be used +# by both C packages with Automake and by stand-alone Perl modules. See +# Test::RRA::Automake for additional functions specifically for C Automake +# distributions. +# +# The canonical version of this file is maintained in the rra-c-util package, +# which can be found at . +# +# Written by Russ Allbery +# Copyright 2013 +# The Board of Trustees of the Leland Stanford Junior University +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +package Test::RRA; + +use 5.006; +use strict; +use warnings; + +use Exporter; +use Test::More; + +# For Perl 5.006 compatibility. +## no critic (ClassHierarchies::ProhibitExplicitISA) + +# Declare variables that should be set in BEGIN for robustness. +our (@EXPORT_OK, @ISA, $VERSION); + +# Set $VERSION and everything export-related in a BEGIN block for robustness +# against circular module loading (not that we load any modules, but +# consistency is good). +BEGIN { + @ISA = qw(Exporter); + @EXPORT_OK = qw(skip_unless_maintainer use_prereq); + + # This version should match the corresponding rra-c-util release, but with + # two digits for the minor version, including a leading zero if necessary, + # so that it will sort properly. + $VERSION = '4.08'; +} + +# Skip this test unless maintainer tests are requested. Takes a short +# description of what tests this script would perform, which is used in the +# skip message. Calls plan skip_all, which will terminate the program. +# +# $description - Short description of the tests +# +# Returns: undef +sub skip_unless_maintainer { + my ($description) = @_; + if (!$ENV{RRA_MAINTAINER_TESTS}) { + plan skip_all => "$description only run for maintainer"; + } + return; +} + +# Attempt to load a module and skip the test if the module could not be +# loaded. If the module could be loaded, call its import function manually. +# If the module could not be loaded, calls plan skip_all, which will terminate +# the program. +# +# The special logic here is based on Test::More and is required to get the +# imports to happen in the caller's namespace. +# +# $module - Name of the module to load +# @imports - Any arguments to import, possibly including a version +# +# Returns: undef +sub use_prereq { + my ($module, @imports) = @_; + + # If the first import looks like a version, pass it as a bare string. + my $version = q{}; + if (@imports >= 1 && $imports[0] =~ m{ \A \d+ (?: [.]\d+ )* \z }xms) { + $version = shift(@imports); + } + + # Get caller information to put imports in the correct package. + my ($package) = caller; + + # Do the import with eval, and try to isolate it from the surrounding + # context as much as possible. Based heavily on Test::More::_eval. + ## no critic (BuiltinFunctions::ProhibitStringyEval) + ## no critic (ValuesAndExpressions::ProhibitImplicitNewlines) + my ($result, $error, $sigdie); + { + local $@ = undef; + local $! = undef; + local $SIG{__DIE__} = undef; + $result = eval qq{ + package $package; + use $module $version \@imports; + 1; + }; + $error = $@; + $sigdie = $SIG{__DIE__} || undef; + } + + # If the use failed for any reason, skip the test. + if (!$result || $error) { + plan skip_all => "$module required for test"; + } + + # If the module set $SIG{__DIE__}, we cleared that via local. Restore it. + ## no critic (Variables::RequireLocalizedPunctuationVars) + if (defined($sigdie)) { + $SIG{__DIE__} = $sigdie; + } + return; +} + +1; +__END__ + +=for stopwords +Allbery Allbery's DESC bareword sublicense MERCHANTABILITY NONINFRINGEMENT +rra-c-util + +=head1 NAME + +Test::RRA - Support functions for Perl tests + +=head1 SYNOPSIS + + use Test::RRA qw(skip_unless_maintainer use_prereq); + + # Skip this test unless maintainer tests are requested. + skip_unless_maintainer('Coding style tests'); + + # Load modules, skipping the test if they're not available. + use_prereq('File::Slurp'); + use_prereq('Test::Script::Run', '0.04'); + +=head1 DESCRIPTION + +This module collects utility functions that are useful for Perl test +scripts. It assumes Russ Allbery's Perl module layout and test +conventions and will only be useful for other people if they use the +same conventions. + +=head1 FUNCTIONS + +None of these functions are imported by default. The ones used by a +script should be explicitly imported. + +=over 4 + +=item skip_unless_maintainer(DESC) + +Checks whether RRA_MAINTAINER_TESTS is set in the environment and skips +the whole test (by calling C from Test::More) if it is not. +DESC is a description of the tests being skipped. A space and C will be appended to it and used as the skip reason. + +=item use_prereq(MODULE[, VERSION][, IMPORT ...]) + +Attempts to load MODULE with the given VERSION and import arguments. If +this fails for any reason, the test will be skipped (by calling C from Test::More) with a skip reason saying that MODULE is +required for the test. + +VERSION will be passed to C as a version bareword if it looks like a +version number. The remaining IMPORT arguments will be passed as the +value of an array. + +=back + +=head1 AUTHOR + +Russ Allbery + +=head1 COPYRIGHT AND LICENSE + +Copyright 2013 The Board of Trustees of the Leland Stanford Junior +University. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +=head1 SEE ALSO + +Test::More(3), Test::RRA::Automake(3), Test::RRA::Config(3) + +This module is maintained in the rra-c-util package. The current version +is available from L. + +=cut diff --git a/tests/tap/perl/Test/RRA/Automake.pm b/tests/tap/perl/Test/RRA/Automake.pm new file mode 100644 index 0000000..2aadb6a --- /dev/null +++ b/tests/tap/perl/Test/RRA/Automake.pm @@ -0,0 +1,362 @@ +# Helper functions for Perl test programs in Automake distributions. +# +# This module provides a collection of helper functions used by test programs +# written in Perl and included in C source distributions that use Automake. +# They embed knowledge of how I lay out my source trees and test suites with +# Autoconf and Automake. They may be usable by others, but doing so will +# require closely following the conventions implemented by the rra-c-util +# utility collection. +# +# All the functions here assume that BUILD and SOURCE are set in the +# environment. This is normally done via the C TAP Harness runtests wrapper. +# +# The canonical version of this file is maintained in the rra-c-util package, +# which can be found at . +# +# Written by Russ Allbery +# Copyright 2013 +# The Board of Trustees of the Leland Stanford Junior University +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +package Test::RRA::Automake; + +use 5.006; +use strict; +use warnings; + +# For Perl 5.006 compatibility. +## no critic (ClassHierarchies::ProhibitExplicitISA) + +use Exporter; +use File::Spec; +use Test::More; +use Test::RRA::Config qw($LIBRARY_PATH); + +# Used below for use lib calls. +my ($PERL_BLIB_ARCH, $PERL_BLIB_LIB); + +# Determine the path to the build tree of any embedded Perl module package in +# this source package. We do this in a BEGIN block because we're going to use +# the results in a use lib command below. +BEGIN { + $PERL_BLIB_ARCH = File::Spec->catdir(qw(perl blib arch)); + $PERL_BLIB_LIB = File::Spec->catdir(qw(perl blib lib)); + + # If BUILD is set, we can come up with better values. + if (defined($ENV{BUILD})) { + my ($vol, $dirs) = File::Spec->splitpath($ENV{BUILD}, 1); + my @dirs = File::Spec->splitdir($dirs); + pop(@dirs); + $PERL_BLIB_ARCH = File::Spec->catdir(@dirs, qw(perl blib arch)); + $PERL_BLIB_LIB = File::Spec->catdir(@dirs, qw(perl blib lib)); + } +} + +# Prefer the modules built as part of our source package. Otherwise, we may +# not find Perl modules while testing, or find the wrong versions. +use lib $PERL_BLIB_ARCH; +use lib $PERL_BLIB_LIB; + +# Declare variables that should be set in BEGIN for robustness. +our (@EXPORT_OK, @ISA, $VERSION); + +# Set $VERSION and everything export-related in a BEGIN block for robustness +# against circular module loading (not that we load any modules, but +# consistency is good). +BEGIN { + @ISA = qw(Exporter); + @EXPORT_OK = qw(automake_setup perl_dirs test_file_path); + + # This version should match the corresponding rra-c-util release, but with + # two digits for the minor version, including a leading zero if necessary, + # so that it will sort properly. + $VERSION = '4.08'; +} + +# Perl directories to skip globally for perl_dirs. We ignore the perl +# directory if it exists since, in my packages, it is treated as a Perl module +# distribution and has its own standalone test suite. +my @GLOBAL_SKIP = qw(.git perl); + +# Perform initial test setup for running a Perl test in an Automake package. +# This verifies that BUILD and SOURCE are set and then changes directory to +# the SOURCE directory by default. Sets LD_LIBRARY_PATH if the $LIBRARY_PATH +# configuration option is set. Calls BAIL_OUT if BUILD or SOURCE are missing +# or if anything else fails. +# +# $args_ref - Reference to a hash of arguments to configure behavior: +# chdir_build - If set to a true value, changes to BUILD instead of SOURCE +# +# Returns: undef +sub automake_setup { + my ($args_ref) = @_; + + # Bail if BUILD or SOURCE are not set. + if (!$ENV{BUILD}) { + BAIL_OUT('BUILD not defined (run under runtests)'); + } + if (!$ENV{SOURCE}) { + BAIL_OUT('SOURCE not defined (run under runtests)'); + } + + # BUILD or SOURCE will be the test directory. Change to the parent. + my $start = $args_ref->{chdir_build} ? $ENV{BUILD} : $ENV{SOURCE}; + my ($vol, $dirs) = File::Spec->splitpath($start, 1); + my @dirs = File::Spec->splitdir($dirs); + pop(@dirs); + if ($dirs[-1] eq File::Spec->updir) { + pop(@dirs); + pop(@dirs); + } + my $root = File::Spec->catpath($vol, File::Spec->catdir(@dirs), q{}); + chdir($root) or BAIL_OUT("cannot chdir to $root: $!"); + + # If BUILD is a subdirectory of SOURCE, add it to the global ignore list. + my ($buildvol, $builddirs) = File::Spec->splitpath($ENV{BUILD}, 1); + my @builddirs = File::Spec->splitdir($builddirs); + pop(@builddirs); + if ($buildvol eq $vol && @builddirs == @dirs + 1) { + while (@dirs && $builddirs[0] eq $dirs[0]) { + shift(@builddirs); + shift(@dirs); + } + if (@builddirs == 1) { + push(@GLOBAL_SKIP, $builddirs[0]); + } + } + + # Set LD_LIBRARY_PATH if the $LIBRARY_PATH configuration option is set. + ## no critic (Variables::RequireLocalizedPunctuationVars) + if (defined($LIBRARY_PATH)) { + @builddirs = File::Spec->splitdir($builddirs); + pop(@builddirs); + my $libdir = File::Spec->catdir(@builddirs, $LIBRARY_PATH); + my $path = File::Spec->catpath($buildvol, $libdir, q{}); + if (-d "$path/.libs") { + $path .= '/.libs'; + } + if ($ENV{LD_LIBRARY_PATH}) { + $ENV{LD_LIBRARY_PATH} .= ":$path"; + } else { + $ENV{LD_LIBRARY_PATH} = $path; + } + } + return; +} + +# Returns a list of directories that may contain Perl scripts and that should +# be passed to Perl test infrastructure that expects a list of directories to +# recursively check. The list will be all eligible top-level directories in +# the package except for the tests directory, which is broken out to one +# additional level. Calls BAIL_OUT on any problems +# +# $args_ref - Reference to a hash of arguments to configure behavior: +# skip - A reference to an array of directories to skip +# +# Returns: List of directories possibly containing Perl scripts to test +sub perl_dirs { + my ($args_ref) = @_; + + # Add the global skip list. + my @skip = $args_ref->{skip} ? @{ $args_ref->{skip} } : (); + push(@skip, @GLOBAL_SKIP); + + # Separate directories to skip under tests from top-level directories. + my @skip_tests = grep { m{ \A tests/ }xms } @skip; + @skip = grep { !m{ \A tests }xms } @skip; + for my $skip_dir (@skip_tests) { + $skip_dir =~ s{ \A tests/ }{}xms; + } + + # Convert the skip lists into hashes for convenience. + my %skip = map { $_ => 1 } @skip, 'tests'; + my %skip_tests = map { $_ => 1 } @skip_tests; + + # Build the list of top-level directories to test. + opendir(my $rootdir, q{.}) or BAIL_OUT("cannot open .: $!"); + my @dirs = grep { -d $_ && !$skip{$_} } readdir($rootdir); + closedir($rootdir); + @dirs = File::Spec->no_upwards(@dirs); + + # Add the list of subdirectories of the tests directory. + if (-d 'tests') { + opendir(my $testsdir, q{tests}) or BAIL_OUT("cannot open tests: $!"); + + # Skip if found in %skip_tests or if not a directory. + my $is_skipped = sub { + my ($dir) = @_; + return 1 if $skip_tests{$dir}; + $dir = File::Spec->catdir('tests', $dir); + return -d $dir ? 0 : 1; + }; + + # Build the filtered list of subdirectories of tests. + my @test_dirs = grep { !$is_skipped->($_) } readdir($testsdir); + closedir($testsdir); + @test_dirs = File::Spec->no_upwards(@test_dirs); + + # Add the tests directory to the start of the directory name. + push(@dirs, map { File::Spec->catdir('tests', $_) } @test_dirs); + } + return @dirs; +} + +# Find a configuration file for the test suite. Searches relative to BUILD +# first and then SOURCE and returns whichever is found first. Calls BAIL_OUT +# if the file could not be found. +# +# $file - Partial path to the file +# +# Returns: Full path to the file +sub test_file_path { + my ($file) = @_; + BASE: + for my $base ($ENV{BUILD}, $ENV{SOURCE}) { + next if !defined($base); + if (-f "$base/$file") { + return "$base/$file"; + } + } + BAIL_OUT("cannot find $file"); + return; +} + +1; +__END__ + +=for stopwords +Allbery Automake Automake-aware Automake-based rra-c-util ARGS +subdirectories sublicense MERCHANTABILITY NONINFRINGEMENT + +=head1 NAME + +Test::RRA::Automake - Automake-aware support functions for Perl tests + +=head1 SYNOPSIS + + use Test::RRA::Automake qw(automake_setup perl_dirs test_file_path); + automake_setup({ chdir_build => 1 }); + + # Paths to directories that may contain Perl scripts. + my @dirs = perl_dirs({ skip => [qw(lib)] }); + + # Configuration for Kerberos tests. + my $keytab = test_file_path('config/keytab'); + +=head1 DESCRIPTION + +This module collects utility functions that are useful for test scripts +written in Perl and included in a C Automake-based package. They assume +the layout of a package that uses rra-c-util and C TAP Harness for the +test structure. + +Loading this module will also add the directories C and +C to the Perl library search path, relative to BUILD if +that environment variable is set. This is harmless for C Automake +projects that don't contain an embedded Perl module, and for those +projects that do, this will allow subsequent C calls to find modules +that are built as part of the package build process. + +The automake_setup() function should be called before calling any other +functions provided by this module. + +=head1 FUNCTIONS + +None of these functions are imported by default. The ones used by a +script should be explicitly imported. On failure, all of these functions +call BAIL_OUT (from Test::More). + +=over 4 + +=item automake_setup([ARGS]) + +Verifies that the BUILD and SOURCE environment variables are set and +then changes directory to the top of the source tree (which is one +directory up from the SOURCE path, since SOURCE points to the top of +the tests directory). + +If ARGS is given, it should be a reference to a hash of configuration +options. Only one option is supported: C. If it is set +to a true value, automake_setup() changes directories to the top of +the build tree instead. + +=item perl_dirs([ARGS]) + +Returns a list of directories that may contain Perl scripts that should be +tested by test scripts that test all Perl in the source tree (such as +syntax or coding style checks). The paths will be simple directory names +relative to the current directory or two-part directory names under the +F directory. (Directories under F are broken out separately +since it's common to want to apply different policies to different +subdirectories of F.) + +If ARGS is given, it should be a reference to a hash of configuration +options. Only one option is supported: C, whose value should be a +reference to an array of additional top-level directories or directories +starting with C that should be skipped. + +=item test_file_path(FILE) + +Given FILE, which should be a relative path, locates that file relative to +the test directory in either the source or build tree. FILE will be +checked for relative to the environment variable BUILD first, and then +relative to SOURCE. test_file_path() returns the full path to FILE or +calls BAIL_OUT if FILE could not be found. + +=back + +=head1 AUTHOR + +Russ Allbery + +=head1 COPYRIGHT AND LICENSE + +Copyright 2013 The Board of Trustees of the Leland Stanford Junior +University. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +=head1 SEE ALSO + +Test::More(3), Test::RRA(3), Test::RRA::Config(3) + +The C TAP Harness test driver and libraries for TAP-based C testing are +available from L. + +This module is maintained in the rra-c-util package. The current version +is available from L. + +=cut diff --git a/tests/tap/perl/Test/RRA/Config.pm b/tests/tap/perl/Test/RRA/Config.pm new file mode 100644 index 0000000..0091b26 --- /dev/null +++ b/tests/tap/perl/Test/RRA/Config.pm @@ -0,0 +1,200 @@ +# Configuration for Perl test cases. +# +# In order to reuse the same Perl test cases in multiple packages, I use a +# configuration file to store some package-specific data. This module loads +# that configuration and provides the namespace for the configuration +# settings. +# +# The canonical version of this file is maintained in the rra-c-util package, +# which can be found at . + +package Test::RRA::Config; + +use 5.006; +use strict; +use warnings; + +# For Perl 5.006 compatibility. +## no critic (ClassHierarchies::ProhibitExplicitISA) + +use Exporter; +use Test::More; + +# Declare variables that should be set in BEGIN for robustness. +our (@EXPORT_OK, @ISA, $VERSION); + +# Set $VERSION and everything export-related in a BEGIN block for robustness +# against circular module loading (not that we load any modules, but +# consistency is good). +BEGIN { + @ISA = qw(Exporter); + @EXPORT_OK = qw( + $COVERAGE_LEVEL @COVERAGE_SKIP_TESTS @CRITIC_IGNORE $LIBRARY_PATH + $MINIMUM_VERSION %MINIMUM_VERSION @POD_COVERAGE_EXCLUDE + ); + + # This version should match the corresponding rra-c-util release, but with + # two digits for the minor version, including a leading zero if necessary, + # so that it will sort properly. + $VERSION = '4.08'; +} + +# If BUILD or SOURCE are set in the environment, look for data/perl.conf under +# those paths for a C Automake package. Otherwise, look in t/data/perl.conf +# for a standalone Perl module. Don't use Test::RRA::Automake since it may +# not exist. +our $PATH; +for my $base ($ENV{BUILD}, $ENV{SOURCE}, 't') { + next if !defined($base); + my $path = "$base/data/perl.conf"; + if (-r $path) { + $PATH = $path; + last; + } +} +if (!defined($PATH)) { + BAIL_OUT('cannot find data/perl.conf'); +} + +# Pre-declare all of our variables and set any defaults. +our $COVERAGE_LEVEL = 100; +our @COVERAGE_SKIP_TESTS; +our @CRITIC_IGNORE; +our $LIBRARY_PATH; +our $MINIMUM_VERSION = '5.008'; +our %MINIMUM_VERSION; +our @POD_COVERAGE_EXCLUDE; + +# Load the configuration. +if (!do($PATH)) { + my $error = $@ || $! || 'loading file did not return true'; + BAIL_OUT("cannot load data/perl.conf: $error"); +} + +1; +__END__ + +=for stopwords +Allbery rra-c-util Automake perlcritic .libs namespace sublicense +MERCHANTABILITY NONINFRINGEMENT + +=head1 NAME + +Test::RRA::Config - Perl test configuration + +=head1 SYNOPSIS + + use Test::RRA::Config qw($MINIMUM_VERSION); + print "Required Perl version is $MINIMUM_VERSION\n"; + +=head1 DESCRIPTION + +Test::RRA::Config encapsulates per-package configuration for generic Perl +test programs that are shared between multiple packages using the +rra-c-util infrastructure. It handles locating and loading the test +configuration file for both C Automake packages and stand-alone Perl +modules. + +Test::RRA::Config looks for a file named F relative to the +root of the test directory. That root is taken from the environment +variables BUILD or SOURCE (in that order) if set, which will be the case +for C Automake packages using C TAP Harness. If neither is set, it +expects the root of the test directory to be a directory named F +relative to the current directory, which will be the case for stand-alone +Perl modules. + +The following variables are supported: + +=over 4 + +=item $COVERAGE_LEVEL + +The coverage level achieved by the test suite for Perl test coverage +testing using Test::Strict, as a percentage. The test will fail if test +coverage less than this percentage is achieved. If not given, defaults +to 100. + +=item @COVERAGE_SKIP_TESTS + +Directories under F whose tests should be skipped when doing coverage +testing. This can be tests that won't contribute to coverage or tests +that don't run properly under Devel::Cover for some reason (such as ones +that use taint checking). F and F