diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 18:47:14 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 18:47:14 +0000 |
commit | 3eaf4237194e25804f221af93c269d3d97e2809d (patch) | |
tree | cdedf3fc954b0727b0b34aa9b0b211cc18f854eb /src | |
download | smartcardauth-3eaf4237194e25804f221af93c269d3d97e2809d.tar.gz smartcardauth-3eaf4237194e25804f221af93c269d3d97e2809d.zip |
Added my SmartCard login/session lock/unlock utility
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/smartcardauth@1097604 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile | 16 | ||||
-rw-r--r-- | src/ckpasswd.c | 366 | ||||
-rw-r--r-- | src/messages.c | 493 | ||||
-rw-r--r-- | src/xmalloc.c | 163 |
4 files changed, 1038 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..e71a413 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,16 @@ +all: ckpasswd.o xmalloc.o messages.o ckpasswd + +ckpasswd.o: ckpasswd.c + gcc ckpasswd.c -c + +xmalloc.o: xmalloc.c + gcc xmalloc.c -c + +messages.o: messages.c + gcc messages.c -c + +ckpasswd: ckpasswd.o + gcc ckpasswd.o xmalloc.o messages.o -o ckpasswd -lpam -lcrypt + +clean: + rm -f ckpasswd.o xmalloc.o messages.o ckpasswd diff --git a/src/ckpasswd.c b/src/ckpasswd.c new file mode 100644 index 0000000..04b4b64 --- /dev/null +++ b/src/ckpasswd.c @@ -0,0 +1,366 @@ +/* $Id: ckpasswd.c 7565 2006-08-28 02:42:54Z eagle $ +** +** The default username/password authenticator. +** +** This program is intended to be run by nnrpd and handle usernames and +** passwords. It can authenticate against a regular flat file (the type +** managed by htpasswd), a DBM file, the system password file or shadow file, +** or PAM. +*/ + +/* Used for unused parameters to silence gcc warnings. */ +#define UNUSED __attribute__((__unused__)) + +/* Make available the bool type. */ +#if INN_HAVE_STDBOOL_H +# include <stdbool.h> +#else +# undef true +# undef false +# define true (1) +# define false (0) +# ifndef __cplusplus +# define bool int +# endif +#endif /* INN_HAVE_STDBOOL_H */ + +#include <stdlib.h> +#include <string.h> +#include <crypt.h> +#include <fcntl.h> +#include <pwd.h> +#include <grp.h> + +#define DB_DBM_HSEARCH 1 +#include <db.h> +#define OPT_DBM "d:" + +#if HAVE_GETSPNAM +# include <shadow.h> +# define OPT_SHADOW "s" +#else +# define OPT_SHADOW "" +#endif + +/* 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__) + +#include <security/pam_appl.h> + +/* Holds the authentication information from nnrpd. */ +struct auth_info { + char *username; + char *password; +}; + +/* +** The PAM conversation function. +** +** Since we already have all the information and can't ask the user +** questions, we can't quite follow the real PAM protocol. Instead, we just +** return the password in response to every question that PAM asks. There +** appears to be no generic way to determine whether the message in question +** is indeed asking for the password.... +** +** This function allocates an array of struct pam_response to return to the +** PAM libraries that's never freed. For this program, this isn't much of an +** issue, since it will likely only be called once and then the program will +** exit. This function uses malloc and strdup instead of xmalloc and xstrdup +** intentionally so that the PAM conversation will be closed cleanly if we +** run out of memory rather than simply terminated. +** +** appdata_ptr contains the password we were given. +*/ +static int pass_conv(int num_msg, const struct pam_message **msgm UNUSED, struct pam_response **response, void *appdata_ptr) +{ + int i; + + *response = malloc(num_msg * sizeof(struct pam_response)); + if (*response == NULL) + return PAM_CONV_ERR; + for (i = 0; i < num_msg; i++) { + (*response)[i].resp = strdup((char *)appdata_ptr); + (*response)[i].resp_retcode = 0; + } + return PAM_SUCCESS; +} + + +/* +** Authenticate a user via PAM. +** +** Attempts to authenticate a user with PAM, returning true if the user +** successfully authenticates and false otherwise. Note that this function +** doesn't attempt to handle any remapping of the authenticated user by the +** PAM stack, but just assumes that the authenticated user was the same as +** the username given. +** +** Right now, all failures are handled via die. This may be worth revisiting +** in case we want to try other authentication methods if this fails for a +** reason other than the system not having PAM support. +*/ + +static bool auth_pam(const char *username, char *password) +{ + pam_handle_t *pamh; + struct pam_conv conv; + int status; + + conv.conv = pass_conv; + conv.appdata_ptr = password; + status = pam_start("nnrpd", username, &conv, &pamh); + if (status != PAM_SUCCESS) + die("pam_start failed: %s", pam_strerror(pamh, status)); + status = pam_authenticate(pamh, PAM_SILENT); + if (status != PAM_SUCCESS) + die("pam_authenticate failed: %s", pam_strerror(pamh, status)); + status = pam_acct_mgmt(pamh, PAM_SILENT); + if (status != PAM_SUCCESS) + die("pam_acct_mgmt failed: %s", pam_strerror(pamh, status)); + status = pam_end(pamh, status); + if (status != PAM_SUCCESS) + die("pam_end failed: %s", pam_strerror(pamh, status)); + + /* If we get to here, the user successfully authenticated. */ + return true; +} + + +/* +** Try to get a password out of a dbm file. The dbm file should have the +** username for the key and the crypted password as the value. The crypted +** password, if found, is returned as a newly allocated string; otherwise, +** NULL is returned. +*/ +#if !(defined(HAVE_DBM) || defined(HAVE_BDB_DBM)) +static char * +password_dbm(char *user UNUSED, const char *file UNUSED) +{ + return NULL; +} +#else +static char * +password_dbm(char *name, const char *file) +{ + datum key, value; + DBM *database; + char *password; + + database = dbm_open(file, O_RDONLY, 0600); + if (database == NULL) + return NULL; + key.dptr = name; + key.dsize = strlen(name); + value = dbm_fetch(database, key); + if (value.dptr == NULL) { + dbm_close(database); + return NULL; + } + password = xmalloc(value.dsize + 1); + strlcpy(password, value.dptr, value.dsize + 1); + dbm_close(database); + return password; +} +#endif /* HAVE_DBM || HAVE_BDB_DBM */ + + +/* +** Try to get a password out of the system /etc/shadow file. The crypted +** password, if found, is returned as a newly allocated string; otherwise, +** NULL is returned. +*/ +#if !HAVE_GETSPNAM +static char * +password_shadow(const char *user UNUSED) +{ + return NULL; +} +#else +static char * +password_shadow(const char *user) +{ + struct spwd *spwd; + + spwd = getspnam(user); + if (spwd != NULL) + return xstrdup(spwd->sp_pwdp); + return NULL; +} +#endif /* HAVE_GETSPNAM */ + + +/* +** Try to get a password out of the system password file. The crypted +** password, if found, is returned as a newly allocated string; otherwise, +** NULL is returned. +*/ +static char * +password_system(const char *username) +{ + struct passwd *pwd; + + pwd = getpwnam(username); + if (pwd != NULL) + return xstrdup(pwd->pw_passwd); + return NULL; +} + + +/* +** Try to get the name of a user's primary group out of the system group +** file. The group, if found, is returned as a newly allocated string; +** otherwise, NULL is returned. If the username is not found, NULL is +** returned. +*/ +static char * +group_system(const char *username) +{ + struct passwd *pwd; + struct group *gr; + + pwd = getpwnam(username); + if (pwd == NULL) + return NULL; + gr = getgrgid(pwd->pw_gid); + if (gr == NULL) + return NULL; + return xstrdup(gr->gr_name); +} + + +/* +** Output username (and group, if desired) in correct return format. +*/ +static void +output_user(const char *username, bool wantgroup) +{ + if (wantgroup) { + char *group = group_system(username); + if (group == NULL) + die("group info for user %s not available", username); + printf("User:%s@%s\n", username, group); + } + else + printf("User:%s\n", username); +} + + +/* +** Main routine. +** +** We handle the variences between systems with #if blocks above, so that +** this code can look fairly clean. +*/ +int +main(int argc, char *argv[]) +{ + enum authtype { AUTH_NONE, AUTH_SHADOW, AUTH_FILE, AUTH_DBM }; + + int opt; + enum authtype type = AUTH_NONE; + bool wantgroup = false; + const char *filename = NULL; + struct auth_info *authinfo = NULL; + char *password = NULL; + + //message_program_name = "ckpasswd"; + + while ((opt = getopt(argc, argv, "gf:u:p:" OPT_DBM OPT_SHADOW)) != -1) { + switch (opt) { + case 'g': + if (type == AUTH_DBM || type == AUTH_FILE) + die("-g option is incompatible with -d or -f"); + wantgroup = true; + break; + case 'd': + if (type != AUTH_NONE) + die("only one of -s, -f, or -d allowed"); + if (wantgroup) + die("-g option is incompatible with -d or -f"); + type = AUTH_DBM; + filename = optarg; + break; + case 'f': + if (type != AUTH_NONE) + die("only one of -s, -f, or -d allowed"); + if (wantgroup) + die("-g option is incompatible with -d or -f"); + type = AUTH_FILE; + filename = optarg; + break; + case 's': + if (type != AUTH_NONE) + die("only one of -s, -f, or -d allowed"); + type = AUTH_SHADOW; + break; + case 'u': + if (authinfo == NULL) { + authinfo = xmalloc(sizeof(struct auth_info)); + authinfo->password = NULL; + } + authinfo->username = optarg; + break; + case 'p': + if (authinfo == NULL) { + authinfo = xmalloc(sizeof(struct auth_info)); + authinfo->username = NULL; + } + authinfo->password = optarg; + break; + default: + exit(1); + } + } + if (argc != optind) + die("extra arguments given"); + if (authinfo != NULL && authinfo->username == NULL) + die("-u option is required if -p option is given"); + if (authinfo != NULL && authinfo->password == NULL) + die("-p option is required if -u option is given"); + +// /* Unless a username or password was given on the command line, assume +// we're being run by nnrpd. */ +// if (authinfo == NULL) +// authinfo = get_auth_info(stdin); +// if (authinfo == NULL) +// die("no authentication information from nnrpd"); +// if (authinfo->username[0] == '\0') +// die("null username"); + + /* Run the appropriate authentication routines. */ + switch (type) { + case AUTH_SHADOW: + password = password_shadow(authinfo->username); + if (password == NULL) + password = password_system(authinfo->username); + break; +// case AUTH_FILE: +// password = password_file(authinfo->username, filename); +// break; + case AUTH_DBM: + password = password_dbm(authinfo->username, filename); + break; + case AUTH_NONE: + if (auth_pam(authinfo->username, authinfo->password)) { + output_user(authinfo->username, wantgroup); + exit(0); + } + password = password_system(authinfo->username); + break; + } + + if (password == NULL) + die("user %s unknown", authinfo->username); + if (strcmp(password, crypt(authinfo->password, password)) != 0) + die("invalid password for user %s", authinfo->username); + + /* The password matched. */ + output_user(authinfo->username, wantgroup); + exit(0); +} diff --git a/src/messages.c b/src/messages.c new file mode 100644 index 0000000..7a54e0a --- /dev/null +++ b/src/messages.c @@ -0,0 +1,493 @@ +/* $Id: messages.c 5496 2002-06-07 13:59:06Z alexk $ +** +** Message and error reporting (possibly fatal). +** +** Usage: +** +** extern int cleanup(void); +** extern void log(int, const char *, va_list, int); +** +** message_fatal_cleanup = cleanup; +** message_program_name = argv[0]; +** +** warn("Something horrible happened at %lu", time); +** syswarn("Couldn't unlink temporary file %s", tmpfile); +** +** die("Something fatal happened at %lu", time); +** sysdie("open of %s failed", filename); +** +** debug("Some debugging message about %s", string); +** trace(TRACE_PROGRAM, "Program trace output"); +** notice("Informational notices"); +** +** message_handlers_warn(1, log); +** warn("This now goes through our log function"); +** +** These functions implement message reporting through user-configurable +** handler functions. debug() only does something if DEBUG is defined, +** trace() supports sending trace messages in one of a number of configurable +** classes of traces so that they can be turned on or off independently, and +** notice() and warn() just output messages as configured. die() similarly +** outputs a message but then exits, normally with a status of 1. +** +** The sys* versions do the same, but append a colon, a space, and the +** results of strerror(errno) to the end of the message. All functions +** accept printf-style formatting strings and arguments. +** +** If message_fatal_cleanup is non-NULL, it is called before exit by die and +** sysdie and its return value is used as the argument to exit. It is a +** pointer to a function taking no arguments and returning an int, and can be +** used to call cleanup functions or to exit in some alternate fashion (such +** as by calling _exit). +** +** If message_program_name is non-NULL, the string it points to, followed by +** a colon and a space, is prepended to all error messages logged through the +** message_log_stdout and message_log_stderr message handlers (the former is +** the default for notice, and the latter is the default for warn and die). +** +** Honoring error_program_name and printing to stderr is just the default +** handler; with message_handlers_* the handlers for any message function can +** be changed. By default, notice prints to stdout, warn and die print to +** stderr, and the others don't do anything at all. These functions take a +** count of handlers and then that many function pointers, each one to a +** function that takes a message length (the number of characters snprintf +** generates given the format and arguments), a format, an argument list as a +** va_list, and the applicable errno value (if any). +*/ + +/* Used for unused parameters to silence gcc warnings. */ +#define UNUSED __attribute__((__unused__)) + +/* Make available the bool type. */ +#if INN_HAVE_STDBOOL_H +# include <stdbool.h> +#else +# undef true +# undef false +# define true (1) +# define false (0) +# ifndef __cplusplus +# define bool int +# endif +#endif /* INN_HAVE_STDBOOL_H */ + +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <stdio.h> +#include <errno.h> +#include <syslog.h> +#include <crypt.h> +#include <fcntl.h> +#include <pwd.h> +#include <grp.h> + +/* 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__) + +/* These are the currently-supported types of traces. */ +enum message_trace { + TRACE_NETWORK, /* Network traffic. */ + TRACE_PROGRAM, /* Stages of program execution. */ + TRACE_ALL /* All traces; this must be last. */ +}; + +/* 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 trace(enum message_trace, const char *, ...) + __attribute__((__format__(printf, 2, 3))); +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))); + +/* Debug is handled specially, since we want to make the code disappear + completely unless we're built with -DDEBUG. We can only do that with + support for variadic macros, though; otherwise, the function just won't do + anything. */ +#if !defined(DEBUG) && (INN_HAVE_C99_VAMACROS || INN_HAVE_GNU_VAMACROS) +# if INN_HAVE_C99_VAMACROS +# define debug(format, ...) /* empty */ +# elif INN_HAVE_GNU_VAMACROS +# define debug(format, args...) /* empty */ +# endif +#else +extern void debug(const char *, ...) + __attribute__((__format__(printf, 1, 2))); +#endif + +/* 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_trace(int count, ...); +extern void message_handlers_notice(int count, ...); +extern void message_handlers_warn(int count, ...); +extern void message_handlers_die(int count, ...); + +/* Enable or disable tracing for particular classes of messages. */ +extern void message_trace_enable(enum message_trace, bool); + +/* 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 default handler lists. */ +static message_handler_func stdout_handlers[2] = { + message_log_stdout, NULL +}; +static message_handler_func stderr_handlers[2] = { + message_log_stderr, NULL +}; + +/* The list of logging functions currently in effect. */ +static message_handler_func *debug_handlers = NULL; +static message_handler_func *trace_handlers = NULL; +static message_handler_func *notice_handlers = stdout_handlers; +static message_handler_func *warn_handlers = stderr_handlers; +static message_handler_func *die_handlers = stderr_handlers; + +/* If non-NULL, called before exit and its return value passed to exit. */ +int (*message_fatal_cleanup)(void) = NULL; + +/* If non-NULL, prepended (followed by ": ") to messages. */ +const char *message_program_name = NULL; + +/* Whether or not we're currently outputting a particular type of trace. */ +static bool tracing[TRACE_ALL] = { false /* false, ... */ }; + + +/* +** Set the handlers for a particular message function. Takes a pointer to +** the handler list, the count of handlers, and the argument list. +*/ +static void +message_handlers(message_handler_func **list, int count, va_list args) +{ + int i; + + if (*list != stdout_handlers && *list != stderr_handlers) + free(*list); + *list = xmalloc(sizeof(message_handler_func) * (count + 1)); + for (i = 0; i < count; i++) + (*list)[i] = (message_handler_func) va_arg(args, message_handler_func); + (*list)[count] = NULL; +} + + +/* +** There's no good way of writing these handlers without a bunch of code +** duplication since we can't assume variadic macros, but I can at least make +** it easier to write and keep them consistent. +*/ +#define HANDLER_FUNCTION(type) \ + void \ + message_handlers_ ## type(int count, ...) \ + { \ + va_list args; \ + \ + va_start(args, count); \ + message_handlers(& type ## _handlers, count, args); \ + va_end(args); \ + } +HANDLER_FUNCTION(debug) +HANDLER_FUNCTION(trace) +HANDLER_FUNCTION(notice) +HANDLER_FUNCTION(warn) +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) +{ + if (message_program_name != NULL) + fprintf(stdout, "%s: ", message_program_name); + vfprintf(stdout, fmt, args); + if (err) + fprintf(stdout, ": %s", strerror(err)); + fprintf(stdout, "\n"); +} + + +/* +** Print a message to stderr, supporting message_program_name. Also flush +** 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) +{ + fflush(stdout); + if (message_program_name != NULL) + fprintf(stderr, "%s: ", message_program_name); + vfprintf(stderr, fmt, args); + if (err) + fprintf(stderr, ": %s", strerror(err)); + fprintf(stderr, "\n"); +} + + +/* +** Log a message to syslog. This is a helper function used to implement all +** of the syslog message log handlers. It takes the same arguments as a +** regular message handler function but with an additional priority +** argument. +*/ +static void +message_log_syslog(int pri, int len, const char *fmt, va_list args, int err) +{ + char *buffer; + + buffer = malloc(len + 1); + if (buffer == NULL) { + fprintf(stderr, "failed to malloc %u bytes at %s line %d: %s", + len + 1, __FILE__, __LINE__, strerror(errno)); + exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1); + } + vsnprintf(buffer, len + 1, fmt, args); + syslog(pri, err ? "%s: %m" : "%s", buffer); + free(buffer); +} + + +/* +** 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); \ + } +SYSLOG_FUNCTION(debug, DEBUG) +SYSLOG_FUNCTION(info, INFO) +SYSLOG_FUNCTION(notice, NOTICE) +SYSLOG_FUNCTION(warning, WARNING) +SYSLOG_FUNCTION(err, ERR) +SYSLOG_FUNCTION(crit, CRIT) + + +/* +** Enable or disable tracing for particular classes of messages. +*/ +void +message_trace_enable(enum message_trace type, bool enable) +{ + if (type > TRACE_ALL) + return; + if (type == TRACE_ALL) { + int i; + + for (i = 0; i < TRACE_ALL; i++) + tracing[i] = enable; + } else { + tracing[type] = enable; + } +} + + +/* +** All of the message handlers. There's a lot of code duplication here too, +** but each one is still *slightly* different and va_start has to be called +** multiple times, so it's hard to get rid of the duplication. +*/ + +#ifdef DEBUG +void +debug(const char *format, ...) +{ + va_list args; + message_handler_func *log; + int length; + + if (debug_handlers == NULL) + return; + va_start(args, format); + length = vsnprintf(NULL, 0, format, args); + va_end(args); + if (length < 0) + return; + for (log = debug_handlers; *log != NULL; log++) { + va_start(args, format); + (**log)(length, format, args, 0); + va_end(args); + } +} +#elif !INN_HAVE_C99_VAMACROS && !INN_HAVE_GNU_VAMACROS +void debug(const char *format UNUSED, ...) { } +#endif + +void +trace(enum message_trace type, const char *format, ...) +{ + va_list args; + message_handler_func *log; + int length; + + if (trace_handlers == NULL || !tracing[type]) + return; + va_start(args, format); + length = vsnprintf(NULL, 0, format, args); + va_end(args); + if (length < 0) + return; + for (log = trace_handlers; *log != NULL; log++) { + va_start(args, format); + (**log)(length, format, args, 0); + va_end(args); + } +} + +void +notice(const char *format, ...) +{ + va_list args; + message_handler_func *log; + int length; + + va_start(args, format); + length = vsnprintf(NULL, 0, format, args); + va_end(args); + if (length < 0) + return; + for (log = notice_handlers; *log != NULL; log++) { + va_start(args, format); + (**log)(length, format, args, 0); + va_end(args); + } +} + +void +sysnotice(const char *format, ...) +{ + va_list args; + message_handler_func *log; + int length; + int error = errno; + + va_start(args, format); + length = vsnprintf(NULL, 0, format, args); + va_end(args); + if (length < 0) + return; + for (log = notice_handlers; *log != NULL; log++) { + va_start(args, format); + (**log)(length, format, args, error); + va_end(args); + } +} + +void +warn(const char *format, ...) +{ + va_list args; + message_handler_func *log; + int length; + + va_start(args, format); + length = vsnprintf(NULL, 0, format, args); + va_end(args); + if (length < 0) + return; + for (log = warn_handlers; *log != NULL; log++) { + va_start(args, format); + (**log)(length, format, args, 0); + va_end(args); + } +} + +void +syswarn(const char *format, ...) +{ + va_list args; + message_handler_func *log; + int length; + int error = errno; + + va_start(args, format); + length = vsnprintf(NULL, 0, format, args); + va_end(args); + if (length < 0) + return; + for (log = warn_handlers; *log != NULL; log++) { + va_start(args, format); + (**log)(length, format, args, error); + va_end(args); + } +} + +void +die(const char *format, ...) +{ + va_list args; + message_handler_func *log; + int length; + + va_start(args, format); + length = vsnprintf(NULL, 0, format, args); + va_end(args); + if (length >= 0) + for (log = die_handlers; *log != NULL; log++) { + va_start(args, format); + (**log)(length, format, args, 0); + va_end(args); + } + exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1); +} + +void +sysdie(const char *format, ...) +{ + va_list args; + message_handler_func *log; + int length; + int error = errno; + + va_start(args, format); + length = vsnprintf(NULL, 0, format, args); + va_end(args); + if (length >= 0) + for (log = die_handlers; *log != NULL; log++) { + va_start(args, format); + (**log)(length, format, args, error); + va_end(args); + } + exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1); +} diff --git a/src/xmalloc.c b/src/xmalloc.c new file mode 100644 index 0000000..4d00c52 --- /dev/null +++ b/src/xmalloc.c @@ -0,0 +1,163 @@ +/* $Id: xmalloc.c 5381 2002-03-31 22:35:47Z rra $ +** +** malloc routines with failure handling. +** +** Usage: +** +** extern xmalloc_handler_t memory_error; +** extern const char *string; +** char *buffer; +** +** xmalloc_error_handler = memory_error; +** buffer = xmalloc(1024); +** xrealloc(buffer, 2048); +** free(buffer); +** buffer = xcalloc(1024); +** free(buffer); +** buffer = xstrdup(string); +** free(buffer); +** buffer = xstrndup(string, 25); +** +** xmalloc, xcalloc, xrealloc, and xstrdup behave exactly like their C +** library counterparts without the leading x except that they will never +** return NULL. Instead, on error, they call xmalloc_error_handler, +** passing it the name of the function whose memory allocation failed, the +** amount of the allocation, and the file and line number where the +** allocation function was invoked (from __FILE__ and __LINE__). This +** function may do whatever it wishes, such as some action to free up +** memory or a call to sleep to hope that system resources return. If the +** handler returns, the interrupted memory allocation function will try its +** allocation again (calling the handler again if it still fails). +** +** xstrndup behaves like xstrdup but only copies the given number of +** characters. It allocates an additional byte over its second argument and +** always nul-terminates the string. +** +** The default error handler, if none is set by the caller, prints an error +** message to stderr and exits with exit status 1. An error handler must +** take a const char * (function name), size_t (bytes allocated), const +** char * (file), and int (line). +** +** xmalloc will return a pointer to a valid memory region on an xmalloc of 0 +** bytes, ensuring this by allocating space for one character instead of 0 +** bytes. +** +** The functions defined here are actually x_malloc, x_realloc, etc. The +** header file defines macros named xmalloc, etc. that pass the file name +** and line number to these functions. +*/ + +/* Used for unused parameters to silence gcc warnings. */ +#define UNUSED __attribute__((__unused__)) + +/* Make available the bool type. */ +#if INN_HAVE_STDBOOL_H +# include <stdbool.h> +#else +# undef true +# undef false +# define true (1) +# define false (0) +# ifndef __cplusplus +# define bool int +# endif +#endif /* INN_HAVE_STDBOOL_H */ + +#include <stdlib.h> +#include <string.h> +#include <crypt.h> +#include <fcntl.h> +#include <pwd.h> +#include <grp.h> + +/* Failure handler takes the function, the size, the file, and the line. */ +typedef void (*xmalloc_handler_t)(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_t xmalloc_error_handler; + +/* The default error handler. */ +void +xmalloc_fail(const char *function, size_t size, const char *file, int line) +{ + sysdie("failed to %s %lu bytes at %s line %d", function, + (unsigned long) size, file, line); +} + +/* Assign to this variable to choose a handler other than the default. */ +xmalloc_handler_t xmalloc_error_handler = xmalloc_fail; + +void * +x_malloc(size_t size, const char *file, int line) +{ + void *p; + size_t real_size; + + real_size = (size > 0) ? size : 1; + p = malloc(real_size); + while (p == NULL) { + (*xmalloc_error_handler)("malloc", size, file, line); + p = malloc(real_size); + } + return p; +} + +void * +x_calloc(size_t n, size_t size, const char *file, int line) +{ + void *p; + + n = (n > 0) ? n : 1; + size = (size > 0) ? size : 1; + p = calloc(n, size); + while (p == NULL) { + (*xmalloc_error_handler)("calloc", n * size, file, line); + p = calloc(n, size); + } + return p; +} + +void * +x_realloc(void *p, size_t size, const char *file, int line) +{ + void *newp; + + newp = realloc(p, size); + while (newp == NULL && size > 0) { + (*xmalloc_error_handler)("realloc", size, file, line); + newp = realloc(p, size); + } + return newp; +} + +char * +x_strdup(const char *s, const char *file, int line) +{ + char *p; + size_t len; + + len = strlen(s) + 1; + p = malloc(len); + while (p == NULL) { + (*xmalloc_error_handler)("strdup", len, file, line); + p = malloc(len); + } + memcpy(p, s, len); + return p; +} + +char * +x_strndup(const char *s, size_t size, const char *file, int line) +{ + char *p; + + p = malloc(size + 1); + while (p == NULL) { + (*xmalloc_error_handler)("strndup", size + 1, file, line); + p = malloc(size + 1); + } + memcpy(p, s, size); + p[size] = '\0'; + return p; +} |