diff options
Diffstat (limited to 'usr/src/cmd/sendmail/libsm')
87 files changed, 18238 insertions, 0 deletions
diff --git a/usr/src/cmd/sendmail/libsm/Makefile b/usr/src/cmd/sendmail/libsm/Makefile new file mode 100644 index 0000000000..5feb0f11c1 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/Makefile @@ -0,0 +1,83 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# cmd/sendmail/libsm/Makefile +# + +include ../../Makefile.cmd +include ../Makefile.cmd + +INCPATH= -I. -I../src -I../include +CPPFLAGS= $(INCPATH) $(RLS_DEF) $(DBMDEF) $(CPPFLAGS.master) + +ARFLAGS= cq + +OBJS= assert.o cf.o clock.o clrerr.o config.o debug.o errstring.o exc.o \ + fclose.o feof.o ferror.o fflush.o fget.o findfp.o flags.o fopen.o \ + fpos.o fprintf.o fpurge.o fput.o fread.o fscanf.o fseek.o fvwrite.o \ + fwalk.o fwrite.o get.o heap.o ldap.o makebuf.o match.o mbdb.o niprop.o \ + path.o put.o refill.o rewind.o rpool.o setvbuf.o sem.o shm.o signal.o \ + smstdio.o snprintf.o sscanf.o stdio.o strcasecmp.o strdup.o strerror.o \ + strexit.o string.o stringf.o strio.o strl.o strrevcmp.o strto.o test.o \ + ungetc.o vasprintf.o vfprintf.o vfscanf.o vprintf.o vsnprintf.o \ + wbuf.o wsetup.o xtrap.o + +SRCS= $(OBJS:%.o=%.c) + +TESTS= t-event t-exc t-rpool t-string t-smstdio t-match t-strio t-heap \ + t-fopen t-strl t-strrevcmp t-path t-float t-scanf t-sem t-shm + +libsm= libsm.a + +.KEEP_STATE: +all: $(libsm) + +.PARALLEL: $(OBJS) + +$(libsm): $(OBJS) + $(RM) $@ + $(AR) $(ARFLAGS) $@ $(OBJS) + +clean: + $(RM) $(OBJS) $(libsm) $(TESTS) foo t-smstdio.1 + +depend obj: + +install: all + +LDLIBS += -lldap + +lint: lint_SRCS + +test: $(TESTS) + +t-%: t-%.c + $(LINK.c) $< -o $@ $(libsm) $(LDLIBS) + $(POST_PROCESS) + ./$@ + +include ../../Makefile.targ diff --git a/usr/src/cmd/sendmail/libsm/assert.c b/usr/src/cmd/sendmail/libsm/assert.c new file mode 100644 index 0000000000..dfced1fcb7 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/assert.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: assert.c,v 1.25.2.1 2003/12/05 22:44:17 ca Exp $") + +/* +** Abnormal program termination and assertion checking. +** For documentation, see assert.html. +*/ + +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> + +#include <sm/assert.h> +#include <sm/exc.h> +#include <sm/io.h> +#include <sm/varargs.h> + +/* +** Debug categories that are used to guard expensive assertion checks. +*/ + +SM_DEBUG_T SmExpensiveAssert = SM_DEBUG_INITIALIZER("sm_check_assert", + "@(#)$Debug: sm_check_assert - check assertions $"); + +SM_DEBUG_T SmExpensiveRequire = SM_DEBUG_INITIALIZER("sm_check_require", + "@(#)$Debug: sm_check_require - check function preconditions $"); + +SM_DEBUG_T SmExpensiveEnsure = SM_DEBUG_INITIALIZER("sm_check_ensure", + "@(#)$Debug: sm_check_ensure - check function postconditions $"); + +/* +** Debug category: send self SIGSTOP on fatal error, +** so that you can run a debugger on the stopped process. +*/ + +SM_DEBUG_T SmAbortStop = SM_DEBUG_INITIALIZER("sm_abort_stop", + "@(#)$Debug: sm_abort_stop - stop process on fatal error $"); + +/* +** SM_ABORT_DEFAULTHANDLER -- Default procedure for abnormal program +** termination. +** +** The goal is to display an error message without disturbing the +** process state too much, then dump core. +** +** Parameters: +** filename -- filename (can be NULL). +** lineno -- line number. +** msg -- message. +** +** Returns: +** doesn't return. +*/ + +static void +sm_abort_defaulthandler __P(( + const char *filename, + int lineno, + const char *msg)); + +static void +sm_abort_defaulthandler(filename, lineno, msg) + const char *filename; + int lineno; + const char *msg; +{ + if (filename != NULL) + sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s:%d: %s\n", filename, + lineno, msg); + else + sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s\n", msg); + sm_io_flush(smioerr, SM_TIME_DEFAULT); +#ifdef SIGSTOP + if (sm_debug_active(&SmAbortStop, 1)) + kill(getpid(), SIGSTOP); +#endif /* SIGSTOP */ + abort(); +} + +/* +** This is the action to be taken to cause abnormal program termination. +*/ + +static SM_ABORT_HANDLER_T SmAbortHandler = sm_abort_defaulthandler; + +/* +** SM_ABORT_SETHANDLER -- Set handler for SM_ABORT() +** +** This allows you to set a handler function for causing abnormal +** program termination; it is called when a logic bug is detected. +** +** Parameters: +** f -- handler. +** +** Returns: +** none. +*/ + +void +sm_abort_sethandler(f) + SM_ABORT_HANDLER_T f; +{ + if (f == NULL) + SmAbortHandler = sm_abort_defaulthandler; + else + SmAbortHandler = f; +} + +/* +** SM_ABORT -- Call it when you have detected a logic bug. +** +** Parameters: +** fmt -- format string. +** ... -- arguments. +** +** Returns: +** doesn't. +*/ + +void SM_DEAD_D +#if SM_VA_STD +sm_abort(char *fmt, ...) +#else /* SM_VA_STD */ +sm_abort(fmt, va_alist) + char *fmt; + va_dcl +#endif /* SM_VA_STD */ +{ + char msg[128]; + SM_VA_LOCAL_DECL + + SM_VA_START(ap, fmt); + sm_vsnprintf(msg, sizeof msg, fmt, ap); + SM_VA_END(ap); + sm_abort_at(NULL, 0, msg); +} + +/* +** SM_ABORT_AT -- Initiate abnormal program termination. +** +** This is the low level function that is called to initiate abnormal +** program termination. It prints an error message and terminates the +** program. It is called by sm_abort and by the assertion macros. +** If filename != NULL then filename and lineno specify the line of source +** code at which the bug was detected. +** +** Parameters: +** filename -- filename (can be NULL). +** lineno -- line number. +** msg -- message. +** +** Returns: +** doesn't. +*/ + +void SM_DEAD_D +sm_abort_at(filename, lineno, msg) + const char *filename; + int lineno; + const char *msg; +{ + SM_TRY + (*SmAbortHandler)(filename, lineno, msg); + SM_EXCEPT(exc, "*") + sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "exception raised by abort handler:\n"); + sm_exc_print(exc, smioerr); + sm_io_flush(smioerr, SM_TIME_DEFAULT); + SM_END_TRY + + /* + ** SmAbortHandler isn't supposed to return. + ** Since it has, let's make sure that the program is terminated. + */ + + abort(); +} diff --git a/usr/src/cmd/sendmail/libsm/cf.c b/usr/src/cmd/sendmail/libsm/cf.c new file mode 100644 index 0000000000..c1bf90d814 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/cf.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: cf.c,v 1.4 2001/02/01 02:40:21 dmoen Exp $") + +#include <ctype.h> +#include <errno.h> + +#include <sm/cf.h> +#include <sm/io.h> +#include <sm/string.h> +#include <sm/heap.h> + +/* +** SM_CF_GETOPT -- look up option values in the sendmail.cf file +** +** Open the sendmail.cf file and parse all of the 'O' directives. +** Each time one of the options named in the option vector optv +** is found, store a malloced copy of the option value in optv. +** +** Parameters: +** path -- pathname of sendmail.cf file +** optc -- size of option vector +** optv -- pointer to option vector +** +** Results: +** 0 on success, or an errno value on failure. +** An exception is raised on malloc failure. +*/ + +int +sm_cf_getopt(path, optc, optv) + char *path; + int optc; + SM_CF_OPT_T *optv; +{ + SM_FILE_T *cfp; + char buf[2048]; + char *p; + char *id; + char *idend; + char *val; + int i; + + cfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, path, SM_IO_RDONLY, NULL); + if (cfp == NULL) + return errno; + + while (sm_io_fgets(cfp, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL) + { + p = strchr(buf, '\n'); + if (p != NULL) + *p = '\0'; + + if (buf[0] != 'O' || buf[1] != ' ') + continue; + + id = &buf[2]; + val = strchr(id, '='); + if (val == NULL) + val = idend = id + strlen(id); + else + { + idend = val; + ++val; + while (*val == ' ') + ++val; + while (idend > id && idend[-1] == ' ') + --idend; + *idend = '\0'; + } + + for (i = 0; i < optc; ++i) + { + if (sm_strcasecmp(optv[i].opt_name, id) == 0) + { + optv[i].opt_val = sm_strdup_x(val); + break; + } + } + } + if (sm_io_error(cfp)) + { + int save_errno = errno; + + (void) sm_io_close(cfp, SM_TIME_DEFAULT); + errno = save_errno; + return errno; + } + (void) sm_io_close(cfp, SM_TIME_DEFAULT); + return 0; +} diff --git a/usr/src/cmd/sendmail/libsm/clock.c b/usr/src/cmd/sendmail/libsm/clock.c new file mode 100644 index 0000000000..0eeb94a196 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/clock.c @@ -0,0 +1,642 @@ +/* + * Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: clock.c,v 1.46 2004/08/03 19:57:22 ca Exp $") +#include <unistd.h> +#include <time.h> +#include <errno.h> +#if SM_CONF_SETITIMER +# include <sys/time.h> +#endif /* SM_CONF_SETITIMER */ +#include <sm/heap.h> +#include <sm/debug.h> +#include <sm/bitops.h> +#include <sm/clock.h> +#include "local.h" +#if _FFR_SLEEP_USE_SELECT > 0 +# include <sys/types.h> +#endif /* _FFR_SLEEP_USE_SELECT > 0 */ +#if defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2 +# include <syslog.h> +#endif /* defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2 */ + +#ifndef sigmask +# define sigmask(s) (1 << ((s) - 1)) +#endif /* ! sigmask */ + + +/* +** SM_SETEVENTM -- set an event to happen at a specific time in milliseconds. +** +** Events are stored in a sorted list for fast processing. +** An event only applies to the process that set it. +** Source is #ifdef'd to work with older OS's that don't have setitimer() +** (that is, don't have a timer granularity less than 1 second). +** +** Parameters: +** intvl -- interval until next event occurs (milliseconds). +** func -- function to call on event. +** arg -- argument to func on event. +** +** Returns: +** On success returns the SM_EVENT entry created. +** On failure returns NULL. +** +** Side Effects: +** none. +*/ + +static SM_EVENT *volatile SmEventQueue; /* head of event queue */ +static SM_EVENT *volatile SmFreeEventList; /* list of free events */ + +SM_EVENT * +sm_seteventm(intvl, func, arg) + int intvl; + void (*func)__P((int)); + int arg; +{ + ENTER_CRITICAL(); + if (SmFreeEventList == NULL) + { + SmFreeEventList = (SM_EVENT *) sm_pmalloc_x(sizeof *SmFreeEventList); + SmFreeEventList->ev_link = NULL; + } + LEAVE_CRITICAL(); + + return sm_sigsafe_seteventm(intvl, func, arg); +} + +/* +** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD +** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE +** DOING. +*/ + +SM_EVENT * +sm_sigsafe_seteventm(intvl, func, arg) + int intvl; + void (*func)__P((int)); + int arg; +{ + register SM_EVENT **evp; + register SM_EVENT *ev; +#if SM_CONF_SETITIMER + auto struct timeval now, nowi, ival; + auto struct itimerval itime; +#else /* SM_CONF_SETITIMER */ + auto time_t now, nowi; +#endif /* SM_CONF_SETITIMER */ + int wasblocked; + + /* negative times are not allowed */ + if (intvl <= 0) + return NULL; + + wasblocked = sm_blocksignal(SIGALRM); +#if SM_CONF_SETITIMER + ival.tv_sec = intvl / 1000; + ival.tv_usec = (intvl - ival.tv_sec * 1000) * 10; + (void) gettimeofday(&now, NULL); + nowi = now; + timeradd(&now, &ival, &nowi); +#else /* SM_CONF_SETITIMER */ + now = time(NULL); + nowi = now + (time_t)(intvl / 1000); +#endif /* SM_CONF_SETITIMER */ + + /* search event queue for correct position */ + for (evp = (SM_EVENT **) (&SmEventQueue); + (ev = *evp) != NULL; + evp = &ev->ev_link) + { +#if SM_CONF_SETITIMER + if (timercmp(&(ev->ev_time), &nowi, >=)) +#else /* SM_CONF_SETITIMER */ + if (ev->ev_time >= nowi) +#endif /* SM_CONF_SETITIMER */ + break; + } + + ENTER_CRITICAL(); + if (SmFreeEventList == NULL) + { + /* + ** This shouldn't happen. If called from sm_seteventm(), + ** we have just malloced a SmFreeEventList entry. If + ** called from a signal handler, it should have been + ** from an existing event which sm_tick() just added to + ** SmFreeEventList. + */ + + LEAVE_CRITICAL(); + if (wasblocked == 0) + (void) sm_releasesignal(SIGALRM); + return NULL; + } + else + { + ev = SmFreeEventList; + SmFreeEventList = ev->ev_link; + } + LEAVE_CRITICAL(); + + /* insert new event */ + ev->ev_time = nowi; + ev->ev_func = func; + ev->ev_arg = arg; + ev->ev_pid = getpid(); + ENTER_CRITICAL(); + ev->ev_link = *evp; + *evp = ev; + LEAVE_CRITICAL(); + + (void) sm_signal(SIGALRM, sm_tick); +# if SM_CONF_SETITIMER + timersub(&SmEventQueue->ev_time, &now, &itime.it_value); + itime.it_interval.tv_sec = 0; + itime.it_interval.tv_usec = 0; + if (itime.it_value.tv_sec < 0) + itime.it_value.tv_sec = 0; + if (itime.it_value.tv_sec == 0 && itime.it_value.tv_usec == 0) + itime.it_value.tv_usec = 1000; + (void) setitimer(ITIMER_REAL, &itime, NULL); +# else /* SM_CONF_SETITIMER */ + intvl = SmEventQueue->ev_time - now; + (void) alarm((unsigned) (intvl < 1 ? 1 : intvl)); +# endif /* SM_CONF_SETITIMER */ + if (wasblocked == 0) + (void) sm_releasesignal(SIGALRM); + return ev; +} +/* +** SM_CLREVENT -- remove an event from the event queue. +** +** Parameters: +** ev -- pointer to event to remove. +** +** Returns: +** none. +** +** Side Effects: +** arranges for event ev to not happen. +*/ + +void +sm_clrevent(ev) + register SM_EVENT *ev; +{ + register SM_EVENT **evp; + int wasblocked; +# if SM_CONF_SETITIMER + struct itimerval clr; +# endif /* SM_CONF_SETITIMER */ + + if (ev == NULL) + return; + + /* find the parent event */ + wasblocked = sm_blocksignal(SIGALRM); + for (evp = (SM_EVENT **) (&SmEventQueue); + *evp != NULL; + evp = &(*evp)->ev_link) + { + if (*evp == ev) + break; + } + + /* now remove it */ + if (*evp != NULL) + { + ENTER_CRITICAL(); + *evp = ev->ev_link; + ev->ev_link = SmFreeEventList; + SmFreeEventList = ev; + LEAVE_CRITICAL(); + } + + /* restore clocks and pick up anything spare */ + if (wasblocked == 0) + (void) sm_releasesignal(SIGALRM); + if (SmEventQueue != NULL) + (void) kill(getpid(), SIGALRM); + else + { + /* nothing left in event queue, no need for an alarm */ +# if SM_CONF_SETITIMER + clr.it_interval.tv_sec = 0; + clr.it_interval.tv_usec = 0; + clr.it_value.tv_sec = 0; + clr.it_value.tv_usec = 0; + (void) setitimer(ITIMER_REAL, &clr, NULL); +# else /* SM_CONF_SETITIMER */ + (void) alarm(0); +# endif /* SM_CONF_SETITIMER */ + } +} +/* +** SM_CLEAR_EVENTS -- remove all events from the event queue. +** +** Parameters: +** none. +** +** Returns: +** none. +*/ + +void +sm_clear_events() +{ + register SM_EVENT *ev; +#if SM_CONF_SETITIMER + struct itimerval clr; +#endif /* SM_CONF_SETITIMER */ + int wasblocked; + + /* nothing will be left in event queue, no need for an alarm */ +#if SM_CONF_SETITIMER + clr.it_interval.tv_sec = 0; + clr.it_interval.tv_usec = 0; + clr.it_value.tv_sec = 0; + clr.it_value.tv_usec = 0; + (void) setitimer(ITIMER_REAL, &clr, NULL); +#else /* SM_CONF_SETITIMER */ + (void) alarm(0); +#endif /* SM_CONF_SETITIMER */ + + if (SmEventQueue == NULL) + return; + + wasblocked = sm_blocksignal(SIGALRM); + + /* find the end of the EventQueue */ + for (ev = SmEventQueue; ev->ev_link != NULL; ev = ev->ev_link) + continue; + + ENTER_CRITICAL(); + ev->ev_link = SmFreeEventList; + SmFreeEventList = SmEventQueue; + SmEventQueue = NULL; + LEAVE_CRITICAL(); + + /* restore clocks and pick up anything spare */ + if (wasblocked == 0) + (void) sm_releasesignal(SIGALRM); +} +/* +** SM_TICK -- take a clock tick +** +** Called by the alarm clock. This routine runs events as needed. +** Always called as a signal handler, so we assume that SIGALRM +** has been blocked. +** +** Parameters: +** One that is ignored; for compatibility with signal handlers. +** +** Returns: +** none. +** +** Side Effects: +** calls the next function in EventQueue. +** +** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD +** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE +** DOING. +*/ + +/* ARGSUSED */ +SIGFUNC_DECL +sm_tick(sig) + int sig; +{ + register SM_EVENT *ev; + pid_t mypid; + int save_errno = errno; +#if SM_CONF_SETITIMER + struct itimerval clr; + struct timeval now; +#else /* SM_CONF_SETITIMER */ + register time_t now; +#endif /* SM_CONF_SETITIMER */ + +#if SM_CONF_SETITIMER + clr.it_interval.tv_sec = 0; + clr.it_interval.tv_usec = 0; + clr.it_value.tv_sec = 0; + clr.it_value.tv_usec = 0; + (void) setitimer(ITIMER_REAL, &clr, NULL); + gettimeofday(&now, NULL); +#else /* SM_CONF_SETITIMER */ + (void) alarm(0); + now = time(NULL); +#endif /* SM_CONF_SETITIMER */ + + FIX_SYSV_SIGNAL(sig, sm_tick); + errno = save_errno; + CHECK_CRITICAL(sig); + + mypid = getpid(); + while (PendingSignal != 0) + { + int sigbit = 0; + int sig = 0; + + if (bitset(PEND_SIGHUP, PendingSignal)) + { + sigbit = PEND_SIGHUP; + sig = SIGHUP; + } + else if (bitset(PEND_SIGINT, PendingSignal)) + { + sigbit = PEND_SIGINT; + sig = SIGINT; + } + else if (bitset(PEND_SIGTERM, PendingSignal)) + { + sigbit = PEND_SIGTERM; + sig = SIGTERM; + } + else if (bitset(PEND_SIGUSR1, PendingSignal)) + { + sigbit = PEND_SIGUSR1; + sig = SIGUSR1; + } + else + { + /* If we get here, we are in trouble */ + abort(); + } + PendingSignal &= ~sigbit; + kill(mypid, sig); + } + +#if SM_CONF_SETITIMER + gettimeofday(&now, NULL); +#else /* SM_CONF_SETITIMER */ + now = time(NULL); +#endif /* SM_CONF_SETITIMER */ + while ((ev = SmEventQueue) != NULL && + (ev->ev_pid != mypid || +#if SM_CONF_SETITIMER + timercmp(&ev->ev_time, &now, <=) +#else /* SM_CONF_SETITIMER */ + ev->ev_time <= now +#endif /* SM_CONF_SETITIMER */ + )) + { + void (*f)__P((int)); + int arg; + pid_t pid; + + /* process the event on the top of the queue */ + ev = SmEventQueue; + SmEventQueue = SmEventQueue->ev_link; + + /* we must be careful in here because ev_func may not return */ + f = ev->ev_func; + arg = ev->ev_arg; + pid = ev->ev_pid; + ENTER_CRITICAL(); + ev->ev_link = SmFreeEventList; + SmFreeEventList = ev; + LEAVE_CRITICAL(); + if (pid != getpid()) + continue; + if (SmEventQueue != NULL) + { +#if SM_CONF_SETITIMER + if (timercmp(&SmEventQueue->ev_time, &now, >)) + { + timersub(&SmEventQueue->ev_time, &now, + &clr.it_value); + clr.it_interval.tv_sec = 0; + clr.it_interval.tv_usec = 0; + if (clr.it_value.tv_sec < 0) + clr.it_value.tv_sec = 0; + if (clr.it_value.tv_sec == 0 && + clr.it_value.tv_usec == 0) + clr.it_value.tv_usec = 1000; + (void) setitimer(ITIMER_REAL, &clr, NULL); + } + else + { + clr.it_interval.tv_sec = 0; + clr.it_interval.tv_usec = 0; + clr.it_value.tv_sec = 3; + clr.it_value.tv_usec = 0; + (void) setitimer(ITIMER_REAL, &clr, NULL); + } +#else /* SM_CONF_SETITIMER */ + if (SmEventQueue->ev_time > now) + (void) alarm((unsigned) (SmEventQueue->ev_time + - now)); + else + (void) alarm(3); +#endif /* SM_CONF_SETITIMER */ + } + + /* call ev_func */ + errno = save_errno; + (*f)(arg); +#if SM_CONF_SETITIMER + clr.it_interval.tv_sec = 0; + clr.it_interval.tv_usec = 0; + clr.it_value.tv_sec = 0; + clr.it_value.tv_usec = 0; + (void) setitimer(ITIMER_REAL, &clr, NULL); + gettimeofday(&now, NULL); +#else /* SM_CONF_SETITIMER */ + (void) alarm(0); + now = time(NULL); +#endif /* SM_CONF_SETITIMER */ + } + if (SmEventQueue != NULL) + { +#if SM_CONF_SETITIMER + timersub(&SmEventQueue->ev_time, &now, &clr.it_value); + clr.it_interval.tv_sec = 0; + clr.it_interval.tv_usec = 0; + if (clr.it_value.tv_sec < 0) + clr.it_value.tv_sec = 0; + if (clr.it_value.tv_sec == 0 && clr.it_value.tv_usec == 0) + clr.it_value.tv_usec = 1000; + (void) setitimer(ITIMER_REAL, &clr, NULL); +#else /* SM_CONF_SETITIMER */ + (void) alarm((unsigned) (SmEventQueue->ev_time - now)); +#endif /* SM_CONF_SETITIMER */ + } + errno = save_errno; + return SIGFUNC_RETURN; +} +/* +** SLEEP -- a version of sleep that works with this stuff +** +** Because Unix sleep uses the alarm facility, I must reimplement +** it here. +** +** Parameters: +** intvl -- time to sleep. +** +** Returns: +** zero. +** +** Side Effects: +** waits for intvl time. However, other events can +** be run during that interval. +*/ + + +# if !HAVE_NANOSLEEP +static void sm_endsleep __P((int)); +static bool volatile SmSleepDone; +# endif /* !HAVE_NANOSLEEP */ + +#ifndef SLEEP_T +# define SLEEP_T unsigned int +#endif /* ! SLEEP_T */ + +SLEEP_T +sleep(intvl) + unsigned int intvl; +{ +#if HAVE_NANOSLEEP + struct timespec rqtp; + + if (intvl == 0) + return (SLEEP_T) 0; + rqtp.tv_sec = intvl; + rqtp.tv_nsec = 0; + nanosleep(&rqtp, NULL); + return (SLEEP_T) 0; +#else /* HAVE_NANOSLEEP */ + int was_held; + SM_EVENT *ev; +#if _FFR_SLEEP_USE_SELECT > 0 + int r; +# if _FFR_SLEEP_USE_SELECT > 0 + struct timeval sm_io_to; +# endif /* _FFR_SLEEP_USE_SELECT > 0 */ +#endif /* _FFR_SLEEP_USE_SELECT > 0 */ +#if SM_CONF_SETITIMER + struct timeval now, begin, diff; +# if _FFR_SLEEP_USE_SELECT > 0 + struct timeval slpv; +# endif /* _FFR_SLEEP_USE_SELECT > 0 */ +#else /* SM_CONF_SETITIMER */ + time_t begin, now; +#endif /* SM_CONF_SETITIMER */ + + if (intvl == 0) + return (SLEEP_T) 0; +#if defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2 + if (intvl > _FFR_MAX_SLEEP_TIME) + { + syslog(LOG_ERR, "sleep: interval=%u exceeds max value %d", + intvl, _FFR_MAX_SLEEP_TIME); +# if 0 + SM_ASSERT(intvl < (unsigned int) INT_MAX); +# endif /* 0 */ + intvl = _FFR_MAX_SLEEP_TIME; + } +#endif /* defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2 */ + SmSleepDone = false; + +#if SM_CONF_SETITIMER +# if _FFR_SLEEP_USE_SELECT > 0 + slpv.tv_sec = intvl; + slpv.tv_usec = 0; +# endif /* _FFR_SLEEP_USE_SELECT > 0 */ + (void) gettimeofday(&now, NULL); + begin = now; +#else /* SM_CONF_SETITIMER */ + now = begin = time(NULL); +#endif /* SM_CONF_SETITIMER */ + + ev = sm_setevent((time_t) intvl, sm_endsleep, 0); + if (ev == NULL) + { + /* COMPLAIN */ +#if 0 + syslog(LOG_ERR, "sleep: sm_setevent(%u) failed", intvl); +#endif /* 0 */ + SmSleepDone = true; + } + was_held = sm_releasesignal(SIGALRM); + + while (!SmSleepDone) + { +#if SM_CONF_SETITIMER + (void) gettimeofday(&now, NULL); + timersub(&now, &begin, &diff); + if (diff.tv_sec < 0 || + (diff.tv_sec == 0 && diff.tv_usec == 0)) + break; +# if _FFR_SLEEP_USE_SELECT > 0 + timersub(&slpv, &diff, &sm_io_to); +# endif /* _FFR_SLEEP_USE_SELECT > 0 */ +#else /* SM_CONF_SETITIMER */ + now = time(NULL); + + /* + ** Check whether time expired before signal is released. + ** Due to the granularity of time() add 1 to be on the + ** safe side. + */ + + if (!(begin + (time_t) intvl + 1 > now)) + break; +# if _FFR_SLEEP_USE_SELECT > 0 + sm_io_to.tv_sec = intvl - (now - begin); + if (sm_io_to.tv_sec <= 0) + sm_io_to.tv_sec = 1; + sm_io_to.tv_usec = 0; +# endif /* _FFR_SLEEP_USE_SELECT > 0 */ +#endif /* SM_CONF_SETITIMER */ +#if _FFR_SLEEP_USE_SELECT > 0 + if (intvl <= _FFR_SLEEP_USE_SELECT) + { + r = select(0, NULL, NULL, NULL, &sm_io_to); + if (r == 0) + break; + } + else +#endif /* _FFR_SLEEP_USE_SELECT > 0 */ + (void) pause(); + } + + /* if out of the loop without the event being triggered remove it */ + if (!SmSleepDone) + sm_clrevent(ev); + if (was_held > 0) + (void) sm_blocksignal(SIGALRM); + return (SLEEP_T) 0; +#endif /* HAVE_NANOSLEEP */ +} + +#if !HAVE_NANOSLEEP +static void +sm_endsleep(ignore) + int ignore; +{ + /* + ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD + ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE + ** DOING. + */ + + SmSleepDone = true; +} +#endif /* !HAVE_NANOSLEEP */ + diff --git a/usr/src/cmd/sendmail/libsm/clrerr.c b/usr/src/cmd/sendmail/libsm/clrerr.c new file mode 100644 index 0000000000..185cd415ed --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/clrerr.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: clrerr.c,v 1.11 2001/01/28 00:29:34 ca Exp $") +#include <sm/io.h> +#include <sm/assert.h> +#include "local.h" + +/* +** SM_IO_CLEARERR -- public function to clear a file pointer's error status +** +** Parameters: +** fp -- the file pointer +** +** Returns: +** nothing. +*/ +#undef sm_io_clearerr + +void +sm_io_clearerr(fp) + SM_FILE_T *fp; +{ + SM_REQUIRE_ISA(fp, SmFileMagic); + + sm_clearerr(fp); +} diff --git a/usr/src/cmd/sendmail/libsm/config.c b/usr/src/cmd/sendmail/libsm/config.c new file mode 100644 index 0000000000..b6ce966b3b --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/config.c @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2000-2003 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: config.c,v 1.30 2003/12/10 03:19:07 gshapiro Exp $") + +#include <stdlib.h> +#include <sm/heap.h> +#include <sm/string.h> +#include <sm/conf.h> + +/* +** PUTENV -- emulation of putenv() in terms of setenv() +** +** Not needed on Posix-compliant systems. +** This doesn't have full Posix semantics, but it's good enough +** for sendmail. +** +** Parameter: +** env -- the environment to put. +** +** Returns: +** 0 on success, < 0 on failure. +*/ + +#if NEEDPUTENV + +# if NEEDPUTENV == 2 /* no setenv(3) call available */ + +int +putenv(str) + char *str; +{ + char **current; + int matchlen, envlen = 0; + char *tmp; + char **newenv; + static bool first = true; + extern char **environ; + + /* + ** find out how much of str to match when searching + ** for a string to replace. + */ + + if ((tmp = strchr(str, '=')) == NULL || tmp == str) + matchlen = strlen(str); + else + matchlen = (int) (tmp - str); + ++matchlen; + + /* + ** Search for an existing string in the environment and find the + ** length of environ. If found, replace and exit. + */ + + for (current = environ; *current != NULL; current++) + { + ++envlen; + + if (strncmp(str, *current, matchlen) == 0) + { + /* found it, now insert the new version */ + *current = (char *) str; + return 0; + } + } + + /* + ** There wasn't already a slot so add space for a new slot. + ** If this is our first time through, use malloc(), else realloc(). + */ + + if (first) + { + newenv = (char **) sm_malloc(sizeof(char *) * (envlen + 2)); + if (newenv == NULL) + return -1; + + first = false; + (void) memcpy(newenv, environ, sizeof(char *) * envlen); + } + else + { + newenv = (char **) sm_realloc((char *) environ, + sizeof(char *) * (envlen + 2)); + if (newenv == NULL) + return -1; + } + + /* actually add in the new entry */ + environ = newenv; + environ[envlen] = (char *) str; + environ[envlen + 1] = NULL; + + return 0; +} + +# else /* NEEDPUTENV == 2 */ + +int +putenv(env) + char *env; +{ + char *p; + int l; + char nbuf[100]; + + p = strchr(env, '='); + if (p == NULL) + return 0; + l = p - env; + if (l > sizeof nbuf - 1) + l = sizeof nbuf - 1; + memmove(nbuf, env, l); + nbuf[l] = '\0'; + return setenv(nbuf, ++p, 1); +} + +# endif /* NEEDPUTENV == 2 */ +#endif /* NEEDPUTENV */ +/* +** UNSETENV -- remove a variable from the environment +** +** Not needed on newer systems. +** +** Parameters: +** name -- the string name of the environment variable to be +** deleted from the current environment. +** +** Returns: +** none. +** +** Globals: +** environ -- a pointer to the current environment. +** +** Side Effects: +** Modifies environ. +*/ + +#if !HASUNSETENV + +void +unsetenv(name) + char *name; +{ + extern char **environ; + register char **pp; + int len = strlen(name); + + for (pp = environ; *pp != NULL; pp++) + { + if (strncmp(name, *pp, len) == 0 && + ((*pp)[len] == '=' || (*pp)[len] == '\0')) + break; + } + + for (; *pp != NULL; pp++) + *pp = pp[1]; +} + +#endif /* !HASUNSETENV */ + +char *SmCompileOptions[] = +{ +#if SM_CONF_BROKEN_STRTOD + "SM_CONF_BROKEN_STRTOD", +#endif /* SM_CONF_BROKEN_STRTOD */ +#if SM_CONF_GETOPT + "SM_CONF_GETOPT", +#endif /* SM_CONF_GETOPT */ +#if SM_CONF_LDAP_INITIALIZE + "SM_CONF_LDAP_INITIALIZE", +#endif /* SM_CONF_LDAP_INITIALIZE */ +#if SM_CONF_LDAP_MEMFREE + "SM_CONF_LDAP_MEMFREE", +#endif /* SM_CONF_LDAP_MEMFREE */ +#if SM_CONF_LONGLONG + "SM_CONF_LONGLONG", +#endif /* SM_CONF_LONGLONG */ +#if SM_CONF_MEMCHR + "SM_CONF_MEMCHR", +#endif /* SM_CONF_MEMCHR */ +#if SM_CONF_MSG + "SM_CONF_MSG", +#endif /* SM_CONF_MSG */ +#if SM_CONF_QUAD_T + "SM_CONF_QUAD_T", +#endif /* SM_CONF_QUAD_T */ +#if SM_CONF_SEM + "SM_CONF_SEM", +#endif /* SM_CONF_SEM */ +#if SM_CONF_SETITIMER + "SM_CONF_SETITIMER", +#endif /* SM_CONF_SETITIMER */ +#if SM_CONF_SIGSETJMP + "SM_CONF_SIGSETJMP", +#endif /* SM_CONF_SIGSETJMP */ +#if SM_CONF_SHM + "SM_CONF_SHM", +#endif /* SM_CONF_SHM */ +#if SM_CONF_SHM_DELAY + "SM_CONF_SHM_DELAY", +#endif /* SM_CONF_SHM_DELAY */ +#if SM_CONF_SSIZE_T + "SM_CONF_SSIZE_T", +#endif /* SM_CONF_SSIZE_T */ +#if SM_CONF_STDBOOL_H + "SM_CONF_STDBOOL_H", +#endif /* SM_CONF_STDBOOL_H */ +#if SM_CONF_STDDEF_H + "SM_CONF_STDDEF_H", +#endif /* SM_CONF_STDDEF_H */ + +#if 0 +/* XXX this is always enabled (for now) */ +#if SM_CONF_STRL + "SM_CONF_STRL", +#endif /* SM_CONF_STRL */ +#endif /* 0 */ + +#if SM_CONF_SYS_CDEFS_H + "SM_CONF_SYS_CDEFS_H", +#endif /* SM_CONF_SYS_CDEFS_H */ +#if SM_CONF_SYSEXITS_H + "SM_CONF_SYSEXITS_H", +#endif /* SM_CONF_SYSEXITS_H */ +#if SM_CONF_UID_GID + "SM_CONF_UID_GID", +#endif /* SM_CONF_UID_GID */ +#if DO_NOT_USE_STRCPY + "DO_NOT_USE_STRCPY", +#endif /* DO_NOT_USE_STRCPY */ +#if SM_HEAP_CHECK + "SM_HEAP_CHECK", +#endif /* SM_HEAP_CHECK */ +#if defined(SM_OS_NAME) && defined(__STDC__) + "SM_OS=sm_os_" SM_OS_NAME, +#endif /* defined(SM_OS_NAME) && defined(__STDC__) */ +#if SM_VA_STD + "SM_VA_STD", +#endif /* SM_VA_STD */ + NULL +}; diff --git a/usr/src/cmd/sendmail/libsm/debug.c b/usr/src/cmd/sendmail/libsm/debug.c new file mode 100644 index 0000000000..1bc7e3ad2d --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/debug.c @@ -0,0 +1,396 @@ +/* + * Copyright (c) 2000, 2001, 2003, 2004 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: debug.c,v 1.30 2004/08/03 20:10:26 ca Exp $") + +/* +** libsm debugging and tracing +** For documentation, see debug.html. +*/ + +#include <ctype.h> +#include <stdlib.h> +#include <setjmp.h> +#include <sm/io.h> +#include <sm/assert.h> +#include <sm/conf.h> +#include <sm/debug.h> +#include <sm/string.h> +#include <sm/varargs.h> +#include <sm/heap.h> + +static void sm_debug_reset __P((void)); +static const char *parse_named_setting_x __P((const char *)); + +/* +** Abstractions for printing trace messages. +*/ + +/* +** The output file to which trace output is directed. +** There is a controversy over whether this variable +** should be process global or thread local. +** To make the interface more abstract, we've hidden the +** variable behind access functions. +*/ + +static SM_FILE_T *SmDebugOutput = smioout; + +/* +** SM_DEBUG_FILE -- Returns current debug file pointer. +** +** Parameters: +** none. +** +** Returns: +** current debug file pointer. +*/ + +SM_FILE_T * +sm_debug_file() +{ + return SmDebugOutput; +} + +/* +** SM_DEBUG_SETFILE -- Sets debug file pointer. +** +** Parameters: +** fp -- new debug file pointer. +** +** Returns: +** none. +** +** Side Effects: +** Sets SmDebugOutput. +*/ + +void +sm_debug_setfile(fp) + SM_FILE_T *fp; +{ + SmDebugOutput = fp; +} + +/* +** SM_DEBUG_CLOSE -- Close debug file pointer. +** +** Parameters: +** none. +** +** Returns: +** none. +** +** Side Effects: +** Closes SmDebugOutput. +*/ + +void +sm_debug_close() +{ + if (SmDebugOutput != NULL && SmDebugOutput != smioout) + { + sm_io_close(SmDebugOutput, SM_TIME_DEFAULT); + SmDebugOutput = NULL; + } +} + +/* +** SM_DPRINTF -- printf() for debug output. +** +** Parameters: +** fmt -- format for printf() +** +** Returns: +** none. +*/ + +void +#if SM_VA_STD +sm_dprintf(char *fmt, ...) +#else /* SM_VA_STD */ +sm_dprintf(fmt, va_alist) + char *fmt; + va_dcl +#endif /* SM_VA_STD */ +{ + SM_VA_LOCAL_DECL + + if (SmDebugOutput == NULL) + return; + SM_VA_START(ap, fmt); + sm_io_vfprintf(SmDebugOutput, SmDebugOutput->f_timeout, fmt, ap); + SM_VA_END(ap); +} + +/* +** SM_DFLUSH -- Flush debug output. +** +** Parameters: +** none. +** +** Returns: +** none. +*/ + +void +sm_dflush() +{ + sm_io_flush(SmDebugOutput, SM_TIME_DEFAULT); +} + +/* +** This is the internal database of debug settings. +** The semantics of looking up a setting in the settings database +** are that the *last* setting specified in a -d option on the sendmail +** command line that matches a given SM_DEBUG structure is the one that is +** used. That is necessary to conform to the existing semantics of +** the sendmail -d option. We store the settings as a linked list in +** reverse order, so when we do a lookup, we take the *first* entry +** that matches. +*/ + +typedef struct sm_debug_setting SM_DEBUG_SETTING_T; +struct sm_debug_setting +{ + const char *ds_pattern; + unsigned int ds_level; + SM_DEBUG_SETTING_T *ds_next; +}; +SM_DEBUG_SETTING_T *SmDebugSettings = NULL; + +/* +** We keep a linked list of SM_DEBUG structures that have been initialized, +** for use by sm_debug_reset. +*/ + +SM_DEBUG_T *SmDebugInitialized = NULL; + +const char SmDebugMagic[] = "sm_debug"; + +/* +** SM_DEBUG_RESET -- Reset SM_DEBUG structures. +** +** Reset all SM_DEBUG structures back to the uninitialized state. +** This is used by sm_debug_addsetting to ensure that references to +** SM_DEBUG structures that occur before sendmail processes its -d flags +** do not cause those structures to be permanently forced to level 0. +** +** Parameters: +** none. +** +** Returns: +** none. +*/ + +static void +sm_debug_reset() +{ + SM_DEBUG_T *debug; + + for (debug = SmDebugInitialized; + debug != NULL; + debug = debug->debug_next) + { + debug->debug_level = SM_DEBUG_UNKNOWN; + } + SmDebugInitialized = NULL; +} + +/* +** SM_DEBUG_ADDSETTING_X -- add an entry to the database of debug settings +** +** Parameters: +** pattern -- a shell-style glob pattern (see sm_match). +** WARNING: the storage for 'pattern' will be owned by +** the debug package, so it should either be a string +** literal or the result of a call to sm_strdup_x. +** level -- a non-negative integer. +** +** Returns: +** none. +** +** Exceptions: +** F:sm_heap -- out of memory +*/ + +void +sm_debug_addsetting_x(pattern, level) + const char *pattern; + int level; +{ + SM_DEBUG_SETTING_T *s; + + SM_REQUIRE(pattern != NULL); + SM_REQUIRE(level >= 0); + s = sm_malloc_x(sizeof(SM_DEBUG_SETTING_T)); + s->ds_pattern = pattern; + s->ds_level = (unsigned int) level; + s->ds_next = SmDebugSettings; + SmDebugSettings = s; + sm_debug_reset(); +} + +/* +** PARSE_NAMED_SETTING_X -- process a symbolic debug setting +** +** Parameters: +** s -- Points to a non-empty \0 or , terminated string, +** of which the initial character is not a digit. +** +** Returns: +** pointer to terminating \0 or , character. +** +** Exceptions: +** F:sm.heap -- out of memory. +** +** Side Effects: +** adds the setting to the database. +*/ + +static const char * +parse_named_setting_x(s) + const char *s; +{ + const char *pat, *endpat; + int level; + + pat = s; + while (*s != '\0' && *s != ',' && *s != '.') + ++s; + endpat = s; + if (*s == '.') + { + ++s; + level = 0; + while (isascii(*s) && isdigit(*s)) + { + level = level * 10 + (*s - '0'); + ++s; + } + if (level < 0) + level = 0; + } + else + level = 1; + + sm_debug_addsetting_x(sm_strndup_x(pat, endpat - pat), level); + + /* skip trailing junk */ + while (*s != '\0' && *s != ',') + ++s; + + return s; +} + +/* +** SM_DEBUG_ADDSETTINGS_X -- process a list of debug options +** +** Parameters: +** s -- a list of debug settings, eg the argument to the +** sendmail -d option. +** +** The syntax of the string s is as follows: +** +** <settings> ::= <setting> | <settings> "," <setting> +** <setting> ::= <categories> | <categories> "." <level> +** <categories> ::= [a-zA-Z_*?][a-zA-Z0-9_*?]* +** +** However, note that we skip over anything we don't +** understand, rather than report an error. +** +** Returns: +** none. +** +** Exceptions: +** F:sm.heap -- out of memory +** +** Side Effects: +** updates the database of debug settings. +*/ + +void +sm_debug_addsettings_x(s) + const char *s; +{ + for (;;) + { + if (*s == '\0') + return; + if (*s == ',') + { + ++s; + continue; + } + s = parse_named_setting_x(s); + } +} + +/* +** SM_DEBUG_LOADLEVEL -- Get activation level of the specified debug object. +** +** Parameters: +** debug -- debug object. +** +** Returns: +** Activation level of the specified debug object. +** +** Side Effects: +** Ensures that the debug object is initialized. +*/ + +int +sm_debug_loadlevel(debug) + SM_DEBUG_T *debug; +{ + if (debug->debug_level == SM_DEBUG_UNKNOWN) + { + SM_DEBUG_SETTING_T *s; + + for (s = SmDebugSettings; s != NULL; s = s->ds_next) + { + if (sm_match(debug->debug_name, s->ds_pattern)) + { + debug->debug_level = s->ds_level; + goto initialized; + } + } + debug->debug_level = 0; + initialized: + debug->debug_next = SmDebugInitialized; + SmDebugInitialized = debug; + } + return (int) debug->debug_level; +} + +/* +** SM_DEBUG_LOADACTIVE -- Activation level reached? +** +** Parameters: +** debug -- debug object. +** level -- level to check. +** +** Returns: +** true iff the activation level of the specified debug +** object >= level. +** +** Side Effects: +** Ensures that the debug object is initialized. +*/ + +bool +sm_debug_loadactive(debug, level) + SM_DEBUG_T *debug; + int level; +{ + return sm_debug_loadlevel(debug) >= level; +} diff --git a/usr/src/cmd/sendmail/libsm/errstring.c b/usr/src/cmd/sendmail/libsm/errstring.c new file mode 100644 index 0000000000..ef2fb3749b --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/errstring.c @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2001, 2003 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: errstring.c,v 1.19 2003/12/10 03:53:05 gshapiro Exp $") + +#include <errno.h> +#include <stdio.h> /* sys_errlist, on some platforms */ + +#include <sm/io.h> /* sm_snprintf */ +#include <sm/string.h> +#include <sm/errstring.h> + +#if NAMED_BIND +# include <netdb.h> +#endif + +#if LDAPMAP +# include <lber.h> +# include <ldap.h> /* for LDAP error codes */ +#endif /* LDAPMAP */ + +/* +** Notice: this file is used by libmilter. Please try to avoid +** using libsm specific functions. +*/ + +/* +** SM_ERRSTRING -- return string description of error code +** +** Parameters: +** errnum -- the error number to translate +** +** Returns: +** A string description of errnum. +** +** Note: this may point to a local (static) buffer. +*/ + +const char * +sm_errstring(errnum) + int errnum; +{ + char *ret; + + + switch (errnum) + { + case EPERM: + /* SunOS gives "Not owner" -- this is the POSIX message */ + return "Operation not permitted"; + + /* + ** Error messages used internally in sendmail. + */ + + case E_SM_OPENTIMEOUT: + return "Timeout on file open"; + + case E_SM_NOSLINK: + return "Symbolic links not allowed"; + + case E_SM_NOHLINK: + return "Hard links not allowed"; + + case E_SM_REGONLY: + return "Regular files only"; + + case E_SM_ISEXEC: + return "Executable files not allowed"; + + case E_SM_WWDIR: + return "World writable directory"; + + case E_SM_GWDIR: + return "Group writable directory"; + + case E_SM_FILECHANGE: + return "File changed after open"; + + case E_SM_WWFILE: + return "World writable file"; + + case E_SM_GWFILE: + return "Group writable file"; + + case E_SM_GRFILE: + return "Group readable file"; + + case E_SM_WRFILE: + return "World readable file"; + + /* + ** DNS error messages. + */ + +#if NAMED_BIND + case HOST_NOT_FOUND + E_DNSBASE: + return "Name server: host not found"; + + case TRY_AGAIN + E_DNSBASE: + return "Name server: host name lookup failure"; + + case NO_RECOVERY + E_DNSBASE: + return "Name server: non-recoverable error"; + + case NO_DATA + E_DNSBASE: + return "Name server: no data known"; +#endif /* NAMED_BIND */ + + /* + ** libsmdb error messages. + */ + + case SMDBE_MALLOC: + return "Memory allocation failed"; + + case SMDBE_GDBM_IS_BAD: + return "GDBM is not supported"; + + case SMDBE_UNSUPPORTED: + return "Unsupported action"; + + case SMDBE_DUPLICATE: + return "Key already exists"; + + case SMDBE_BAD_OPEN: + return "Database open failed"; + + case SMDBE_NOT_FOUND: + return "Key not found"; + + case SMDBE_UNKNOWN_DB_TYPE: + return "Unknown database type"; + + case SMDBE_UNSUPPORTED_DB_TYPE: + return "Support for database type not compiled into this program"; + + case SMDBE_INCOMPLETE: + return "DB sync did not finish"; + + case SMDBE_KEY_EMPTY: + return "Key is empty"; + + case SMDBE_KEY_EXIST: + return "Key already exists"; + + case SMDBE_LOCK_DEADLOCK: + return "Locker killed to resolve deadlock"; + + case SMDBE_LOCK_NOT_GRANTED: + return "Lock unavailable"; + + case SMDBE_LOCK_NOT_HELD: + return "Lock not held by locker"; + + case SMDBE_RUN_RECOVERY: + return "Database panic, run recovery"; + + case SMDBE_IO_ERROR: + return "I/O error"; + + case SMDBE_READ_ONLY: + return "Database opened read-only"; + + case SMDBE_DB_NAME_TOO_LONG: + return "Name too long"; + + case SMDBE_INVALID_PARAMETER: + return "Invalid parameter"; + + case SMDBE_ONLY_SUPPORTS_ONE_CURSOR: + return "Only one cursor allowed"; + + case SMDBE_NOT_A_VALID_CURSOR: + return "Invalid cursor"; + + case SMDBE_OLD_VERSION: + return "Berkeley DB file is an old version, recreate it"; + + case SMDBE_VERSION_MISMATCH: + return "Berkeley DB version mismatch between include file and library"; + +#if LDAPMAP + + /* + ** LDAP URL error messages. + */ + + /* OpenLDAP errors */ +# ifdef LDAP_URL_ERR_MEM + case E_LDAPURLBASE + LDAP_URL_ERR_MEM: + return "LDAP URL can't allocate memory space"; +# endif /* LDAP_URL_ERR_MEM */ + +# ifdef LDAP_URL_ERR_PARAM + case E_LDAPURLBASE + LDAP_URL_ERR_PARAM: + return "LDAP URL parameter is bad"; +# endif /* LDAP_URL_ERR_PARAM */ + +# ifdef LDAP_URL_ERR_BADSCHEME + case E_LDAPURLBASE + LDAP_URL_ERR_BADSCHEME: + return "LDAP URL doesn't begin with \"ldap[si]://\""; +# endif /* LDAP_URL_ERR_BADSCHEME */ + +# ifdef LDAP_URL_ERR_BADENCLOSURE + case E_LDAPURLBASE + LDAP_URL_ERR_BADENCLOSURE: + return "LDAP URL is missing trailing \">\""; +# endif /* LDAP_URL_ERR_BADENCLOSURE */ + +# ifdef LDAP_URL_ERR_BADURL + case E_LDAPURLBASE + LDAP_URL_ERR_BADURL: + return "LDAP URL is bad"; +# endif /* LDAP_URL_ERR_BADURL */ + +# ifdef LDAP_URL_ERR_BADHOST + case E_LDAPURLBASE + LDAP_URL_ERR_BADHOST: + return "LDAP URL host port is bad"; +# endif /* LDAP_URL_ERR_BADHOST */ + +# ifdef LDAP_URL_ERR_BADATTRS + case E_LDAPURLBASE + LDAP_URL_ERR_BADATTRS: + return "LDAP URL bad (or missing) attributes"; +# endif /* LDAP_URL_ERR_BADATTRS */ + +# ifdef LDAP_URL_ERR_BADSCOPE + case E_LDAPURLBASE + LDAP_URL_ERR_BADSCOPE: + return "LDAP URL scope string is invalid (or missing)"; +# endif /* LDAP_URL_ERR_BADSCOPE */ + +# ifdef LDAP_URL_ERR_BADFILTER + case E_LDAPURLBASE + LDAP_URL_ERR_BADFILTER: + return "LDAP URL bad or missing filter"; +# endif /* LDAP_URL_ERR_BADFILTER */ + +# ifdef LDAP_URL_ERR_BADEXTS + case E_LDAPURLBASE + LDAP_URL_ERR_BADEXTS: + return "LDAP URL bad or missing extensions"; +# endif /* LDAP_URL_ERR_BADEXTS */ + + /* Sun LDAP errors */ +# ifdef LDAP_URL_ERR_NOTLDAP + case E_LDAPURLBASE + LDAP_URL_ERR_NOTLDAP: + return "LDAP URL doesn't begin with \"ldap://\""; +# endif /* LDAP_URL_ERR_NOTLDAP */ + +# ifdef LDAP_URL_ERR_NODN + case E_LDAPURLBASE + LDAP_URL_ERR_NODN: + return "LDAP URL has no DN (required)"; +# endif /* LDAP_URL_ERR_NODN */ + +#endif /* LDAPMAP */ + } + +#if LDAPMAP + + /* + ** LDAP error messages. + */ + + if (errnum >= E_LDAPBASE) + return ldap_err2string(errnum - E_LDAPBASE); +#endif /* LDAPMAP */ + + ret = strerror(errnum); + if (ret == NULL) + { + static char buf[30]; + + (void) sm_snprintf(buf, sizeof buf, "Error %d", errnum); + return buf; + } + return ret; +} diff --git a/usr/src/cmd/sendmail/libsm/exc.c b/usr/src/cmd/sendmail/libsm/exc.c new file mode 100644 index 0000000000..42e4da7924 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/exc.c @@ -0,0 +1,671 @@ +/* + * Copyright (c) 2000-2002 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: exc.c,v 1.47.2.1 2003/12/05 22:44:17 ca Exp $") + +/* +** exception handling +** For documentation, see exc.html +*/ + +#include <ctype.h> +#include <string.h> + +#include <sm/errstring.h> +#include <sm/exc.h> +#include <sm/heap.h> +#include <sm/string.h> +#include <sm/varargs.h> +#include <sm/io.h> + +const char SmExcMagic[] = "sm_exc"; +const char SmExcTypeMagic[] = "sm_exc_type"; + +/* +** SM_ETYPE_PRINTF -- printf for exception types. +** +** Parameters: +** exc -- exception. +** stream -- file for output. +** +** Returns: +** none. +*/ + +/* +** A simple formatted print function that can be used as the print function +** by most exception types. It prints the printcontext string, interpreting +** occurrences of %0 through %9 as references to the argument vector. +** If exception argument 3 is an int or long, then %3 will print the +** argument in decimal, and %o3 or %x3 will print it in octal or hex. +*/ + +void +sm_etype_printf(exc, stream) + SM_EXC_T *exc; + SM_FILE_T *stream; +{ + size_t n = strlen(exc->exc_type->etype_argformat); + const char *p, *s; + char format; + + for (p = exc->exc_type->etype_printcontext; *p != '\0'; ++p) + { + if (*p != '%') + { + (void) sm_io_putc(stream, SM_TIME_DEFAULT, *p); + continue; + } + ++p; + if (*p == '\0') + { + (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%'); + break; + } + if (*p == '%') + { + (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%'); + continue; + } + format = '\0'; + if (isalpha(*p)) + { + format = *p++; + if (*p == '\0') + { + (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%'); + (void) sm_io_putc(stream, SM_TIME_DEFAULT, + format); + break; + } + } + if (isdigit(*p)) + { + size_t i = *p - '0'; + if (i < n) + { + switch (exc->exc_type->etype_argformat[i]) + { + case 's': + case 'r': + s = exc->exc_argv[i].v_str; + if (s == NULL) + s = "(null)"; + sm_io_fputs(stream, SM_TIME_DEFAULT, s); + continue; + case 'i': + sm_io_fprintf(stream, + SM_TIME_DEFAULT, + format == 'o' ? "%o" + : format == 'x' ? "%x" + : "%d", + exc->exc_argv[i].v_int); + continue; + case 'l': + sm_io_fprintf(stream, + SM_TIME_DEFAULT, + format == 'o' ? "%lo" + : format == 'x' ? "%lx" + : "%ld", + exc->exc_argv[i].v_long); + continue; + case 'e': + sm_exc_write(exc->exc_argv[i].v_exc, + stream); + continue; + } + } + } + (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%'); + if (format) + (void) sm_io_putc(stream, SM_TIME_DEFAULT, format); + (void) sm_io_putc(stream, SM_TIME_DEFAULT, *p); + } +} + +/* +** Standard exception types. +*/ + +/* +** SM_ETYPE_OS_PRINT -- Print OS related exception. +** +** Parameters: +** exc -- exception. +** stream -- file for output. +** +** Returns: +** none. +*/ + +static void +sm_etype_os_print __P(( + SM_EXC_T *exc, + SM_FILE_T *stream)); + +static void +sm_etype_os_print(exc, stream) + SM_EXC_T *exc; + SM_FILE_T *stream; +{ + int err = exc->exc_argv[0].v_int; + char *syscall = exc->exc_argv[1].v_str; + char *sysargs = exc->exc_argv[2].v_str; + + if (sysargs) + sm_io_fprintf(stream, SM_TIME_DEFAULT, "%s: %s failed: %s", + sysargs, syscall, sm_errstring(err)); + else + sm_io_fprintf(stream, SM_TIME_DEFAULT, "%s failed: %s", syscall, + sm_errstring(err)); +} + +/* +** SmEtypeOs represents the failure of a Unix system call. +** The three arguments are: +** int errno (eg, ENOENT) +** char *syscall (eg, "open") +** char *sysargs (eg, NULL or "/etc/mail/sendmail.cf") +*/ + +const SM_EXC_TYPE_T SmEtypeOs = +{ + SmExcTypeMagic, + "E:sm.os", + "isr", + sm_etype_os_print, + NULL, +}; + +/* +** SmEtypeErr is a completely generic error which should only be +** used in applications and test programs. Libraries should use +** more specific exception codes. +*/ + +const SM_EXC_TYPE_T SmEtypeErr = +{ + SmExcTypeMagic, + "E:sm.err", + "r", + sm_etype_printf, + "%0", +}; + +/* +** SM_EXC_VNEW_X -- Construct a new exception object. +** +** Parameters: +** etype -- type of exception. +** ap -- varargs. +** +** Returns: +** pointer to exception object. +*/ + +/* +** This is an auxiliary function called by sm_exc_new_x and sm_exc_raisenew_x. +** +** If an exception is raised, then to avoid a storage leak, we must: +** (a) Free all storage we have allocated. +** (b) Free all exception arguments in the varargs list. +** Getting this right is tricky. +** +** To see why (b) is required, consider the code fragment +** SM_EXCEPT(exc, "*") +** sm_exc_raisenew_x(&MyEtype, exc); +** SM_END_TRY +** In the normal case, sm_exc_raisenew_x will allocate and raise a new +** exception E that owns exc. When E is eventually freed, exc is also freed. +** In the exceptional case, sm_exc_raisenew_x must free exc before raising +** an out-of-memory exception so that exc is not leaked. +*/ + +SM_EXC_T * +sm_exc_vnew_x(etype, ap) + const SM_EXC_TYPE_T *etype; + va_list SM_NONVOLATILE ap; +{ + /* + ** All variables that are modified in the SM_TRY clause and + ** referenced in the SM_EXCEPT clause must be declared volatile. + */ + + /* NOTE: Type of si, i, and argc *must* match */ + SM_EXC_T * volatile exc = NULL; + int volatile si = 0; + SM_VAL_T * volatile argv = NULL; + int i, argc; + + SM_REQUIRE_ISA(etype, SmExcTypeMagic); + argc = strlen(etype->etype_argformat); + SM_TRY + { + /* + ** Step 1. Allocate the exception structure. + ** On failure, scan the varargs list and free all + ** exception arguments. + */ + + exc = sm_malloc_x(sizeof(SM_EXC_T)); + exc->sm_magic = SmExcMagic; + exc->exc_refcount = 1; + exc->exc_type = etype; + exc->exc_argv = NULL; + + /* + ** Step 2. Allocate the argument vector. + ** On failure, free exc, scan the varargs list and free all + ** exception arguments. On success, scan the varargs list, + ** and copy the arguments into argv. + */ + + argv = sm_malloc_x(argc * sizeof(SM_VAL_T)); + exc->exc_argv = argv; + for (i = 0; i < argc; ++i) + { + switch (etype->etype_argformat[i]) + { + case 'i': + argv[i].v_int = SM_VA_ARG(ap, int); + break; + case 'l': + argv[i].v_long = SM_VA_ARG(ap, long); + break; + case 'e': + argv[i].v_exc = SM_VA_ARG(ap, SM_EXC_T*); + break; + case 's': + argv[i].v_str = SM_VA_ARG(ap, char*); + break; + case 'r': + SM_REQUIRE(etype->etype_argformat[i+1] == '\0'); + argv[i].v_str = SM_VA_ARG(ap, char*); + break; + default: + sm_abort("sm_exc_vnew_x: bad argformat '%c'", + etype->etype_argformat[i]); + } + } + + /* + ** Step 3. Scan argv, and allocate space for all + ** string arguments. si is the number of elements + ** of argv that have been processed so far. + ** On failure, free exc, argv, all the exception arguments + ** and all of the strings that have been copied. + */ + + for (si = 0; si < argc; ++si) + { + switch (etype->etype_argformat[si]) + { + case 's': + { + char *str = argv[si].v_str; + if (str != NULL) + argv[si].v_str = sm_strdup_x(str); + } + break; + case 'r': + { + char *fmt = argv[si].v_str; + if (fmt != NULL) + argv[si].v_str = sm_vstringf_x(fmt, ap); + } + break; + } + } + } + SM_EXCEPT(e, "*") + { + if (exc == NULL || argv == NULL) + { + /* + ** Failure in step 1 or step 2. + ** Scan ap and free all exception arguments. + */ + + for (i = 0; i < argc; ++i) + { + switch (etype->etype_argformat[i]) + { + case 'i': + (void) SM_VA_ARG(ap, int); + break; + case 'l': + (void) SM_VA_ARG(ap, long); + break; + case 'e': + sm_exc_free(SM_VA_ARG(ap, SM_EXC_T*)); + break; + case 's': + case 'r': + (void) SM_VA_ARG(ap, char*); + break; + } + } + } + else + { + /* + ** Failure in step 3. Scan argv and free + ** all exception arguments and all string + ** arguments that have been duplicated. + ** Then free argv. + */ + + for (i = 0; i < argc; ++i) + { + switch (etype->etype_argformat[i]) + { + case 'e': + sm_exc_free(argv[i].v_exc); + break; + case 's': + case 'r': + if (i < si) + sm_free(argv[i].v_str); + break; + } + } + sm_free(argv); + } + sm_free(exc); + sm_exc_raise_x(e); + } + SM_END_TRY + + return exc; +} + +/* +** SM_EXC_NEW_X -- Construct a new exception object. +** +** Parameters: +** etype -- type of exception. +** ... -- varargs. +** +** Returns: +** pointer to exception object. +*/ + +SM_EXC_T * +#if SM_VA_STD +sm_exc_new_x( + const SM_EXC_TYPE_T *etype, + ...) +#else /* SM_VA_STD */ +sm_exc_new_x(etype, va_alist) + const SM_EXC_TYPE_T *etype; + va_dcl +#endif /* SM_VA_STD */ +{ + SM_EXC_T *exc; + SM_VA_LOCAL_DECL + + SM_VA_START(ap, etype); + exc = sm_exc_vnew_x(etype, ap); + SM_VA_END(ap); + return exc; +} + +/* +** SM_ADDREF -- Add a reference to an exception object. +** +** Parameters: +** exc -- exception object. +** +** Returns: +** exc itself. +*/ + +SM_EXC_T * +sm_addref(exc) + SM_EXC_T *exc; +{ + SM_REQUIRE_ISA(exc, SmExcMagic); + if (exc->exc_refcount != 0) + ++exc->exc_refcount; + return exc; +} + +/* +** SM_EXC_FREE -- Destroy a reference to an exception object. +** +** Parameters: +** exc -- exception object. +** +** Returns: +** none. +*/ + +void +sm_exc_free(exc) + SM_EXC_T *exc; +{ + if (exc == NULL) + return; + SM_REQUIRE(exc->sm_magic == SmExcMagic); + if (exc->exc_refcount == 0) + return; + if (--exc->exc_refcount == 0) + { + int i, c; + + for (i = 0; (c = exc->exc_type->etype_argformat[i]) != '\0'; + ++i) + { + switch (c) + { + case 's': + case 'r': + sm_free(exc->exc_argv[i].v_str); + break; + case 'e': + sm_exc_free(exc->exc_argv[i].v_exc); + break; + } + } + exc->sm_magic = NULL; + sm_free(exc->exc_argv); + sm_free(exc); + } +} + +/* +** SM_EXC_MATCH -- Match exception category against a glob pattern. +** +** Parameters: +** exc -- exception. +** pattern -- glob pattern. +** +** Returns: +** true iff match. +*/ + +bool +sm_exc_match(exc, pattern) + SM_EXC_T *exc; + const char *pattern; +{ + if (exc == NULL) + return false; + SM_REQUIRE(exc->sm_magic == SmExcMagic); + return sm_match(exc->exc_type->etype_category, pattern); +} + +/* +** SM_EXC_WRITE -- Write exception message to a stream (wo trailing newline). +** +** Parameters: +** exc -- exception. +** stream -- file for output. +** +** Returns: +** none. +*/ + +void +sm_exc_write(exc, stream) + SM_EXC_T *exc; + SM_FILE_T *stream; +{ + SM_REQUIRE_ISA(exc, SmExcMagic); + exc->exc_type->etype_print(exc, stream); +} + +/* +** SM_EXC_PRINT -- Print exception message to a stream (with trailing newline). +** +** Parameters: +** exc -- exception. +** stream -- file for output. +** +** Returns: +** none. +*/ + +void +sm_exc_print(exc, stream) + SM_EXC_T *exc; + SM_FILE_T *stream; +{ + SM_REQUIRE_ISA(exc, SmExcMagic); + exc->exc_type->etype_print(exc, stream); + (void) sm_io_putc(stream, SM_TIME_DEFAULT, '\n'); +} + +SM_EXC_HANDLER_T *SmExcHandler = NULL; +static SM_EXC_DEFAULT_HANDLER_T SmExcDefaultHandler = NULL; + +/* +** SM_EXC_NEWTHREAD -- Initialize exception handling for new process/thread. +** +** Parameters: +** h -- default exception handler. +** +** Returns: +** none. +*/ + +/* +** Initialize a new process or a new thread by clearing the +** exception handler stack and optionally setting a default +** exception handler function. Call this at the beginning of main, +** or in a new process after calling fork, or in a new thread. +** +** This function is a luxury, not a necessity. +** If h != NULL then you can get the same effect by +** wrapping the body of main, or the body of a forked child +** or a new thread in SM_TRY ... SM_EXCEPT(e,"*") h(e); SM_END_TRY. +*/ + +void +sm_exc_newthread(h) + SM_EXC_DEFAULT_HANDLER_T h; +{ + SmExcHandler = NULL; + SmExcDefaultHandler = h; +} + +/* +** SM_EXC_RAISE_X -- Raise an exception. +** +** Parameters: +** exc -- exception. +** +** Returns: +** doesn't. +*/ + +void SM_DEAD_D +sm_exc_raise_x(exc) + SM_EXC_T *exc; +{ + SM_REQUIRE_ISA(exc, SmExcMagic); + + if (SmExcHandler == NULL) + { + if (SmExcDefaultHandler != NULL) + { + SM_EXC_DEFAULT_HANDLER_T h; + + /* + ** If defined, the default handler is expected + ** to terminate the current thread of execution + ** using exit() or pthread_exit(). + ** If it instead returns normally, then we fall + ** through to the default case below. If it + ** raises an exception, then sm_exc_raise_x is + ** re-entered and, because we set SmExcDefaultHandler + ** to NULL before invoking h, we will again + ** end up in the default case below. + */ + + h = SmExcDefaultHandler; + SmExcDefaultHandler = NULL; + (*h)(exc); + } + + /* + ** No exception handler, so print the error and exit. + ** To override this behaviour on a program wide basis, + ** call sm_exc_newthread or put an exception handler in main(). + ** + ** XXX TODO: map the exception category to an exit code + ** XXX from <sysexits.h>. + */ + + sm_exc_print(exc, smioerr); + exit(255); + } + + if (SmExcHandler->eh_value == NULL) + SmExcHandler->eh_value = exc; + else + sm_exc_free(exc); + + sm_longjmp_nosig(SmExcHandler->eh_context, 1); +} + +/* +** SM_EXC_RAISENEW_X -- shorthand for sm_exc_raise_x(sm_exc_new_x(...)) +** +** Parameters: +** etype -- type of exception. +** ap -- varargs. +** +** Returns: +** none. +*/ + +void SM_DEAD_D +#if SM_VA_STD +sm_exc_raisenew_x( + const SM_EXC_TYPE_T *etype, + ...) +#else +sm_exc_raisenew_x(etype, va_alist) + const SM_EXC_TYPE_T *etype; + va_dcl +#endif +{ + SM_EXC_T *exc; + SM_VA_LOCAL_DECL + + SM_VA_START(ap, etype); + exc = sm_exc_vnew_x(etype, ap); + SM_VA_END(ap); + sm_exc_raise_x(exc); +} diff --git a/usr/src/cmd/sendmail/libsm/fclose.c b/usr/src/cmd/sendmail/libsm/fclose.c new file mode 100644 index 0000000000..d540d99e6f --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/fclose.c @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2000-2002, 2004 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: fclose.c,v 1.43 2004/08/03 20:17:38 ca Exp $") +#include <errno.h> +#include <stdlib.h> +#include <sys/time.h> +#include <setjmp.h> +#include <sm/io.h> +#include <sm/assert.h> +#include <sm/heap.h> +#include <sm/signal.h> +#include <sm/conf.h> +#include <sm/clock.h> +#include "local.h" + +static void closealrm __P((int)); +static jmp_buf CloseTimeOut; + +/* +** CLOSEALRM -- handler when timeout activated for sm_io_close() +** +** Returns flow of control to where setjmp(CloseTimeOut) was set. +** +** Parameters: +** sig -- unused +** +** Returns: +** does not return +** +** Side Effects: +** returns flow of control to setjmp(CloseTimeOut). +** +** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD +** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE +** DOING. +*/ + +/* ARGSUSED0 */ +static void +closealrm(sig) + int sig; +{ + longjmp(CloseTimeOut, 1); +} + +/* +** SM_IO_CLOSE -- close a file handle/pointer +** +** Parameters: +** fp -- file pointer to be closed +** timeout -- maximum time allowed to perform the close (millisecs) +** +** Returns: +** 0 on success +** -1 on failure and sets errno +** +** Side Effects: +** file pointer 'fp' will no longer be valid. +*/ + +int +sm_io_close(fp, timeout) + register SM_FILE_T *fp; + int SM_NONVOLATILE timeout; +{ + register int SM_NONVOLATILE r; + SM_EVENT *evt = NULL; + + if (fp == NULL) + { + errno = EBADF; + return SM_IO_EOF; + } + + SM_REQUIRE_ISA(fp, SmFileMagic); + + /* XXX this won't be reached if above macro is active */ + if (fp->sm_magic == NULL) + { + /* not open! */ + errno = EBADF; + return SM_IO_EOF; + } + if (fp->f_close == NULL) + { + /* no close function! */ + errno = ENODEV; + return SM_IO_EOF; + } + if (fp->f_dup_cnt > 0) + { + /* decrement file pointer open count */ + fp->f_dup_cnt--; + return 0; + } + + /* Okay, this is where we set the timeout. */ + if (timeout == SM_TIME_DEFAULT) + timeout = fp->f_timeout; + if (timeout == SM_TIME_IMMEDIATE) + { + errno = EAGAIN; + return -1; + } + + /* No more duplicates of file pointer. Flush buffer and close */ + r = fp->f_flags & SMWR ? sm_flush(fp, (int *) &timeout) : 0; + + /* sm_flush() has updated to.it_value for the time it's used */ + if (timeout != SM_TIME_FOREVER) + { + if (setjmp(CloseTimeOut) != 0) + { + errno = EAGAIN; + return SM_IO_EOF; + } + evt = sm_seteventm(timeout, closealrm, 0); + } + if ((*fp->f_close)(fp) < 0) + r = SM_IO_EOF; + + /* We're back. So undo our timeout and handler */ + if (evt != NULL) + sm_clrevent(evt); + if (fp->f_flags & SMMBF) + { + sm_free((char *)fp->f_bf.smb_base); + fp->f_bf.smb_base = NULL; + } + if (HASUB(fp)) + FREEUB(fp); + fp->f_flags = 0; /* clear flags */ + fp->sm_magic = NULL; /* Release this SM_FILE_T for reuse. */ + fp->f_r = fp->f_w = 0; /* Mess up if reaccessed. */ + return r; +} diff --git a/usr/src/cmd/sendmail/libsm/feof.c b/usr/src/cmd/sendmail/libsm/feof.c new file mode 100644 index 0000000000..2f1efc37d0 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/feof.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: feof.c,v 1.11 2001/04/03 01:46:40 ca Exp $") +#include <sm/io.h> +#include <sm/assert.h> +#include "local.h" + +/* +** SM_IO_EOF -- subroutine version of the macro sm_io_eof. +** +** Tests if the file for 'fp' has reached the end. +** +** Parameters: +** fp -- file pointer. +** +** Returns: +** 0 (zero) when the file is not at end +** non-zero when EOF has been found +*/ +#undef sm_io_eof + +int +sm_io_eof(fp) + SM_FILE_T *fp; +{ + SM_REQUIRE_ISA(fp, SmFileMagic); + + return sm_eof(fp); +} diff --git a/usr/src/cmd/sendmail/libsm/ferror.c b/usr/src/cmd/sendmail/libsm/ferror.c new file mode 100644 index 0000000000..08ee823810 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/ferror.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: ferror.c,v 1.11 2001/04/03 01:46:40 ca Exp $") +#include <sm/io.h> +#include <sm/assert.h> +#include "local.h" + +/* +** SM_IO_ERROR -- subroutine version of the macro sm_io_error. +** +** Parameters: +** fp -- file pointer +** +** Returns: +** 0 (zero) when 'fp' is not in an error state +** non-zero when 'fp' is in an error state +*/ + +#undef sm_io_error + +int +sm_io_error(fp) + SM_FILE_T *fp; +{ + SM_REQUIRE_ISA(fp, SmFileMagic); + + return sm_error(fp); +} diff --git a/usr/src/cmd/sendmail/libsm/fflush.c b/usr/src/cmd/sendmail/libsm/fflush.c new file mode 100644 index 0000000000..4ee32c02f4 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/fflush.c @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: fflush.c,v 1.41 2001/05/15 16:55:27 ca Exp $") +#include <unistd.h> +#include <errno.h> +#include <sys/time.h> +#include <signal.h> +#include <fcntl.h> +#include <string.h> +#include <sm/io.h> +#include <sm/assert.h> +#include <sm/setjmp.h> +#include "local.h" +#include <sm/conf.h> + +/* +** SM_IO_FLUSH -- flush the buffer for a 'fp' to the "file" +** +** Flush a single file. We don't allow this function to flush +** all open files when fp==NULL any longer. +** +** Parameters: +** fp -- the file pointer buffer to flush +** timeout -- time to complete the flush +** +** Results: +** Failure: SM_IO_EOF and sets errno +** Success: 0 (zero) +*/ + +int +sm_io_flush(fp, timeout) + register SM_FILE_T *fp; + int SM_NONVOLATILE timeout; +{ + int fd; + struct timeval to; + + SM_REQUIRE_ISA(fp, SmFileMagic); + + if ((fp->f_flags & (SMWR | SMRW)) == 0) + { + /* + ** The file is not opened for writing, so it cannot be flushed + ** (writable means SMWR [write] or SMRW [read/write]. + */ + + errno = EBADF; + return SM_IO_EOF; + } + + SM_CONVERT_TIME(fp, fd, timeout, &to); + + /* Now do the flush */ + return sm_flush(fp, (int *) &timeout); +} + +/* +** SM_FLUSH -- perform the actual flush +** +** Assumes that 'fp' has been validated before this function called. +** +** Parameters: +** fp -- file pointer to be flushed +** timeout -- max time allowed for flush (milliseconds) +** +** Results: +** Success: 0 (zero) +** Failure: SM_IO_EOF and errno set +** +** Side Effects: +** timeout will get updated with the time remaining (if any) +*/ + +int +sm_flush(fp, timeout) + register SM_FILE_T *fp; + int *timeout; +{ + register unsigned char *p; + register int n, t; + int fd; + + SM_REQUIRE_ISA(fp, SmFileMagic); + + t = fp->f_flags; + if ((t & SMWR) == 0) + return 0; + + if (t & SMSTR) + { + *fp->f_p = '\0'; + return 0; + } + + if ((p = fp->f_bf.smb_base) == NULL) + return 0; + + n = fp->f_p - p; /* write this much */ + + if ((fd = sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL)) == -1) + { + /* can't get an fd, likely internal 'fake' fp */ + errno = 0; + fd = -1; + } + + /* + ** Set these immediately to avoid problems with longjmp and to allow + ** exchange buffering (via setvbuf) in user write function. + */ + + fp->f_p = p; + fp->f_w = t & (SMLBF|SMNBF) ? 0 : fp->f_bf.smb_size; /* implies SMFBF */ + + for (; n > 0; n -= t, p += t) + { + errno = 0; /* needed to ensure EOF correctly found */ + + /* Call the file type's write function */ + t = (*fp->f_write)(fp, (char *)p, n); + if (t <= 0) + { + if (t == 0 && errno == 0) + break; /* EOF found */ + + if (IS_IO_ERROR(fd, t, *timeout)) + { + fp->f_flags |= SMERR; + + /* errno set by fp->f_write */ + return SM_IO_EOF; + } + SM_IO_WR_TIMEOUT(fp, fd, *timeout); + } + } + return 0; +} diff --git a/usr/src/cmd/sendmail/libsm/fget.c b/usr/src/cmd/sendmail/libsm/fget.c new file mode 100644 index 0000000000..96dbf71cbd --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/fget.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: fget.c,v 1.22 2001/08/27 18:54:14 gshapiro Exp $") +#include <stdlib.h> +#include <string.h> +#include <sm/io.h> +#include <sm/assert.h> +#include "local.h" + +/* +** SM_IO_FGETS -- get a string from a file +** +** Read at most n-1 characters from the given file. +** Stop when a newline has been read, or the count ('n') runs out. +** +** Parameters: +** fp -- the file to read from +** timeout -- time to complete reading the string in milliseconds +** buf -- buffer to place read string in +** n -- size of 'buf' +** +** Returns: +** success: returns value of 'buf' +** failure: NULL (no characters were read) +** timeout: NULL and errno set to EAGAIN +** +** Side Effects: +** may move the file pointer +*/ + +char * +sm_io_fgets(fp, timeout, buf, n) + register SM_FILE_T *fp; + int timeout; + char *buf; + register int n; +{ + register int len; + register char *s; + register unsigned char *p, *t; + + SM_REQUIRE_ISA(fp, SmFileMagic); + if (n <= 0) /* sanity check */ + return NULL; + + s = buf; + n--; /* leave space for NUL */ + while (n > 0) + { + /* If the buffer is empty, refill it. */ + if ((len = fp->f_r) <= 0) + { + + /* + ** Timeout is only passed if we can't get the data + ** from the buffer (which is counted as immediately). + */ + + if (sm_refill(fp, timeout) != 0) + { + /* EOF/error: stop with partial or no line */ + if (s == buf) + return NULL; + break; + } + len = fp->f_r; + } + p = fp->f_p; + + /* + ** Scan through at most n bytes of the current buffer, + ** looking for '\n'. If found, copy up to and including + ** newline, and stop. Otherwise, copy entire chunk + ** and loop. + */ + + if (len > n) + len = n; + t = (unsigned char *) memchr((void *) p, '\n', len); + if (t != NULL) + { + len = ++t - p; + fp->f_r -= len; + fp->f_p = t; + (void) memcpy((void *) s, (void *) p, len); + s[len] = 0; + return buf; + } + fp->f_r -= len; + fp->f_p += len; + (void) memcpy((void *) s, (void *) p, len); + s += len; + n -= len; + } + *s = 0; + return buf; +} diff --git a/usr/src/cmd/sendmail/libsm/findfp.c b/usr/src/cmd/sendmail/libsm/findfp.c new file mode 100644 index 0000000000..befb996a89 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/findfp.c @@ -0,0 +1,427 @@ +/* + * Copyright (c) 2000-2002 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: findfp.c,v 1.66 2002/02/20 02:40:24 ca Exp $") +#include <stdlib.h> +#include <unistd.h> +#include <sys/param.h> +#include <errno.h> +#include <string.h> +#include <syslog.h> +#include <sm/io.h> +#include <sm/assert.h> +#include <sm/heap.h> +#include <sm/string.h> +#include <sm/conf.h> +#include "local.h" +#include "glue.h" + +bool Sm_IO_DidInit; /* IO system has been initialized? */ + +const char SmFileMagic[] = "sm_file"; + +/* An open type to map to fopen()-like behavior */ +SM_FILE_T SmFtStdio_def = + {SmFileMagic, 0, 0, 0, (SMRW|SMFBF), -1, {0, 0}, 0, 0, 0, + sm_stdclose, sm_stdread, sm_stdseek, sm_stdwrite, + sm_stdopen, sm_stdsetinfo, sm_stdgetinfo, SM_TIME_FOREVER, + SM_TIME_BLOCK, "stdio" }; + +/* An open type to map to fdopen()-like behavior */ +SM_FILE_T SmFtStdiofd_def = + {SmFileMagic, 0, 0, 0, (SMRW|SMFBF), -1, {0, 0}, 0, 0, 0, + sm_stdclose, sm_stdread, sm_stdseek, sm_stdwrite, + sm_stdfdopen, sm_stdsetinfo, sm_stdgetinfo, SM_TIME_FOREVER, + SM_TIME_BLOCK, "stdiofd" }; + +/* A string file type */ +SM_FILE_T SmFtString_def = + {SmFileMagic, 0, 0, 0, (SMRW|SMNBF), -1, {0, 0}, 0, 0, 0, + sm_strclose, sm_strread, sm_strseek, sm_strwrite, + sm_stropen, sm_strsetinfo, sm_strgetinfo, SM_TIME_FOREVER, + SM_TIME_BLOCK, "string" }; + +#if 0 +/* A file type for syslog communications */ +SM_FILE_T SmFtSyslog_def = + {SmFileMagic, 0, 0, 0, (SMRW|SMNBF), -1, {0, 0}, 0, 0, 0, + sm_syslogclose, sm_syslogread, sm_syslogseek, sm_syslogwrite, + sm_syslogopen, sm_syslogsetinfo, sm_sysloggetinfo, SM_TIME_FOREVER, + SM_TIME_BLOCK, "syslog" }; +#endif /* 0 */ + +#define NDYNAMIC 10 /* add ten more whenever necessary */ + +#define smio(flags, file, name) \ + {SmFileMagic, 0, 0, 0, flags, file, {0}, 0, SmIoF+file, 0, \ + sm_stdclose, sm_stdread, sm_stdseek, sm_stdwrite, \ + sm_stdopen, sm_stdsetinfo, sm_stdgetinfo, SM_TIME_FOREVER, \ + SM_TIME_BLOCK, name} + +/* sm_magic p r w flags file bf lbfsize cookie ival */ +#define smstd(flags, file, name) \ + {SmFileMagic, 0, 0, 0, flags, -1, {0}, 0, 0, file, \ + sm_stdioclose, sm_stdioread, sm_stdioseek, sm_stdiowrite, \ + sm_stdioopen, sm_stdiosetinfo, sm_stdiogetinfo, SM_TIME_FOREVER,\ + SM_TIME_BLOCK, name} + +/* A file type for interfacing to stdio FILE* streams. */ +SM_FILE_T SmFtRealStdio_def = smstd(SMRW|SMNBF, -1, "realstdio"); + + /* the usual - (stdin + stdout + stderr) */ +static SM_FILE_T usual[SM_IO_OPEN_MAX - 3]; +static struct sm_glue smuglue = { 0, SM_IO_OPEN_MAX - 3, usual }; + +/* List of builtin automagically already open file pointers */ +SM_FILE_T SmIoF[6] = +{ + smio(SMRD|SMLBF, SMIOIN_FILENO, "smioin"), /* smioin */ + smio(SMWR|SMLBF, SMIOOUT_FILENO, "smioout"), /* smioout */ + smio(SMWR|SMNBF, SMIOERR_FILENO, "smioerr"), /* smioerr */ + smstd(SMRD|SMNBF, SMIOIN_FILENO, "smiostdin"), /* smiostdin */ + smstd(SMWR|SMNBF, SMIOOUT_FILENO, "smiostdout"),/* smiostdout */ + smstd(SMWR|SMNBF, SMIOERR_FILENO, "smiostderr") /* smiostderr */ +}; + +/* Structure containing list of currently open file pointers */ +struct sm_glue smglue = { &smuglue, 3, SmIoF }; + +/* +** SM_MOREGLUE -- adds more space for open file pointers +** +** Parameters: +** n -- number of new spaces for file pointers +** +** Returns: +** Raises an exception if no more memory. +** Otherwise, returns a pointer to new spaces. +*/ + +static struct sm_glue *sm_moreglue_x __P((int)); +static SM_FILE_T empty; + +static struct sm_glue * +sm_moreglue_x(n) + register int n; +{ + register struct sm_glue *g; + register SM_FILE_T *p; + + g = (struct sm_glue *) sm_pmalloc_x(sizeof(*g) + SM_ALIGN_BITS + + n * sizeof(SM_FILE_T)); + p = (SM_FILE_T *) SM_ALIGN(g + 1); + g->gl_next = NULL; + g->gl_niobs = n; + g->gl_iobs = p; + while (--n >= 0) + *p++ = empty; + return g; +} + +/* +** SM_FP -- allocate and initialize an SM_FILE structure +** +** Parameters: +** t -- file type requested to be opened. +** flags -- control flags for file type behavior +** oldfp -- file pointer to reuse if available (optional) +** +** Returns: +** Raises exception on memory exhaustion. +** Aborts if type is invalid. +** Otherwise, returns file pointer for requested file type. +*/ + +SM_FILE_T * +sm_fp(t, flags, oldfp) + const SM_FILE_T *t; + const int flags; + SM_FILE_T *oldfp; +{ + register SM_FILE_T *fp; + register int n; + register struct sm_glue *g; + + SM_REQUIRE(t->f_open && t->f_close && (t->f_read || t->f_write)); + + if (!Sm_IO_DidInit) + sm_init(); + + if (oldfp != NULL) + { + fp = oldfp; + goto found; /* for opening reusing an 'fp' */ + } + + for (g = &smglue;; g = g->gl_next) + { + for (fp = g->gl_iobs, n = g->gl_niobs; --n >= 0; fp++) + if (fp->sm_magic == NULL) + goto found; + if (g->gl_next == NULL) + g->gl_next = sm_moreglue_x(NDYNAMIC); + } +found: + fp->sm_magic = SmFileMagic; /* 'fp' now valid and in-use */ + fp->f_p = NULL; /* no current pointer */ + fp->f_w = 0; /* nothing to write */ + fp->f_r = 0; /* nothing to read */ + fp->f_flags = flags; + fp->f_file = -1; /* no file */ + fp->f_bf.smb_base = NULL; /* no buffer */ + fp->f_bf.smb_size = 0; /* no buffer size with no buffer */ + fp->f_lbfsize = 0; /* not line buffered */ + fp->f_flushfp = NULL; /* no associated flush file */ + + fp->f_cookie = fp; /* default: *open* overrides cookie setting */ + fp->f_close = t->f_close; /* assign close function */ + fp->f_read = t->f_read; /* assign read function */ + fp->f_seek = t->f_seek; /* assign seek function */ + fp->f_write = t->f_write; /* assign write function */ + fp->f_open = t->f_open; /* assign open function */ + fp->f_setinfo = t->f_setinfo; /* assign setinfo function */ + fp->f_getinfo = t->f_getinfo; /* assign getinfo function */ + fp->f_type = t->f_type; /* file type */ + + fp->f_ub.smb_base = NULL; /* no ungetc buffer */ + fp->f_ub.smb_size = 0; /* no size for no ungetc buffer */ + + if (fp->f_timeout == SM_TIME_DEFAULT) + fp->f_timeout = SM_TIME_FOREVER; + else + fp->f_timeout = t->f_timeout; /* traditional behavior */ + fp->f_timeoutstate = SM_TIME_BLOCK; /* by default */ + + return fp; +} + +/* +** SM_CLEANUP -- cleanup function when exit called. +** +** This function is registered via atexit(). +** +** Parameters: +** none +** +** Returns: +** nothing. +** +** Side Effects: +** flushes open files before they are forced closed +*/ + +void +sm_cleanup() +{ + int timeout = SM_TIME_DEFAULT; + + (void) sm_fwalk(sm_flush, &timeout); /* `cheating' */ +} + +/* +** SM_INIT -- called whenever sm_io's internal variables must be set up. +** +** Parameters: +** none +** +** Returns: +** none +** +** Side Effects: +** Registers sm_cleanup() using atexit(). +*/ + +void +sm_init() +{ + if (Sm_IO_DidInit) /* paranoia */ + return; + + /* more paranoia: initialize pointers in a static variable */ + empty.f_type = NULL; + empty.sm_magic = NULL; + + /* make sure we clean up on exit */ + atexit(sm_cleanup); /* conservative */ + Sm_IO_DidInit = true; +} + +/* +** SM_IO_SETINFO -- change info for an open file type (fp) +** +** The generic SM_IO_WHAT_VECTORS is auto supplied for all file types. +** If the request is to set info other than SM_IO_WHAT_VECTORS then +** the request is passed on to the file type's specific setinfo vector. +** WARNING: this is working on an active/open file type. +** +** Parameters: +** fp -- file to make the setting on +** what -- type of information to set +** valp -- structure to obtain info from +** +** Returns: +** 0 on success +** -1 on error and sets errno: +** - when what != SM_IO_WHAT_VECTORS and setinfo vector +** not set +** - when vectored setinfo returns -1 +*/ + +int +sm_io_setinfo(fp, what, valp) + SM_FILE_T *fp; + int what; + void *valp; +{ + SM_FILE_T *v = (SM_FILE_T *) valp; + + SM_REQUIRE_ISA(fp, SmFileMagic); + switch (what) + { + case SM_IO_WHAT_VECTORS: + + /* + ** This is the "generic" available for all. + ** This allows the function vectors to be replaced + ** while the file type is active. + */ + + fp->f_close = v->f_close; + fp->f_read = v->f_read; + fp->f_seek = v->f_seek; + fp->f_write = v->f_write; + fp->f_open = v->f_open; + fp->f_setinfo = v->f_setinfo; + fp->f_getinfo = v->f_getinfo; + sm_free(fp->f_type); + fp->f_type = sm_strdup_x(v->f_type); + return 0; + case SM_IO_WHAT_TIMEOUT: + fp->f_timeout = *((int *)valp); + return 0; + } + + /* Otherwise the vector will check it out */ + if (fp->f_setinfo == NULL) + { + errno = EINVAL; + return -1; + } + else + return (*fp->f_setinfo)(fp, what, valp); +} + +/* +** SM_IO_GETINFO -- get information for an active file type (fp) +** +** This function supplies for all file types the answers for the +** three requests SM_IO_WHAT_VECTORS, SM_IO_WHAT_TYPE and +** SM_IO_WHAT_ISTYPE. Other requests are handled by the getinfo +** vector if available for the open file type. +** SM_IO_WHAT_VECTORS returns information for the file pointer vectors. +** SM_IO_WHAT_TYPE returns the type identifier for the file pointer +** SM_IO_WHAT_ISTYPE returns >0 if the passed in type matches the +** file pointer's type. +** SM_IO_IS_READABLE returns 1 if there is data available for reading, +** 0 otherwise. +** +** Parameters: +** fp -- file pointer for active file type +** what -- type of information request +** valp -- structure to place obtained info into +** +** Returns: +** -1 on error and sets errno: +** - when valp==NULL and request expects otherwise +** - when request is not SM_IO_WHAT_VECTORS and not +** SM_IO_WHAT_TYPE and not SM_IO_WHAT_ISTYPE +** and getinfo vector is NULL +** - when getinfo type vector returns -1 +** >=0 on success +*/ + +int +sm_io_getinfo(fp, what, valp) + SM_FILE_T *fp; + int what; + void *valp; +{ + SM_FILE_T *v = (SM_FILE_T *) valp; + + SM_REQUIRE_ISA(fp, SmFileMagic); + + switch (what) + { + case SM_IO_WHAT_VECTORS: + + /* This is the "generic" available for all */ + v->f_close = fp->f_close; + v->f_read = fp->f_read; + v->f_seek = fp->f_seek; + v->f_write = fp->f_write; + v->f_open = fp->f_open; + v->f_setinfo = fp->f_setinfo; + v->f_getinfo = fp->f_getinfo; + v->f_type = fp->f_type; + return 0; + + case SM_IO_WHAT_TYPE: + if (valp == NULL) + { + errno = EINVAL; + return -1; + } + valp = sm_strdup_x(fp->f_type); + return 0; + + case SM_IO_WHAT_ISTYPE: + if (valp == NULL) + { + errno = EINVAL; + return -1; + } + return strcmp(fp->f_type, valp) == 0; + + case SM_IO_IS_READABLE: + + /* if there is data in the buffer, it must be readable */ + if (fp->f_r > 0) + return 1; + + /* otherwise query the underlying file */ + break; + + case SM_IO_WHAT_TIMEOUT: + *((int *) valp) = fp->f_timeout; + return 0; + + case SM_IO_WHAT_FD: + if (fp->f_file > -1) + return fp->f_file; + + /* try the file type specific getinfo to see if it knows */ + break; + } + + /* Otherwise the vector will check it out */ + if (fp->f_getinfo == NULL) + { + errno = EINVAL; + return -1; + } + return (*fp->f_getinfo)(fp, what, valp); +} diff --git a/usr/src/cmd/sendmail/libsm/flags.c b/usr/src/cmd/sendmail/libsm/flags.c new file mode 100644 index 0000000000..8e4ede9546 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/flags.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2000-2001, 2003 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: flags.c,v 1.20.2.1 2003/09/03 18:51:56 ca Exp $") +#include <sys/types.h> +#include <sys/file.h> +#include <errno.h> +#include <sm/io.h> + +/* +** SM_FLAGS -- translate external (user) flags into internal flags +** +** Paramters: +** flags -- user select flags +** +** Returns: +** Internal flag value matching user selected flags +*/ + +int +sm_flags(flags) + register int flags; +{ + register int ret; + + switch(SM_IO_MODE(flags)) + { + case SM_IO_RDONLY: /* open for reading */ + ret = SMRD; + break; + + case SM_IO_WRONLY: /* open for writing */ + ret = SMWR; + break; + + case SM_IO_APPEND: /* open for appending */ + ret = SMWR; + break; + + case SM_IO_RDWR: /* open for read and write */ + ret = SMRW; + break; + + default: + ret = 0; + break; + } + if (SM_IS_BINARY(flags)) + ret |= SM_IO_BINARY; + return ret; +} diff --git a/usr/src/cmd/sendmail/libsm/fopen.c b/usr/src/cmd/sendmail/libsm/fopen.c new file mode 100644 index 0000000000..cfb3d4baca --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/fopen.c @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2000-2002, 2004 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: fopen.c,v 1.61 2004/08/03 20:17:38 ca Exp $") +#include <errno.h> +#include <setjmp.h> +#include <sys/time.h> +#include <sm/heap.h> +#include <sm/signal.h> +#include <sm/assert.h> +#include <sm/io.h> +#include <sm/clock.h> +#include "local.h" + +static void openalrm __P((int)); +static void reopenalrm __P((int)); +extern int sm_io_fclose __P((SM_FILE_T *)); + +static jmp_buf OpenTimeOut, ReopenTimeOut; + +/* +** OPENALRM -- handler when timeout activated for sm_io_open() +** +** Returns flow of control to where setjmp(OpenTimeOut) was set. +** +** Parameters: +** sig -- unused +** +** Returns: +** does not return +** +** Side Effects: +** returns flow of control to setjmp(OpenTimeOut). +** +** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD +** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE +** DOING. +*/ + +/* ARGSUSED0 */ +static void +openalrm(sig) + int sig; +{ + longjmp(OpenTimeOut, 1); +} +/* +** REOPENALRM -- handler when timeout activated for sm_io_reopen() +** +** Returns flow of control to where setjmp(ReopenTimeOut) was set. +** +** Parameters: +** sig -- unused +** +** Returns: +** does not return +** +** Side Effects: +** returns flow of control to setjmp(ReopenTimeOut). +** +** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD +** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE +** DOING. +*/ + +/* ARGSUSED0 */ +static void +reopenalrm(sig) + int sig; +{ + longjmp(ReopenTimeOut, 1); +} + +/* +** SM_IO_OPEN -- open a file of a specific type +** +** Parameters: +** type -- type of file to open +** timeout -- time to complete the open +** info -- info describing what is to be opened (type dependant) +** flags -- user selected flags +** rpool -- pointer to rpool to be used for this open +** +** Returns: +** Raises exception on heap exhaustion. +** Aborts if type is invalid. +** Returns NULL and sets errno +** - when the type specific open fails +** - when open vector errors +** - when flags not set or invalid +** Success returns a file pointer to the opened file type. +*/ + +SM_FILE_T * +sm_io_open(type, timeout, info, flags, rpool) + const SM_FILE_T *type; + int SM_NONVOLATILE timeout; /* this is not the file type timeout */ + const void *info; + int flags; + const void *rpool; +{ + register SM_FILE_T *fp; + int ioflags; + SM_EVENT *evt = NULL; + + ioflags = sm_flags(flags); + + if (ioflags == 0) + { + /* must give some indication/intent */ + errno = EINVAL; + return NULL; + } + + if (timeout == SM_TIME_DEFAULT) + timeout = SM_TIME_FOREVER; + if (timeout == SM_TIME_IMMEDIATE) + { + errno = EAGAIN; + return NULL; + } + + fp = sm_fp(type, ioflags, NULL); + + /* Okay, this is where we set the timeout. */ + if (timeout != SM_TIME_FOREVER) + { + if (setjmp(OpenTimeOut) != 0) + { + errno = EAGAIN; + return NULL; + } + evt = sm_seteventm(timeout, openalrm, 0); + } + + if ((*fp->f_open)(fp, info, flags, rpool) < 0) + { + fp->f_flags = 0; /* release */ + fp->sm_magic = NULL; /* release */ + return NULL; + } + + /* We're back. So undo our timeout and handler */ + if (evt != NULL) + sm_clrevent(evt); + +#if SM_RPOOL + if (rpool != NULL) + sm_rpool_attach_x(rpool, sm_io_fclose, fp); +#endif /* SM_RPOOL */ + + return fp; +} +/* +** SM_IO_DUP -- duplicate a file pointer +** +** Parameters: +** fp -- file pointer to duplicate +** +** Returns: +** Success - the duplicated file pointer +** Failure - NULL (was an invalid file pointer or too many open) +** +** Increments the duplicate counter (dup_cnt) for the open file pointer. +** The counter counts the number of duplicates. When the duplicate +** counter is 0 (zero) then the file pointer is the only one left +** (no duplicates, it is the only one). +*/ + +SM_FILE_T * +sm_io_dup(fp) + SM_FILE_T *fp; +{ + + SM_REQUIRE_ISA(fp, SmFileMagic); + if (fp->sm_magic != SmFileMagic) + { + errno = EBADF; + return NULL; + } + if (fp->f_dup_cnt >= INT_MAX - 1) + { + /* Can't let f_dup_cnt wrap! */ + errno = EMFILE; + return NULL; + } + fp->f_dup_cnt++; + return fp; +} +/* +** SM_IO_REOPEN -- open a new file using the old file pointer +** +** Parameters: +** type -- file type to be opened +** timeout -- time to complete the reopen +** info -- infomation about what is to be "re-opened" (type dep.) +** flags -- user flags to map to internal flags +** rpool -- rpool file to be associated with +** fp -- the file pointer to reuse +** +** Returns: +** Raises an exception on heap exhaustion. +** Aborts if type is invalid. +** Failure: returns NULL +** Success: returns "reopened" file pointer +*/ + +SM_FILE_T * +sm_io_reopen(type, timeout, info, flags, rpool, fp) + const SM_FILE_T *type; + int SM_NONVOLATILE timeout; + const void *info; + int flags; + const void *rpool; + SM_FILE_T *fp; +{ + int ioflags, ret; + SM_FILE_T *fp2; + SM_EVENT *evt = NULL; + + if ((ioflags = sm_flags(flags)) == 0) + { + (void) sm_io_close(fp, timeout); + return NULL; + } + + if (!Sm_IO_DidInit) + sm_init(); + + if (timeout == SM_TIME_DEFAULT) + timeout = SM_TIME_FOREVER; + if (timeout == SM_TIME_IMMEDIATE) + { + /* + ** Filling the buffer will take time and we are wanted to + ** return immediately. So... + */ + + errno = EAGAIN; + return NULL; + } + /* Okay, this is where we set the timeout. */ + if (timeout != SM_TIME_FOREVER) + { + if (setjmp(ReopenTimeOut) != 0) + { + errno = EAGAIN; + return NULL; + } + + evt = sm_seteventm(timeout, reopenalrm, 0); + } + + /* + ** There are actually programs that depend on being able to "reopen" + ** descriptors that weren't originally open. Keep this from breaking. + ** Remember whether the stream was open to begin with, and which file + ** descriptor (if any) was associated with it. If it was attached to + ** a descriptor, defer closing it; reopen("/dev/stdin", "r", stdin) + ** should work. This is unnecessary if it was not a Unix file. + */ + + if (fp != NULL) + { + if (fp->sm_magic != SmFileMagic) + fp->f_flags = SMFEOF; /* hold on to it */ + else + { + /* flush the stream; ANSI doesn't require this. */ + (void) sm_io_flush(fp, SM_TIME_FOREVER); + (void) sm_io_close(fp, SM_TIME_FOREVER); + } + } + + fp2 = sm_fp(type, ioflags, fp); + ret = (*fp2->f_open)(fp2, info, flags, rpool); + + /* We're back. So undo our timeout and handler */ + if (evt != NULL) + sm_clrevent(evt); + + if (ret < 0) + { + fp2->f_flags = 0; /* release */ + fp2->sm_magic = NULL; /* release */ + return NULL; + } + + /* + ** We're not preserving this logic (below) for sm_io because it is now + ** abstracted at least one "layer" away. By closing and reopening + ** the 1st fd used should be the just released one (when Unix + ** behavior followed). Old comment:: + ** If reopening something that was open before on a real file, try + ** to maintain the descriptor. Various C library routines (perror) + ** assume stderr is always fd STDERR_FILENO, even if being reopen'd. + */ + +#if SM_RPOOL + if (rpool != NULL) + sm_rpool_attach_x(rpool, sm_io_close, fp2); +#endif /* SM_RPOOL */ + + return fp2; +} +/* +** SM_IO_AUTOFLUSH -- link another file to this for auto-flushing +** +** When a read occurs on fp, fp2 will be flushed iff there is no +** data waiting on fp. +** +** Parameters: +** fp -- the file opened for reading. +** fp2 -- the file opened for writing. +** +** Returns: +** The old flush file pointer. +*/ + +SM_FILE_T * +sm_io_autoflush(fp, fp2) + SM_FILE_T *fp; + SM_FILE_T *fp2; +{ + SM_FILE_T *savefp; + + SM_REQUIRE_ISA(fp, SmFileMagic); + if (fp2 != NULL) + SM_REQUIRE_ISA(fp2, SmFileMagic); + + savefp = fp->f_flushfp; + fp->f_flushfp = fp2; + return savefp; +} +/* +** SM_IO_AUTOMODE -- link another file to this for auto-moding +** +** When the mode (blocking or non-blocking) changes for fp1 then +** update fp2's mode at the same time. This is to be used when +** a system dup() has generated a second file descriptor for +** another sm_io_open() by file descriptor. The modes have been +** linked in the system and this formalizes it for sm_io internally. +** +** Parameters: +** fp1 -- the first file +** fp2 -- the second file +** +** Returns: +** nothing +*/ + +void +sm_io_automode(fp1, fp2) + SM_FILE_T *fp1; + SM_FILE_T *fp2; +{ + SM_REQUIRE_ISA(fp1, SmFileMagic); + SM_REQUIRE_ISA(fp2, SmFileMagic); + + fp1->f_modefp = fp2; + fp2->f_modefp = fp1; +} diff --git a/usr/src/cmd/sendmail/libsm/fpos.c b/usr/src/cmd/sendmail/libsm/fpos.c new file mode 100644 index 0000000000..b443015626 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/fpos.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2000-2001, 2004 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: fpos.c,v 1.38 2004/08/03 20:17:38 ca Exp $") +#include <errno.h> +#include <setjmp.h> +#include <sys/time.h> +#include <sm/heap.h> +#include <sm/signal.h> +#include <sm/clock.h> +#include <sm/io.h> +#include <sm/assert.h> +#include "local.h" + +static void tellalrm __P((int)); +static jmp_buf TellTimeOut; + +/* +** TELLALRM -- handler when timeout activated for sm_io_tell() +** +** Returns flow of control to where setjmp(TellTimeOut) was set. +** +** Parameters: +** sig -- unused +** +** Returns: +** does not return +** +** Side Effects: +** returns flow of control to setjmp(TellTimeOut). +** +** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD +** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE +** DOING. +*/ + +/* ARGSUSED0 */ +static void +tellalrm(sig) + int sig; +{ + longjmp(TellTimeOut, 1); +} + +/* +** SM_IO_TELL -- position the file pointer +** +** Paramters: +** fp -- the file pointer to get repositioned +** timeout -- time to complete the tell (milliseconds) +** +** Returns: +** Success -- the repositioned location. +** Failure -- -1 (minus 1) and sets errno +*/ + +long +sm_io_tell(fp, timeout) + register SM_FILE_T *fp; + int SM_NONVOLATILE timeout; +{ + register off_t pos; + SM_EVENT *evt = NULL; + + SM_REQUIRE_ISA(fp, SmFileMagic); + if (fp->f_seek == NULL) + { + errno = ESPIPE; /* historic practice */ + return -1L; + } + + if (timeout == SM_TIME_DEFAULT) + timeout = fp->f_timeout; + if (timeout == SM_TIME_IMMEDIATE) + { + /* + ** Filling the buffer will take time and we are wanted to + ** return immediately. So... + */ + + errno = EAGAIN; + return -1L; + } + + /* + ** Find offset of underlying I/O object, then adjust byte position + ** may adjust seek offset on append stream + */ + + (void) sm_flush(fp, (int *) &timeout); + + /* This is where we start the timeout */ + if (timeout != SM_TIME_FOREVER) + { + if (setjmp(TellTimeOut) != 0) + { + errno = EAGAIN; + return -1L; + } + + evt = sm_seteventm(timeout, tellalrm, 0); + } + + if (fp->f_flags & SMOFF) + pos = fp->f_lseekoff; + else + { + /* XXX only set the timeout here? */ + pos = (*fp->f_seek)(fp, (off_t) 0, SM_IO_SEEK_CUR); + if (pos == -1L) + goto clean; + } + if (fp->f_flags & SMRD) + { + /* + ** Reading. Any unread characters (including + ** those from ungetc) cause the position to be + ** smaller than that in the underlying object. + */ + + pos -= fp->f_r; + if (HASUB(fp)) + pos -= fp->f_ur; + } + else if (fp->f_flags & SMWR && fp->f_p != NULL) + { + /* + ** Writing. Any buffered characters cause the + ** position to be greater than that in the + ** underlying object. + */ + + pos += fp->f_p - fp->f_bf.smb_base; + } + +clean: + /* We're back. So undo our timeout and handler */ + if (evt != NULL) + sm_clrevent(evt); + return pos; +} diff --git a/usr/src/cmd/sendmail/libsm/fprintf.c b/usr/src/cmd/sendmail/libsm/fprintf.c new file mode 100644 index 0000000000..591e09fc09 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/fprintf.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: fprintf.c,v 1.15 2001/03/02 23:53:41 ca Exp $") +#include <sm/varargs.h> +#include <sm/io.h> +#include <sm/assert.h> +#include "local.h" + +/* +** SM_IO_FPRINTF -- format and print a string to a file pointer +** +** Parameters: +** fp -- file pointer to be printed to +** timeout -- time to complete print +** fmt -- markup format for the string to be printed +** ... -- additional information for 'fmt' +** +** Returns: +** Failure: returns SM_IO_EOF and sets errno +** Success: returns the number of characters o/p +*/ + +int +#if SM_VA_STD +sm_io_fprintf(SM_FILE_T *fp, int timeout, const char *fmt, ...) +#else /* SM_VA_STD */ +sm_io_fprintf(fp, timeout, fmt, va_alist) + SM_FILE_T *fp; + int timeout; + char *fmt; + va_dcl +#endif /* SM_VA_STD */ +{ + int ret; + SM_VA_LOCAL_DECL + + SM_REQUIRE_ISA(fp, SmFileMagic); + SM_VA_START(ap, fmt); + ret = sm_io_vfprintf(fp, timeout, fmt, ap); + SM_VA_END(ap); + return ret; +} diff --git a/usr/src/cmd/sendmail/libsm/fpurge.c b/usr/src/cmd/sendmail/libsm/fpurge.c new file mode 100644 index 0000000000..4484a4d546 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/fpurge.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: fpurge.c,v 1.18 2001/01/28 00:29:35 ca Exp $") +#include <stdlib.h> +#include <errno.h> +#include <sm/io.h> +#include <sm/assert.h> +#include "local.h" + +/* +** SM_IO_PURGE -- purge/empty the buffer without committing buffer content +** +** Parameters: +** fp -- file pointer to purge +** +** Returns: +** Failure: returns SM_IO_EOF and sets errno +** Success: returns 0 (zero) +*/ + +int +sm_io_purge(fp) + register SM_FILE_T *fp; +{ + SM_REQUIRE_ISA(fp, SmFileMagic); + if (!fp->f_flags) + { + errno = EBADF; + return SM_IO_EOF; + } + + if (HASUB(fp)) + FREEUB(fp); + fp->f_p = fp->f_bf.smb_base; + fp->f_r = 0; + + /* implies SMFBF */ + fp->f_w = fp->f_flags & (SMLBF|SMNBF) ? 0 : fp->f_bf.smb_size; + return 0; +} diff --git a/usr/src/cmd/sendmail/libsm/fput.c b/usr/src/cmd/sendmail/libsm/fput.c new file mode 100644 index 0000000000..6a787758eb --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/fput.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: fput.c,v 1.18 2001/01/28 00:29:35 ca Exp $") +#include <string.h> +#include <errno.h> +#include <sm/io.h> +#include <sm/assert.h> +#include "local.h" +#include "fvwrite.h" + +/* +** SM_IO_FPUTS -- add a string to the buffer for the file pointer +** +** Parameters: +** fp -- the file pointer for the buffer to be written to +** timeout -- time to complete the put-string +** s -- string to be placed in the buffer +** +** Returns: +** Failure: returns SM_IO_EOF +** Success: returns 0 (zero) +*/ + +int +sm_io_fputs(fp, timeout, s) + SM_FILE_T *fp; + int timeout; + const char *s; +{ + struct sm_uio uio; + struct sm_iov iov; + + SM_REQUIRE_ISA(fp, SmFileMagic); + iov.iov_base = (void *) s; + iov.iov_len = uio.uio_resid = strlen(s); + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + return sm_fvwrite(fp, timeout, &uio); +} diff --git a/usr/src/cmd/sendmail/libsm/fread.c b/usr/src/cmd/sendmail/libsm/fread.c new file mode 100644 index 0000000000..40cd23cfcd --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/fread.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: fread.c,v 1.26 2001/02/28 20:54:03 ca Exp $") +#include <string.h> +#include <errno.h> +#include <sm/io.h> +#include <sm/assert.h> +#include "local.h" + +/* +** SM_IO_READ -- read data from the file pointer +** +** Parameters: +** fp -- file pointer to read from +** timeout -- time to complete the read +** buf -- location to place read data +** size -- size of each chunk of data +** +** Returns: +** Failure: returns 0 (zero) _and_ sets errno +** Success: returns the number of whole chunks read. +** +** A read returning 0 (zero) is only an indication of error when errno +** has been set. +*/ + +size_t +sm_io_read(fp, timeout, buf, size) + SM_FILE_T *fp; + int timeout; + void *buf; + size_t size; +{ + register size_t resid = size; + register char *p; + register int r; + + SM_REQUIRE_ISA(fp, SmFileMagic); + + if (fp->f_read == NULL) + { + errno = ENODEV; + return 0; + } + + /* + ** The ANSI standard requires a return value of 0 for a count + ** or a size of 0. Peculiarily, it imposes no such requirements + ** on fwrite; it only requires read to be broken. + */ + + if (resid == 0) + return 0; + if (fp->f_r < 0) + fp->f_r = 0; + p = buf; + while ((int) resid > (r = fp->f_r)) + { + (void) memcpy((void *) p, (void *) fp->f_p, (size_t) r); + fp->f_p += r; + /* fp->f_r = 0 ... done in sm_refill */ + p += r; + resid -= r; + if ((fp->f_flags & SMNOW) != 0 && r > 0) + { + /* + ** Take whatever we have available. Spend no more time + ** trying to get all that has been requested. + ** This is needed on some file types (such as + ** SASL) that would jam when given extra, untimely + ** reads. + */ + + fp->f_r -= r; + return size - resid; + } + if (sm_refill(fp, timeout) != 0) + { + /* no more input: return partial result */ + return size - resid; + } + } + (void) memcpy((void *) p, (void *) fp->f_p, resid); + fp->f_r -= resid; + fp->f_p += resid; + return size; +} diff --git a/usr/src/cmd/sendmail/libsm/fscanf.c b/usr/src/cmd/sendmail/libsm/fscanf.c new file mode 100644 index 0000000000..c456740c66 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/fscanf.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: fscanf.c,v 1.15 2001/03/02 23:53:41 ca Exp $") +#include <sm/varargs.h> +#include <sm/assert.h> +#include <sm/io.h> +#include "local.h" + +/* +** SM_IO_FSCANF -- convert input data to translated format +** +** Parameters: +** fp -- the file pointer to obtain the data from +** timeout -- time to complete scan +** fmt -- the format to translate the data to +** ... -- memory locations to place the formated data +** +** Returns: +** Failure: returns SM_IO_EOF +** Success: returns the number of data units translated +*/ + +int +#if SM_VA_STD +sm_io_fscanf(SM_FILE_T *fp, int timeout, char const *fmt, ...) +#else /* SM_VA_STD */ +sm_io_fscanf(fp, timeout, fmt, va_alist) + SM_FILE_T *fp; + int timeout; + char *fmt; + va_dcl +#endif /* SM_VA_STD */ +{ + int ret; + SM_VA_LOCAL_DECL + + SM_REQUIRE_ISA(fp, SmFileMagic); + SM_VA_START(ap, fmt); + ret = sm_vfscanf(fp, timeout, fmt, ap); + SM_VA_END(ap); + return ret; +} diff --git a/usr/src/cmd/sendmail/libsm/fseek.c b/usr/src/cmd/sendmail/libsm/fseek.c new file mode 100644 index 0000000000..d461aba89b --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/fseek.c @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2000-2001, 2004 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: fseek.c,v 1.46 2004/08/03 20:17:38 ca Exp $") +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <errno.h> +#include <setjmp.h> +#include <sys/time.h> +#include <sm/signal.h> +#include <sm/io.h> +#include <sm/assert.h> +#include <sm/clock.h> +#include "local.h" + +#define POS_ERR (-(off_t)1) + +static void seekalrm __P((int)); +static jmp_buf SeekTimeOut; + +/* +** SEEKALRM -- handler when timeout activated for sm_io_seek() +** +** Returns flow of control to where setjmp(SeekTimeOut) was set. +** +** Parameters: +** sig -- unused +** +** Returns: +** does not return +** +** Side Effects: +** returns flow of control to setjmp(SeekTimeOut). +** +** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD +** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE +** DOING. +*/ + +/* ARGSUSED0 */ +static void +seekalrm(sig) + int sig; +{ + longjmp(SeekTimeOut, 1); +} + +/* +** SM_IO_SEEK -- position the file pointer +** +** Parameters: +** fp -- the file pointer to be seek'd +** timeout -- time to complete seek (milliseconds) +** offset -- seek offset based on 'whence' +** whence -- indicates where seek is relative from. +** One of SM_IO_SEEK_{CUR,SET,END}. +** Returns: +** Failure: returns -1 (minus 1) and sets errno +** Success: returns 0 (zero) +*/ + +int +sm_io_seek(fp, timeout, offset, whence) + register SM_FILE_T *fp; + int SM_NONVOLATILE timeout; + long SM_NONVOLATILE offset; + int SM_NONVOLATILE whence; +{ + bool havepos; + off_t target, curoff; + size_t n; + struct stat st; + int ret; + SM_EVENT *evt = NULL; + register off_t (*seekfn) __P((SM_FILE_T *, off_t, int)); + + SM_REQUIRE_ISA(fp, SmFileMagic); + + /* make sure stdio is set up */ + if (!Sm_IO_DidInit) + sm_init(); + + /* Have to be able to seek. */ + if ((seekfn = fp->f_seek) == NULL) + { + errno = ESPIPE; /* historic practice */ + return -1; + } + + if (timeout == SM_TIME_DEFAULT) + timeout = fp->f_timeout; + if (timeout == SM_TIME_IMMEDIATE) + { + /* + ** Filling the buffer will take time and we are wanted to + ** return immediately. So... + */ + + errno = EAGAIN; + return -1; + } + +#define SM_SET_ALARM() \ + if (timeout != SM_TIME_FOREVER) \ + { \ + if (setjmp(SeekTimeOut) != 0) \ + { \ + errno = EAGAIN; \ + return -1; \ + } \ + evt = sm_seteventm(timeout, seekalrm, 0); \ + } + + /* + ** Change any SM_IO_SEEK_CUR to SM_IO_SEEK_SET, and check `whence' + ** argument. After this, whence is either SM_IO_SEEK_SET or + ** SM_IO_SEEK_END. + */ + + switch (whence) + { + case SM_IO_SEEK_CUR: + + /* + ** In order to seek relative to the current stream offset, + ** we have to first find the current stream offset a la + ** ftell (see ftell for details). + */ + + /* may adjust seek offset on append stream */ + sm_flush(fp, (int *) &timeout); + SM_SET_ALARM(); + if (fp->f_flags & SMOFF) + curoff = fp->f_lseekoff; + else + { + curoff = (*seekfn)(fp, (off_t) 0, SM_IO_SEEK_CUR); + if (curoff == -1L) + { + ret = -1; + goto clean; + } + } + if (fp->f_flags & SMRD) + { + curoff -= fp->f_r; + if (HASUB(fp)) + curoff -= fp->f_ur; + } + else if (fp->f_flags & SMWR && fp->f_p != NULL) + curoff += fp->f_p - fp->f_bf.smb_base; + + offset += curoff; + whence = SM_IO_SEEK_SET; + havepos = true; + break; + + case SM_IO_SEEK_SET: + case SM_IO_SEEK_END: + SM_SET_ALARM(); + curoff = 0; /* XXX just to keep gcc quiet */ + havepos = false; + break; + + default: + errno = EINVAL; + return -1; + } + + /* + ** Can only optimise if: + ** reading (and not reading-and-writing); + ** not unbuffered; and + ** this is a `regular' Unix file (and hence seekfn==sm_stdseek). + ** We must check SMNBF first, because it is possible to have SMNBF + ** and SMSOPT both set. + */ + + if (fp->f_bf.smb_base == NULL) + sm_makebuf(fp); + if (fp->f_flags & (SMWR | SMRW | SMNBF | SMNPT)) + goto dumb; + if ((fp->f_flags & SMOPT) == 0) + { + if (seekfn != sm_stdseek || + fp->f_file < 0 || fstat(fp->f_file, &st) || + (st.st_mode & S_IFMT) != S_IFREG) + { + fp->f_flags |= SMNPT; + goto dumb; + } + fp->f_blksize = st.st_blksize; + fp->f_flags |= SMOPT; + } + + /* + ** We are reading; we can try to optimise. + ** Figure out where we are going and where we are now. + */ + + if (whence == SM_IO_SEEK_SET) + target = offset; + else + { + if (fstat(fp->f_file, &st)) + goto dumb; + target = st.st_size + offset; + } + + if (!havepos) + { + if (fp->f_flags & SMOFF) + curoff = fp->f_lseekoff; + else + { + curoff = (*seekfn)(fp, (off_t) 0, SM_IO_SEEK_CUR); + if (curoff == POS_ERR) + goto dumb; + } + curoff -= fp->f_r; + if (HASUB(fp)) + curoff -= fp->f_ur; + } + + /* + ** Compute the number of bytes in the input buffer (pretending + ** that any ungetc() input has been discarded). Adjust current + ** offset backwards by this count so that it represents the + ** file offset for the first byte in the current input buffer. + */ + + if (HASUB(fp)) + { + curoff += fp->f_r; /* kill off ungetc */ + n = fp->f_up - fp->f_bf.smb_base; + curoff -= n; + n += fp->f_ur; + } + else + { + n = fp->f_p - fp->f_bf.smb_base; + curoff -= n; + n += fp->f_r; + } + + /* + ** If the target offset is within the current buffer, + ** simply adjust the pointers, clear SMFEOF, undo ungetc(), + ** and return. (If the buffer was modified, we have to + ** skip this; see getln in fget.c.) + */ + + if (target >= curoff && target < curoff + (off_t) n) + { + register int o = target - curoff; + + fp->f_p = fp->f_bf.smb_base + o; + fp->f_r = n - o; + if (HASUB(fp)) + FREEUB(fp); + fp->f_flags &= ~SMFEOF; + ret = 0; + goto clean; + } + + /* + ** The place we want to get to is not within the current buffer, + ** but we can still be kind to the kernel copyout mechanism. + ** By aligning the file offset to a block boundary, we can let + ** the kernel use the VM hardware to map pages instead of + ** copying bytes laboriously. Using a block boundary also + ** ensures that we only read one block, rather than two. + */ + + curoff = target & ~(fp->f_blksize - 1); + if ((*seekfn)(fp, curoff, SM_IO_SEEK_SET) == POS_ERR) + goto dumb; + fp->f_r = 0; + fp->f_p = fp->f_bf.smb_base; + if (HASUB(fp)) + FREEUB(fp); + fp->f_flags &= ~SMFEOF; + n = target - curoff; + if (n) + { + /* Note: SM_TIME_FOREVER since fn timeout already set */ + if (sm_refill(fp, SM_TIME_FOREVER) || fp->f_r < (int) n) + goto dumb; + fp->f_p += n; + fp->f_r -= n; + } + + ret = 0; +clean: + /* We're back. So undo our timeout and handler */ + if (evt != NULL) + sm_clrevent(evt); + return ret; +dumb: + /* + ** We get here if we cannot optimise the seek ... just + ** do it. Allow the seek function to change fp->f_bf.smb_base. + */ + + /* Note: SM_TIME_FOREVER since fn timeout already set */ + ret = SM_TIME_FOREVER; + if (sm_flush(fp, &ret) != 0 || + (*seekfn)(fp, (off_t) offset, whence) == POS_ERR) + { + ret = -1; + goto clean; + } + + /* success: clear SMFEOF indicator and discard ungetc() data */ + if (HASUB(fp)) + FREEUB(fp); + fp->f_p = fp->f_bf.smb_base; + fp->f_r = 0; + fp->f_flags &= ~SMFEOF; + ret = 0; + goto clean; +} diff --git a/usr/src/cmd/sendmail/libsm/fvwrite.c b/usr/src/cmd/sendmail/libsm/fvwrite.c new file mode 100644 index 0000000000..4cdd022fe0 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/fvwrite.c @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: fvwrite.c,v 1.47 2001/08/27 13:02:20 ca Exp $") +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <signal.h> +#include <fcntl.h> +#include <sm/io.h> +#include <sm/setjmp.h> +#include <sm/conf.h> +#include "local.h" +#include "fvwrite.h" + +/* +** SM_FVWRITE -- write memory regions and buffer for file pointer +** +** Parameters: +** fp -- the file pointer to write to +** timeout -- time length for function to return by +** uio -- the memory regions to write +** +** Returns: +** Failure: returns SM_IO_EOF and sets errno +** Success: returns 0 (zero) +** +** This routine is large and unsightly, but most of the ugliness due +** to the different kinds of output buffering handled here. +*/ + +#define COPY(n) (void)memcpy((void *)fp->f_p, (void *)p, (size_t)(n)) +#define GETIOV(extra_work) \ + while (len == 0) \ + { \ + extra_work; \ + p = iov->iov_base; \ + len = iov->iov_len; \ + iov++; \ + } + +int +sm_fvwrite(fp, timeout, uio) + register SM_FILE_T *fp; + int timeout; + register struct sm_uio *uio; +{ + register size_t len; + register char *p; + register struct sm_iov *iov; + register int w, s; + char *nl; + int nlknown, nldist; + int fd; + struct timeval to; + + if (uio->uio_resid == 0) + return 0; + + /* make sure we can write */ + if (cantwrite(fp)) + { + errno = EBADF; + return SM_IO_EOF; + } + + SM_CONVERT_TIME(fp, fd, timeout, &to); + + iov = uio->uio_iov; + p = iov->iov_base; + len = iov->iov_len; + iov++; + if (fp->f_flags & SMNBF) + { + /* Unbuffered: write up to BUFSIZ bytes at a time. */ + do + { + GETIOV(;); + errno = 0; /* needed to ensure EOF correctly found */ + w = (*fp->f_write)(fp, p, SM_MIN(len, SM_IO_BUFSIZ)); + if (w <= 0) + { + if (w == 0 && errno == 0) + break; /* EOF found */ + if (IS_IO_ERROR(fd, w, timeout)) + goto err; /* errno set */ + + /* write would block */ + SM_IO_WR_TIMEOUT(fp, fd, timeout); + w = 0; + } + else + { + p += w; + len -= w; + } + } while ((uio->uio_resid -= w) != 0); + } + else if ((fp->f_flags & SMLBF) == 0) + { + /* + ** Not SMLBF (line-buffered). Either SMFBF or SMNOW + ** buffered: fill partially full buffer, if any, + ** and then flush. If there is no partial buffer, write + ** one bf._size byte chunk directly (without copying). + ** + ** String output is a special case: write as many bytes + ** as fit, but pretend we wrote everything. This makes + ** snprintf() return the number of bytes needed, rather + ** than the number used, and avoids its write function + ** (so that the write function can be invalid). + */ + + do + { + GETIOV(;); + if ((((fp->f_flags & (SMALC | SMSTR)) == (SMALC | SMSTR)) + || ((fp->f_flags & SMNOW) != 0)) + && (size_t) fp->f_w < len) + { + size_t blen = fp->f_p - fp->f_bf.smb_base; + unsigned char *tbase; + int tsize; + + /* Allocate space exponentially. */ + tsize = fp->f_bf.smb_size; + do + { + tsize = (tsize << 1) + 1; + } while ((size_t) tsize < blen + len); + tbase = (unsigned char *) sm_realloc(fp->f_bf.smb_base, + tsize + 1); + if (tbase == NULL) + { + errno = ENOMEM; + goto err; /* errno set */ + } + fp->f_w += tsize - fp->f_bf.smb_size; + fp->f_bf.smb_base = tbase; + fp->f_bf.smb_size = tsize; + fp->f_p = tbase + blen; + } + w = fp->f_w; + errno = 0; /* needed to ensure EOF correctly found */ + if (fp->f_flags & SMSTR) + { + if (len < (size_t) w) + w = len; + COPY(w); /* copy SM_MIN(fp->f_w,len), */ + fp->f_w -= w; + fp->f_p += w; + w = len; /* but pretend copied all */ + } + else if (fp->f_p > fp->f_bf.smb_base + && len > (size_t) w) + { + /* fill and flush */ + COPY(w); + fp->f_p += w; + if (sm_flush(fp, &timeout)) + goto err; /* errno set */ + } + else if (len >= (size_t) (w = fp->f_bf.smb_size)) + { + /* write directly */ + w = (*fp->f_write)(fp, p, w); + if (w <= 0) + { + if (w == 0 && errno == 0) + break; /* EOF found */ + if (IS_IO_ERROR(fd, w, timeout)) + goto err; /* errno set */ + + /* write would block */ + SM_IO_WR_TIMEOUT(fp, fd, timeout); + w = 0; + } + } + else + { + /* fill and done */ + w = len; + COPY(w); + fp->f_w -= w; + fp->f_p += w; + } + p += w; + len -= w; + } while ((uio->uio_resid -= w) != 0); + + if ((fp->f_flags & SMNOW) != 0 && sm_flush(fp, &timeout)) + goto err; /* errno set */ + } + else + { + /* + ** Line buffered: like fully buffered, but we + ** must check for newlines. Compute the distance + ** to the first newline (including the newline), + ** or `infinity' if there is none, then pretend + ** that the amount to write is SM_MIN(len,nldist). + */ + + nlknown = 0; + nldist = 0; /* XXX just to keep gcc happy */ + do + { + GETIOV(nlknown = 0); + if (!nlknown) + { + nl = memchr((void *)p, '\n', len); + nldist = nl != NULL ? nl + 1 - p : len + 1; + nlknown = 1; + } + s = SM_MIN(len, ((size_t) nldist)); + w = fp->f_w + fp->f_bf.smb_size; + errno = 0; /* needed to ensure EOF correctly found */ + if (fp->f_p > fp->f_bf.smb_base && s > w) + { + COPY(w); + /* fp->f_w -= w; */ + fp->f_p += w; + if (sm_flush(fp, &timeout)) + goto err; /* errno set */ + } + else if (s >= (w = fp->f_bf.smb_size)) + { + w = (*fp->f_write)(fp, p, w); + if (w <= 0) + { + if (w == 0 && errno == 0) + break; /* EOF found */ + if (IS_IO_ERROR(fd, w, timeout)) + goto err; /* errno set */ + + /* write would block */ + SM_IO_WR_TIMEOUT(fp, fd, timeout); + w = 0; + } + } + else + { + w = s; + COPY(w); + fp->f_w -= w; + fp->f_p += w; + } + if ((nldist -= w) == 0) + { + /* copied the newline: flush and forget */ + if (sm_flush(fp, &timeout)) + goto err; /* errno set */ + nlknown = 0; + } + p += w; + len -= w; + } while ((uio->uio_resid -= w) != 0); + } + + return 0; + +err: + /* errno set before goto places us here */ + fp->f_flags |= SMERR; + return SM_IO_EOF; +} diff --git a/usr/src/cmd/sendmail/libsm/fvwrite.h b/usr/src/cmd/sendmail/libsm/fvwrite.h new file mode 100644 index 0000000000..f7344e5535 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/fvwrite.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + * $Id: fvwrite.h,v 1.7 2001/03/02 00:18:19 ca Exp $ + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* I/O descriptors for sm_fvwrite() */ +struct sm_iov +{ + void *iov_base; + size_t iov_len; +}; +struct sm_uio +{ + struct sm_iov *uio_iov; + int uio_iovcnt; + int uio_resid; +}; + +extern int sm_fvwrite __P((SM_FILE_T *, int, struct sm_uio *)); diff --git a/usr/src/cmd/sendmail/libsm/fwalk.c b/usr/src/cmd/sendmail/libsm/fwalk.c new file mode 100644 index 0000000000..2c0522e989 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/fwalk.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: fwalk.c,v 1.19 2001/03/02 03:22:18 ca Exp $") +#include <errno.h> +#include <sm/io.h> +#include "local.h" +#include "glue.h" + +/* +** SM_FWALK -- apply a function to all found-open file pointers +** +** Parameters: +** function -- a function vector to be applied +** timeout -- time to complete actions (milliseconds) +** +** Returns: +** The (binary) OR'd result of each function call +*/ + +int +sm_fwalk(function, timeout) + int (*function) __P((SM_FILE_T *, int *)); + int *timeout; +{ + register SM_FILE_T *fp; + register int n, ret; + register struct sm_glue *g; + int fptimeout; + + ret = 0; + for (g = &smglue; g != NULL; g = g->gl_next) + { + for (fp = g->gl_iobs, n = g->gl_niobs; --n >= 0; fp++) + { + if (fp->f_flags != 0) + { + if (*timeout == SM_TIME_DEFAULT) + fptimeout = fp->f_timeout; + else + fptimeout = *timeout; + if (fptimeout == SM_TIME_IMMEDIATE) + continue; /* skip it */ + ret |= (*function)(fp, &fptimeout); + } + } + } + return ret; +} diff --git a/usr/src/cmd/sendmail/libsm/fwrite.c b/usr/src/cmd/sendmail/libsm/fwrite.c new file mode 100644 index 0000000000..f99eab1bb2 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/fwrite.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: fwrite.c,v 1.22 2001/04/03 01:46:40 ca Exp $") +#include <errno.h> +#include <sm/io.h> +#include <sm/assert.h> +#include "local.h" +#include "fvwrite.h" + +/* +** SM_IO_WRITE -- write to a file pointer +** +** Parameters: +** fp -- file pointer writing to +** timeout -- time to complete the write +** buf -- location of data to be written +** size -- number of bytes to be written +** +** Result: +** Failure: returns 0 _and_ sets errno +** Success: returns >=0 with errno unchanged, where the +** number returned is the number of bytes written. +*/ + +size_t +sm_io_write(fp, timeout, buf, size) + SM_FILE_T *fp; + int timeout; + const void *buf; + size_t size; +{ + struct sm_uio uio; + struct sm_iov iov; + + SM_REQUIRE_ISA(fp, SmFileMagic); + + if (fp->f_write == NULL) + { + errno = ENODEV; + return 0; + } + + iov.iov_base = (void *) buf; + uio.uio_resid = iov.iov_len = size; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + + /* The usual case is success (sm_fvwrite returns 0) */ + if (sm_fvwrite(fp, timeout, &uio) == 0) + return size; + + /* else return number of bytes actually written */ + return size - uio.uio_resid; +} diff --git a/usr/src/cmd/sendmail/libsm/get.c b/usr/src/cmd/sendmail/libsm/get.c new file mode 100644 index 0000000000..26226309f9 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/get.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: get.c,v 1.16 2001/01/28 00:29:35 ca Exp $") +#include <sm/io.h> +#include <sm/assert.h> +#include "local.h" + +/* +** SM_IO_GETC -- get a character from a file +** +** Parameters: +** fp -- the file to get the character from +** timeout -- time to complete getc +** +** Returns: +** Success: the value of the character read. +** Failure: SM_IO_EOF +** +** This is a function version of the macro (in <sm/io.h>). +** It is guarded with locks (which are currently not functional) +** for multi-threaded programs. +*/ + +#undef sm_io_getc + +int +sm_io_getc(fp, timeout) + register SM_FILE_T *fp; + int timeout; +{ + SM_REQUIRE_ISA(fp, SmFileMagic); + return sm_getc(fp, timeout); +} diff --git a/usr/src/cmd/sendmail/libsm/glue.h b/usr/src/cmd/sendmail/libsm/glue.h new file mode 100644 index 0000000000..6993bb4292 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/glue.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + * $Id: glue.h,v 1.6 2001/01/22 23:09:49 ca Exp $ + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* +** The first few FILEs are statically allocated; others are dynamically +** allocated and linked in via this glue structure. +*/ + +extern struct sm_glue +{ + struct sm_glue *gl_next; + int gl_niobs; + SM_FILE_T *gl_iobs; +} smglue; diff --git a/usr/src/cmd/sendmail/libsm/heap.c b/usr/src/cmd/sendmail/libsm/heap.c new file mode 100644 index 0000000000..03acf34555 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/heap.c @@ -0,0 +1,822 @@ +/* + * Copyright (c) 2000-2001, 2004 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: heap.c,v 1.51 2004/08/03 20:32:00 ca Exp $") + +/* +** debugging memory allocation package +** See heap.html for documentation. +*/ + +#include <string.h> + +#include <sm/assert.h> +#include <sm/debug.h> +#include <sm/exc.h> +#include <sm/heap.h> +#include <sm/io.h> +#include <sm/signal.h> +#include <sm/xtrap.h> + +/* undef all macro versions of the "functions" so they can be specified here */ +#undef sm_malloc +#undef sm_malloc_x +#undef sm_malloc_tagged +#undef sm_malloc_tagged_x +#undef sm_free +#undef sm_free_tagged +#undef sm_realloc +#if SM_HEAP_CHECK +# undef sm_heap_register +# undef sm_heap_checkptr +# undef sm_heap_report +#endif /* SM_HEAP_CHECK */ + +#if SM_HEAP_CHECK +SM_DEBUG_T SmHeapCheck = SM_DEBUG_INITIALIZER("sm_check_heap", + "@(#)$Debug: sm_check_heap - check sm_malloc, sm_realloc, sm_free calls $"); +# define HEAP_CHECK sm_debug_active(&SmHeapCheck, 1) +static int ptrhash __P((void *p)); +#endif /* SM_HEAP_CHECK */ + +const SM_EXC_TYPE_T SmHeapOutOfMemoryType = +{ + SmExcTypeMagic, + "F:sm.heap", + "", + sm_etype_printf, + "out of memory", +}; + +SM_EXC_T SmHeapOutOfMemory = SM_EXC_INITIALIZER(&SmHeapOutOfMemoryType, NULL); + + +/* +** The behaviour of malloc with size==0 is platform dependent (it +** says so in the C standard): it can return NULL or non-NULL. We +** don't want sm_malloc_x(0) to raise an exception on some platforms +** but not others, so this case requires special handling. We've got +** two choices: "size = 1" or "return NULL". We use the former in the +** following. +** If we had something like autoconf we could figure out the +** behaviour of the platform and either use this hack or just +** use size. +*/ + +#define MALLOC_SIZE(size) ((size) == 0 ? 1 : (size)) + +/* +** SM_MALLOC_X -- wrapper around malloc(), raises an exception on error. +** +** Parameters: +** size -- size of requested memory. +** +** Returns: +** Pointer to memory region. +** +** Note: +** sm_malloc_x only gets called from source files in which heap +** debugging is disabled at compile time. Otherwise, a call to +** sm_malloc_x is macro expanded to a call to sm_malloc_tagged_x. +** +** Exceptions: +** F:sm_heap -- out of memory +*/ + +void * +sm_malloc_x(size) + size_t size; +{ + void *ptr; + + ENTER_CRITICAL(); + ptr = malloc(MALLOC_SIZE(size)); + LEAVE_CRITICAL(); + if (ptr == NULL) + sm_exc_raise_x(&SmHeapOutOfMemory); + return ptr; +} + +#if !SM_HEAP_CHECK + +/* +** SM_MALLOC -- wrapper around malloc() +** +** Parameters: +** size -- size of requested memory. +** +** Returns: +** Pointer to memory region. +*/ + +void * +sm_malloc(size) + size_t size; +{ + void *ptr; + + ENTER_CRITICAL(); + ptr = malloc(MALLOC_SIZE(size)); + LEAVE_CRITICAL(); + return ptr; +} + +/* +** SM_REALLOC -- wrapper for realloc() +** +** Parameters: +** ptr -- pointer to old memory area. +** size -- size of requested memory. +** +** Returns: +** Pointer to new memory area, NULL on failure. +*/ + +void * +sm_realloc(ptr, size) + void *ptr; + size_t size; +{ + void *newptr; + + ENTER_CRITICAL(); + newptr = realloc(ptr, MALLOC_SIZE(size)); + LEAVE_CRITICAL(); + return newptr; +} + +/* +** SM_REALLOC_X -- wrapper for realloc() +** +** Parameters: +** ptr -- pointer to old memory area. +** size -- size of requested memory. +** +** Returns: +** Pointer to new memory area. +** +** Exceptions: +** F:sm_heap -- out of memory +*/ + +void * +sm_realloc_x(ptr, size) + void *ptr; + size_t size; +{ + void *newptr; + + ENTER_CRITICAL(); + newptr = realloc(ptr, MALLOC_SIZE(size)); + LEAVE_CRITICAL(); + if (newptr == NULL) + sm_exc_raise_x(&SmHeapOutOfMemory); + return newptr; +} +/* +** SM_FREE -- wrapper around free() +** +** Parameters: +** ptr -- pointer to memory region. +** +** Returns: +** none. +*/ + +void +sm_free(ptr) + void *ptr; +{ + if (ptr == NULL) + return; + ENTER_CRITICAL(); + free(ptr); + LEAVE_CRITICAL(); + return; +} + +#else /* !SM_HEAP_CHECK */ + +/* +** Each allocated block is assigned a "group number". +** By default, all blocks are assigned to group #1. +** By convention, group #0 is for memory that is never freed. +** You can use group numbers any way you want, in order to help make +** sense of sm_heap_report output. +*/ + +int SmHeapGroup = 1; +int SmHeapMaxGroup = 1; + +/* +** Total number of bytes allocated. +** This is only maintained if the sm_check_heap debug category is active. +*/ + +size_t SmHeapTotal = 0; + +/* +** High water mark: the most that SmHeapTotal has ever been. +*/ + +size_t SmHeapMaxTotal = 0; + +/* +** Maximum number of bytes that may be allocated at any one time. +** 0 means no limit. +** This is only honoured if sm_check_heap is active. +*/ + +SM_DEBUG_T SmHeapLimit = SM_DEBUG_INITIALIZER("sm_heap_limit", + "@(#)$Debug: sm_heap_limit - max # of bytes permitted in heap $"); + +/* +** This is the data structure that keeps track of all currently +** allocated blocks of memory known to the heap package. +*/ + +typedef struct sm_heap_item SM_HEAP_ITEM_T; +struct sm_heap_item +{ + void *hi_ptr; + size_t hi_size; + char *hi_tag; + int hi_num; + int hi_group; + SM_HEAP_ITEM_T *hi_next; +}; + +#define SM_HEAP_TABLE_SIZE 256 +static SM_HEAP_ITEM_T *SmHeapTable[SM_HEAP_TABLE_SIZE]; + +/* +** This is a randomly generated table +** which contains exactly one occurrence +** of each of the numbers between 0 and 255. +** It is used by ptrhash. +*/ + +static unsigned char hashtab[SM_HEAP_TABLE_SIZE] = +{ + 161, 71, 77,187, 15,229, 9,176,221,119,239, 21, 85,138,203, 86, + 102, 65, 80,199,235, 32,140, 96,224, 78,126,127,144, 0, 11,179, + 64, 30,120, 23,225,226, 33, 50,205,167,130,240,174, 99,206, 73, + 231,210,189,162, 48, 93,246, 54,213,141,135, 39, 41,192,236,193, + 157, 88, 95,104,188, 63,133,177,234,110,158,214,238,131,233, 91, + 125, 82, 94, 79, 66, 92,151, 45,252, 98, 26,183, 7,191,171,106, + 145,154,251,100,113, 5, 74, 62, 76,124, 14,217,200, 75,115,190, + 103, 28,198,196,169,219, 37,118,150, 18,152,175, 49,136, 6,142, + 89, 19,243,254, 47,137, 24,166,180, 10, 40,186,202, 46,184, 67, + 148,108,181, 81, 25,241, 13,139, 58, 38, 84,253,201, 12,116, 17, + 195, 22,112, 69,255, 43,147,222,111, 56,194,216,149,244, 42,173, + 232,220,249,105,207, 51,197,242, 72,211,208, 59,122,230,237,170, + 165, 44, 68,123,129,245,143,101, 8,209,215,247,185, 57,218, 53, + 114,121, 3,128, 4,204,212,146, 2,155, 83,250, 87, 29, 31,159, + 60, 27,107,156,227,182, 1, 61, 36,160,109, 97, 90, 20,168,132, + 223,248, 70,164, 55,172, 34, 52,163,117, 35,153,134, 16,178,228 +}; + +/* +** PTRHASH -- hash a pointer value +** +** Parameters: +** p -- pointer. +** +** Returns: +** hash value. +** +** ptrhash hashes a pointer value to a uniformly distributed random +** number between 0 and 255. +** +** This hash algorithm is based on Peter K. Pearson, +** "Fast Hashing of Variable-Length Text Strings", +** in Communications of the ACM, June 1990, vol 33 no 6. +*/ + +static int +ptrhash(p) + void *p; +{ + int h; + + if (sizeof(void*) == 4 && sizeof(unsigned long) == 4) + { + unsigned long n = (unsigned long)p; + + h = hashtab[n & 0xFF]; + h = hashtab[h ^ ((n >> 8) & 0xFF)]; + h = hashtab[h ^ ((n >> 16) & 0xFF)]; + h = hashtab[h ^ ((n >> 24) & 0xFF)]; + } +# if 0 + else if (sizeof(void*) == 8 && sizeof(unsigned long) == 8) + { + unsigned long n = (unsigned long)p; + + h = hashtab[n & 0xFF]; + h = hashtab[h ^ ((n >> 8) & 0xFF)]; + h = hashtab[h ^ ((n >> 16) & 0xFF)]; + h = hashtab[h ^ ((n >> 24) & 0xFF)]; + h = hashtab[h ^ ((n >> 32) & 0xFF)]; + h = hashtab[h ^ ((n >> 40) & 0xFF)]; + h = hashtab[h ^ ((n >> 48) & 0xFF)]; + h = hashtab[h ^ ((n >> 56) & 0xFF)]; + } +# endif /* 0 */ + else + { + unsigned char *cp = (unsigned char *)&p; + int i; + + h = 0; + for (i = 0; i < sizeof(void*); ++i) + h = hashtab[h ^ cp[i]]; + } + return h; +} + +/* +** SM_MALLOC_TAGGED -- wrapper around malloc(), debugging version. +** +** Parameters: +** size -- size of requested memory. +** tag -- tag for debugging. +** num -- additional value for debugging. +** group -- heap group for debugging. +** +** Returns: +** Pointer to memory region. +*/ + +void * +sm_malloc_tagged(size, tag, num, group) + size_t size; + char *tag; + int num; + int group; +{ + void *ptr; + + if (!HEAP_CHECK) + { + ENTER_CRITICAL(); + ptr = malloc(MALLOC_SIZE(size)); + LEAVE_CRITICAL(); + return ptr; + } + + if (sm_xtrap_check()) + return NULL; + if (sm_debug_active(&SmHeapLimit, 1) + && sm_debug_level(&SmHeapLimit) < SmHeapTotal + size) + return NULL; + ENTER_CRITICAL(); + ptr = malloc(MALLOC_SIZE(size)); + LEAVE_CRITICAL(); + if (ptr != NULL && !sm_heap_register(ptr, size, tag, num, group)) + { + ENTER_CRITICAL(); + free(ptr); + LEAVE_CRITICAL(); + ptr = NULL; + } + SmHeapTotal += size; + if (SmHeapTotal > SmHeapMaxTotal) + SmHeapMaxTotal = SmHeapTotal; + return ptr; +} + +/* +** SM_MALLOC_TAGGED_X -- wrapper around malloc(), debugging version. +** +** Parameters: +** size -- size of requested memory. +** tag -- tag for debugging. +** num -- additional value for debugging. +** group -- heap group for debugging. +** +** Returns: +** Pointer to memory region. +** +** Exceptions: +** F:sm_heap -- out of memory +*/ + +void * +sm_malloc_tagged_x(size, tag, num, group) + size_t size; + char *tag; + int num; + int group; +{ + void *ptr; + + if (!HEAP_CHECK) + { + ENTER_CRITICAL(); + ptr = malloc(MALLOC_SIZE(size)); + LEAVE_CRITICAL(); + if (ptr == NULL) + sm_exc_raise_x(&SmHeapOutOfMemory); + return ptr; + } + + sm_xtrap_raise_x(&SmHeapOutOfMemory); + if (sm_debug_active(&SmHeapLimit, 1) + && sm_debug_level(&SmHeapLimit) < SmHeapTotal + size) + { + sm_exc_raise_x(&SmHeapOutOfMemory); + } + ENTER_CRITICAL(); + ptr = malloc(MALLOC_SIZE(size)); + LEAVE_CRITICAL(); + if (ptr != NULL && !sm_heap_register(ptr, size, tag, num, group)) + { + ENTER_CRITICAL(); + free(ptr); + LEAVE_CRITICAL(); + ptr = NULL; + } + if (ptr == NULL) + sm_exc_raise_x(&SmHeapOutOfMemory); + SmHeapTotal += size; + if (SmHeapTotal > SmHeapMaxTotal) + SmHeapMaxTotal = SmHeapTotal; + return ptr; +} + +/* +** SM_HEAP_REGISTER -- register a pointer into the heap for debugging. +** +** Parameters: +** ptr -- pointer to register. +** size -- size of requested memory. +** tag -- tag for debugging. +** num -- additional value for debugging. +** group -- heap group for debugging. +** +** Returns: +** true iff successfully registered (not yet in table). +*/ + +bool +sm_heap_register(ptr, size, tag, num, group) + void *ptr; + size_t size; + char *tag; + int num; + int group; +{ + int i; + SM_HEAP_ITEM_T *hi; + + if (!HEAP_CHECK) + return true; + SM_REQUIRE(ptr != NULL); + i = ptrhash(ptr); +# if SM_CHECK_REQUIRE + + /* + ** We require that ptr is not already in SmHeapTable. + */ + + for (hi = SmHeapTable[i]; hi != NULL; hi = hi->hi_next) + { + if (hi->hi_ptr == ptr) + sm_abort("sm_heap_register: ptr %p is already registered (%s:%d)", + ptr, hi->hi_tag, hi->hi_num); + } +# endif /* SM_CHECK_REQUIRE */ + ENTER_CRITICAL(); + hi = (SM_HEAP_ITEM_T *) malloc(sizeof(SM_HEAP_ITEM_T)); + LEAVE_CRITICAL(); + if (hi == NULL) + return false; + hi->hi_ptr = ptr; + hi->hi_size = size; + hi->hi_tag = tag; + hi->hi_num = num; + hi->hi_group = group; + hi->hi_next = SmHeapTable[i]; + SmHeapTable[i] = hi; + return true; +} +/* +** SM_REALLOC -- wrapper for realloc(), debugging version. +** +** Parameters: +** ptr -- pointer to old memory area. +** size -- size of requested memory. +** +** Returns: +** Pointer to new memory area, NULL on failure. +*/ + +void * +sm_realloc(ptr, size) + void *ptr; + size_t size; +{ + void *newptr; + SM_HEAP_ITEM_T *hi, **hp; + + if (!HEAP_CHECK) + { + ENTER_CRITICAL(); + newptr = realloc(ptr, MALLOC_SIZE(size)); + LEAVE_CRITICAL(); + return newptr; + } + + if (ptr == NULL) + return sm_malloc_tagged(size, "realloc", 0, SmHeapGroup); + + for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next) + { + if ((**hp).hi_ptr == ptr) + { + if (sm_xtrap_check()) + return NULL; + hi = *hp; + if (sm_debug_active(&SmHeapLimit, 1) + && sm_debug_level(&SmHeapLimit) + < SmHeapTotal - hi->hi_size + size) + { + return NULL; + } + ENTER_CRITICAL(); + newptr = realloc(ptr, MALLOC_SIZE(size)); + LEAVE_CRITICAL(); + if (newptr == NULL) + return NULL; + SmHeapTotal = SmHeapTotal - hi->hi_size + size; + if (SmHeapTotal > SmHeapMaxTotal) + SmHeapMaxTotal = SmHeapTotal; + *hp = hi->hi_next; + hi->hi_ptr = newptr; + hi->hi_size = size; + hp = &SmHeapTable[ptrhash(newptr)]; + hi->hi_next = *hp; + *hp = hi; + return newptr; + } + } + sm_abort("sm_realloc: bad argument (%p)", ptr); + /* NOTREACHED */ + return NULL; /* keep Irix compiler happy */ +} + +/* +** SM_REALLOC_X -- wrapper for realloc(), debugging version. +** +** Parameters: +** ptr -- pointer to old memory area. +** size -- size of requested memory. +** +** Returns: +** Pointer to new memory area. +** +** Exceptions: +** F:sm_heap -- out of memory +*/ + +void * +sm_realloc_x(ptr, size) + void *ptr; + size_t size; +{ + void *newptr; + SM_HEAP_ITEM_T *hi, **hp; + + if (!HEAP_CHECK) + { + ENTER_CRITICAL(); + newptr = realloc(ptr, MALLOC_SIZE(size)); + LEAVE_CRITICAL(); + if (newptr == NULL) + sm_exc_raise_x(&SmHeapOutOfMemory); + return newptr; + } + + if (ptr == NULL) + return sm_malloc_tagged_x(size, "realloc", 0, SmHeapGroup); + + for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next) + { + if ((**hp).hi_ptr == ptr) + { + sm_xtrap_raise_x(&SmHeapOutOfMemory); + hi = *hp; + if (sm_debug_active(&SmHeapLimit, 1) + && sm_debug_level(&SmHeapLimit) + < SmHeapTotal - hi->hi_size + size) + { + sm_exc_raise_x(&SmHeapOutOfMemory); + } + ENTER_CRITICAL(); + newptr = realloc(ptr, MALLOC_SIZE(size)); + LEAVE_CRITICAL(); + if (newptr == NULL) + sm_exc_raise_x(&SmHeapOutOfMemory); + SmHeapTotal = SmHeapTotal - hi->hi_size + size; + if (SmHeapTotal > SmHeapMaxTotal) + SmHeapMaxTotal = SmHeapTotal; + *hp = hi->hi_next; + hi->hi_ptr = newptr; + hi->hi_size = size; + hp = &SmHeapTable[ptrhash(newptr)]; + hi->hi_next = *hp; + *hp = hi; + return newptr; + } + } + sm_abort("sm_realloc_x: bad argument (%p)", ptr); + /* NOTREACHED */ + return NULL; /* keep Irix compiler happy */ +} + +/* +** SM_FREE_TAGGED -- wrapper around free(), debugging version. +** +** Parameters: +** ptr -- pointer to memory region. +** tag -- tag for debugging. +** num -- additional value for debugging. +** +** Returns: +** none. +*/ + +void +sm_free_tagged(ptr, tag, num) + void *ptr; + char *tag; + int num; +{ + SM_HEAP_ITEM_T **hp; + + if (ptr == NULL) + return; + if (!HEAP_CHECK) + { + ENTER_CRITICAL(); + free(ptr); + LEAVE_CRITICAL(); + return; + } + for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next) + { + if ((**hp).hi_ptr == ptr) + { + SM_HEAP_ITEM_T *hi = *hp; + + *hp = hi->hi_next; + + /* + ** Fill the block with zeros before freeing. + ** This is intended to catch problems with + ** dangling pointers. The block is filled with + ** zeros, not with some non-zero value, because + ** it is common practice in some C code to store + ** a zero in a structure member before freeing the + ** structure, as a defense against dangling pointers. + */ + + (void) memset(ptr, 0, hi->hi_size); + SmHeapTotal -= hi->hi_size; + ENTER_CRITICAL(); + free(ptr); + free(hi); + LEAVE_CRITICAL(); + return; + } + } + sm_abort("sm_free: bad argument (%p) (%s:%d)", ptr, tag, num); +} + +/* +** SM_HEAP_CHECKPTR_TAGGED -- check whether ptr is a valid argument to sm_free +** +** Parameters: +** ptr -- pointer to memory region. +** tag -- tag for debugging. +** num -- additional value for debugging. +** +** Returns: +** none. +** +** Side Effects: +** aborts if check fails. +*/ + +void +sm_heap_checkptr_tagged(ptr, tag, num) + void *ptr; + char *tag; + int num; +{ + SM_HEAP_ITEM_T *hp; + + if (!HEAP_CHECK) + return; + if (ptr == NULL) + return; + for (hp = SmHeapTable[ptrhash(ptr)]; hp != NULL; hp = hp->hi_next) + { + if (hp->hi_ptr == ptr) + return; + } + sm_abort("sm_heap_checkptr(%p): bad ptr (%s:%d)", ptr, tag, num); +} + +/* +** SM_HEAP_REPORT -- output "map" of used heap. +** +** Parameters: +** stream -- the file pointer to write to. +** verbosity -- how much info? +** +** Returns: +** none. +*/ + +void +sm_heap_report(stream, verbosity) + SM_FILE_T *stream; + int verbosity; +{ + int i; + unsigned long group0total, group1total, otherstotal, grandtotal; + + if (!HEAP_CHECK || verbosity <= 0) + return; + group0total = group1total = otherstotal = grandtotal = 0; + for (i = 0; i < sizeof(SmHeapTable) / sizeof(SmHeapTable[0]); ++i) + { + SM_HEAP_ITEM_T *hi = SmHeapTable[i]; + + while (hi != NULL) + { + if (verbosity > 2 + || (verbosity > 1 && hi->hi_group != 0)) + { + sm_io_fprintf(stream, SM_TIME_DEFAULT, + "%4d %*lx %7lu bytes", + hi->hi_group, + (int) sizeof(void *) * 2, + (long)hi->hi_ptr, + (unsigned long)hi->hi_size); + if (hi->hi_tag != NULL) + { + sm_io_fprintf(stream, SM_TIME_DEFAULT, + " %s", + hi->hi_tag); + if (hi->hi_num) + { + sm_io_fprintf(stream, + SM_TIME_DEFAULT, + ":%d", + hi->hi_num); + } + } + sm_io_fprintf(stream, SM_TIME_DEFAULT, "\n"); + } + switch (hi->hi_group) + { + case 0: + group0total += hi->hi_size; + break; + case 1: + group1total += hi->hi_size; + break; + default: + otherstotal += hi->hi_size; + break; + } + grandtotal += hi->hi_size; + hi = hi->hi_next; + } + } + sm_io_fprintf(stream, SM_TIME_DEFAULT, + "heap max=%lu, total=%lu, ", + (unsigned long) SmHeapMaxTotal, grandtotal); + sm_io_fprintf(stream, SM_TIME_DEFAULT, + "group 0=%lu, group 1=%lu, others=%lu\n", + group0total, group1total, otherstotal); + if (grandtotal != SmHeapTotal) + { + sm_io_fprintf(stream, SM_TIME_DEFAULT, + "BUG => SmHeapTotal: got %lu, expected %lu\n", + (unsigned long) SmHeapTotal, grandtotal); + } +} +#endif /* !SM_HEAP_CHECK */ diff --git a/usr/src/cmd/sendmail/libsm/ldap.c b/usr/src/cmd/sendmail/libsm/ldap.c new file mode 100644 index 0000000000..f438b91823 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/ldap.c @@ -0,0 +1,1334 @@ +/* + * Copyright (c) 2001-2005 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: ldap.c,v 1.62 2005/02/24 00:30:01 ca Exp $") + +#if LDAPMAP +# include <sys/types.h> +# include <errno.h> +# include <setjmp.h> +# include <stdlib.h> +# include <unistd.h> + +# include <sm/bitops.h> +# include <sm/clock.h> +# include <sm/conf.h> +# include <sm/debug.h> +# include <sm/errstring.h> +# include <sm/ldap.h> +# include <sm/string.h> +# ifdef EX_OK +# undef EX_OK /* for SVr4.2 SMP */ +# endif /* EX_OK */ +# include <sm/sysexits.h> + +SM_DEBUG_T SmLDAPTrace = SM_DEBUG_INITIALIZER("sm_trace_ldap", + "@(#)$Debug: sm_trace_ldap - trace LDAP operations $"); + +static void ldaptimeout __P((int)); +static bool sm_ldap_has_objectclass __P((SM_LDAP_STRUCT *, LDAPMessage *, char *)); +static SM_LDAP_RECURSE_ENTRY *sm_ldap_add_recurse __P((SM_LDAP_RECURSE_LIST **, char *, int, SM_RPOOL_T *)); + +/* +** SM_LDAP_CLEAR -- set default values for SM_LDAP_STRUCT +** +** Parameters: +** lmap -- pointer to SM_LDAP_STRUCT to clear +** +** Returns: +** None. +** +*/ + +void +sm_ldap_clear(lmap) + SM_LDAP_STRUCT *lmap; +{ + if (lmap == NULL) + return; + + lmap->ldap_host = NULL; + lmap->ldap_port = LDAP_PORT; + lmap->ldap_uri = NULL; + lmap->ldap_version = 0; + lmap->ldap_deref = LDAP_DEREF_NEVER; + lmap->ldap_timelimit = LDAP_NO_LIMIT; + lmap->ldap_sizelimit = LDAP_NO_LIMIT; +# ifdef LDAP_REFERRALS + lmap->ldap_options = LDAP_OPT_REFERRALS; +# else /* LDAP_REFERRALS */ + lmap->ldap_options = 0; +# endif /* LDAP_REFERRALS */ + lmap->ldap_attrsep = '\0'; + lmap->ldap_binddn = NULL; + lmap->ldap_secret = NULL; + lmap->ldap_method = LDAP_AUTH_SIMPLE; + lmap->ldap_base = NULL; + lmap->ldap_scope = LDAP_SCOPE_SUBTREE; + lmap->ldap_attrsonly = LDAPMAP_FALSE; + lmap->ldap_timeout.tv_sec = 0; + lmap->ldap_timeout.tv_usec = 0; + lmap->ldap_ld = NULL; + lmap->ldap_filter = NULL; + lmap->ldap_attr[0] = NULL; + lmap->ldap_attr_type[0] = SM_LDAP_ATTR_NONE; + lmap->ldap_attr_needobjclass[0] = NULL; + lmap->ldap_res = NULL; + lmap->ldap_next = NULL; + lmap->ldap_pid = 0; +} + +/* +** SM_LDAP_START -- actually connect to an LDAP server +** +** Parameters: +** name -- name of map for debug output. +** lmap -- the LDAP map being opened. +** +** Returns: +** true if connection is successful, false otherwise. +** +** Side Effects: +** Populates lmap->ldap_ld. +*/ + +static jmp_buf LDAPTimeout; + +#define SM_LDAP_SETTIMEOUT(to) \ +do \ +{ \ + if (to != 0) \ + { \ + if (setjmp(LDAPTimeout) != 0) \ + { \ + errno = ETIMEDOUT; \ + return false; \ + } \ + ev = sm_setevent(to, ldaptimeout, 0); \ + } \ +} while (0) + +#define SM_LDAP_CLEARTIMEOUT() \ +do \ +{ \ + if (ev != NULL) \ + sm_clrevent(ev); \ +} while (0) + +bool +sm_ldap_start(name, lmap) + char *name; + SM_LDAP_STRUCT *lmap; +{ + int bind_result; + int save_errno = 0; + char *id; + SM_EVENT *ev = NULL; + LDAP *ld = NULL; + + if (sm_debug_active(&SmLDAPTrace, 2)) + sm_dprintf("ldapmap_start(%s)\n", name == NULL ? "" : name); + + if (lmap->ldap_host != NULL) + id = lmap->ldap_host; + else if (lmap->ldap_uri != NULL) + id = lmap->ldap_uri; + else + id = "localhost"; + + if (sm_debug_active(&SmLDAPTrace, 9)) + { + /* Don't print a port number for LDAP URIs */ + if (lmap->ldap_uri != NULL) + sm_dprintf("ldapmap_start(%s)\n", id); + else + sm_dprintf("ldapmap_start(%s, %d)\n", id, + lmap->ldap_port); + } + + if (lmap->ldap_uri != NULL) + { +#if SM_CONF_LDAP_INITIALIZE + /* LDAP server supports URIs so use them directly */ + save_errno = ldap_initialize(&ld, lmap->ldap_uri); +#else /* SM_CONF_LDAP_INITIALIZE */ + int err; + LDAPURLDesc *ludp = NULL; + + /* Blast apart URL and use the ldap_init/ldap_open below */ + err = ldap_url_parse(lmap->ldap_uri, &ludp); + if (err != 0) + { + errno = err + E_LDAPURLBASE; + return false; + } + lmap->ldap_host = sm_strdup_x(ludp->lud_host); + if (lmap->ldap_host == NULL) + { + save_errno = errno; + ldap_free_urldesc(ludp); + errno = save_errno; + return false; + } + lmap->ldap_port = ludp->lud_port; + ldap_free_urldesc(ludp); +#endif /* SM_CONF_LDAP_INITIALIZE */ + } + + if (ld == NULL) + { +# if USE_LDAP_INIT + ld = ldap_init(lmap->ldap_host, lmap->ldap_port); + save_errno = errno; +# else /* USE_LDAP_INIT */ + /* + ** If using ldap_open(), the actual connection to the server + ** happens now so we need the timeout here. For ldap_init(), + ** the connection happens at bind time. + */ + + SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec); + ld = ldap_open(lmap->ldap_host, lmap->ldap_port); + save_errno = errno; + + /* clear the event if it has not sprung */ + SM_LDAP_CLEARTIMEOUT(); +# endif /* USE_LDAP_INIT */ + } + + errno = save_errno; + if (ld == NULL) + return false; + + sm_ldap_setopts(ld, lmap); + +# if USE_LDAP_INIT + /* + ** If using ldap_init(), the actual connection to the server + ** happens at ldap_bind_s() so we need the timeout here. + */ + + SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec); +# endif /* USE_LDAP_INIT */ + +# ifdef LDAP_AUTH_KRBV4 + if (lmap->ldap_method == LDAP_AUTH_KRBV4 && + lmap->ldap_secret != NULL) + { + /* + ** Need to put ticket in environment here instead of + ** during parseargs as there may be different tickets + ** for different LDAP connections. + */ + + (void) putenv(lmap->ldap_secret); + } +# endif /* LDAP_AUTH_KRBV4 */ + + bind_result = ldap_bind_s(ld, lmap->ldap_binddn, + lmap->ldap_secret, lmap->ldap_method); + +# if USE_LDAP_INIT + /* clear the event if it has not sprung */ + SM_LDAP_CLEARTIMEOUT(); +# endif /* USE_LDAP_INIT */ + + if (bind_result != LDAP_SUCCESS) + { + errno = bind_result + E_LDAPBASE; + return false; + } + + /* Save PID to make sure only this PID closes the LDAP connection */ + lmap->ldap_pid = getpid(); + lmap->ldap_ld = ld; + return true; +} + +/* ARGSUSED */ +static void +ldaptimeout(unused) + int unused; +{ + /* + ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD + ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE + ** DOING. + */ + + errno = ETIMEDOUT; + longjmp(LDAPTimeout, 1); +} + +/* +** SM_LDAP_SEARCH -- initiate LDAP search +** +** Initiate an LDAP search, return the msgid. +** The calling function must collect the results. +** +** Parameters: +** lmap -- LDAP map information +** key -- key to substitute in LDAP filter +** +** Returns: +** -1 on failure, msgid on success +** +*/ + +int +sm_ldap_search(lmap, key) + SM_LDAP_STRUCT *lmap; + char *key; +{ + int msgid; + char *fp, *p, *q; + char filter[LDAPMAP_MAX_FILTER + 1]; + + /* substitute key into filter, perhaps multiple times */ + memset(filter, '\0', sizeof filter); + fp = filter; + p = lmap->ldap_filter; + while ((q = strchr(p, '%')) != NULL) + { + if (q[1] == 's') + { + (void) sm_snprintf(fp, SPACELEFT(filter, fp), + "%.*s%s", (int) (q - p), p, key); + fp += strlen(fp); + p = q + 2; + } + else if (q[1] == '0') + { + char *k = key; + + (void) sm_snprintf(fp, SPACELEFT(filter, fp), + "%.*s", (int) (q - p), p); + fp += strlen(fp); + p = q + 2; + + /* Properly escape LDAP special characters */ + while (SPACELEFT(filter, fp) > 0 && + *k != '\0') + { + if (*k == '*' || *k == '(' || + *k == ')' || *k == '\\') + { + (void) sm_strlcat(fp, + (*k == '*' ? "\\2A" : + (*k == '(' ? "\\28" : + (*k == ')' ? "\\29" : + (*k == '\\' ? "\\5C" : + "\00")))), + SPACELEFT(filter, fp)); + fp += strlen(fp); + k++; + } + else + *fp++ = *k++; + } + } + else + { + (void) sm_snprintf(fp, SPACELEFT(filter, fp), + "%.*s", (int) (q - p + 1), p); + p = q + (q[1] == '%' ? 2 : 1); + fp += strlen(fp); + } + } + (void) sm_strlcpy(fp, p, SPACELEFT(filter, fp)); + if (sm_debug_active(&SmLDAPTrace, 20)) + sm_dprintf("ldap search filter=%s\n", filter); + + lmap->ldap_res = NULL; + msgid = ldap_search(lmap->ldap_ld, lmap->ldap_base, + lmap->ldap_scope, filter, + (lmap->ldap_attr[0] == NULL ? NULL : + lmap->ldap_attr), + lmap->ldap_attrsonly); + return msgid; +} + +/* +** SM_LDAP_HAS_OBJECTCLASS -- determine if an LDAP entry is part of a +** particular objectClass +** +** Parameters: +** lmap -- pointer to SM_LDAP_STRUCT in use +** entry -- current LDAP entry struct +** ocvalue -- particular objectclass in question. +** may be of form (fee|foo|fum) meaning +** any entry can be part of either fee, +** foo or fum objectclass +** +** Returns: +** true if item has that objectClass +*/ + +static bool +sm_ldap_has_objectclass(lmap, entry, ocvalue) + SM_LDAP_STRUCT *lmap; + LDAPMessage *entry; + char *ocvalue; +{ + char **vals = NULL; + int i; + + if (ocvalue == NULL) + return false; + + vals = ldap_get_values(lmap->ldap_ld, entry, "objectClass"); + if (vals == NULL) + return false; + + for (i = 0; vals[i] != NULL; i++) + { + char *p; + char *q; + + p = q = ocvalue; + while (*p != '\0') + { + while (*p != '\0' && *p != '|') + p++; + + if ((p - q) == strlen(vals[i]) && + sm_strncasecmp(vals[i], q, p - q) == 0) + { + ldap_value_free(vals); + return true; + } + + while (*p == '|') + p++; + q = p; + } + } + + ldap_value_free(vals); + return false; +} + +/* +** SM_LDAP_RESULTS -- return results from an LDAP lookup in result +** +** Parameters: +** lmap -- pointer to SM_LDAP_STRUCT in use +** msgid -- msgid returned by sm_ldap_search() +** flags -- flags for the lookup +** delim -- delimiter for result concatenation +** rpool -- memory pool for storage +** result -- return string +** recurse -- recursion list +** +** Returns: +** status (sysexit) +*/ + +# define SM_LDAP_ERROR_CLEANUP() \ +{ \ + if (lmap->ldap_res != NULL) \ + { \ + ldap_msgfree(lmap->ldap_res); \ + lmap->ldap_res = NULL; \ + } \ + (void) ldap_abandon(lmap->ldap_ld, msgid); \ +} + +static SM_LDAP_RECURSE_ENTRY * +sm_ldap_add_recurse(top, item, type, rpool) + SM_LDAP_RECURSE_LIST **top; + char *item; + int type; + SM_RPOOL_T *rpool; +{ + int n; + int m; + int p; + int insertat; + int moveb; + int oldsizeb; + int rc; + SM_LDAP_RECURSE_ENTRY *newe; + SM_LDAP_RECURSE_ENTRY **olddata; + + /* + ** This code will maintain a list of + ** SM_LDAP_RECURSE_ENTRY structures + ** in ascending order. + */ + + if (*top == NULL) + { + /* Allocate an initial SM_LDAP_RECURSE_LIST struct */ + *top = sm_rpool_malloc_x(rpool, sizeof **top); + (*top)->lr_cnt = 0; + (*top)->lr_size = 0; + (*top)->lr_data = NULL; + } + + if ((*top)->lr_cnt >= (*top)->lr_size) + { + /* Grow the list of SM_LDAP_RECURSE_ENTRY ptrs */ + olddata = (*top)->lr_data; + if ((*top)->lr_size == 0) + { + oldsizeb = 0; + (*top)->lr_size = 256; + } + else + { + oldsizeb = (*top)->lr_size * sizeof *((*top)->lr_data); + (*top)->lr_size *= 2; + } + (*top)->lr_data = sm_rpool_malloc_x(rpool, + (*top)->lr_size * sizeof *((*top)->lr_data)); + if (oldsizeb > 0) + memcpy((*top)->lr_data, olddata, oldsizeb); + } + + /* + ** Binary search/insert item:type into list. + ** Return current entry pointer if already exists. + */ + + n = 0; + m = (*top)->lr_cnt - 1; + if (m < 0) + insertat = 0; + else + insertat = -1; + + while (insertat == -1) + { + p = (m + n) / 2; + + rc = sm_strcasecmp(item, (*top)->lr_data[p]->lr_search); + if (rc == 0) + rc = type - (*top)->lr_data[p]->lr_type; + + if (rc < 0) + m = p - 1; + else if (rc > 0) + n = p + 1; + else + return (*top)->lr_data[p]; + + if (m == -1) + insertat = 0; + else if (n >= (*top)->lr_cnt) + insertat = (*top)->lr_cnt; + else if (m < n) + insertat = m + 1; + } + + /* + ** Not found in list, make room + ** at insert point and add it. + */ + + newe = sm_rpool_malloc_x(rpool, sizeof *newe); + if (newe != NULL) + { + moveb = ((*top)->lr_cnt - insertat) * sizeof *((*top)->lr_data); + if (moveb > 0) + memmove(&((*top)->lr_data[insertat + 1]), + &((*top)->lr_data[insertat]), + moveb); + + newe->lr_search = sm_rpool_strdup_x(rpool, item); + newe->lr_type = type; + newe->lr_ludp = NULL; + newe->lr_attrs = NULL; + newe->lr_done = false; + + ((*top)->lr_data)[insertat] = newe; + (*top)->lr_cnt++; + } + return newe; +} + +int +sm_ldap_results(lmap, msgid, flags, delim, rpool, result, + resultln, resultsz, recurse) + SM_LDAP_STRUCT *lmap; + int msgid; + int flags; + int delim; + SM_RPOOL_T *rpool; + char **result; + int *resultln; + int *resultsz; + SM_LDAP_RECURSE_LIST *recurse; +{ + bool toplevel; + int i; + int statp; + int vsize; + int ret; + int save_errno; + char *p; + SM_LDAP_RECURSE_ENTRY *rl; + + /* Are we the top top level of the search? */ + toplevel = (recurse == NULL); + + /* Get results */ + statp = EX_NOTFOUND; + while ((ret = ldap_result(lmap->ldap_ld, msgid, 0, + (lmap->ldap_timeout.tv_sec == 0 ? NULL : + &(lmap->ldap_timeout)), + &(lmap->ldap_res))) == LDAP_RES_SEARCH_ENTRY) + { + LDAPMessage *entry; + + /* If we don't want multiple values and we have one, break */ + if ((char) delim == '\0' && + !bitset(SM_LDAP_SINGLEMATCH, flags) && + *result != NULL) + break; + + /* Cycle through all entries */ + for (entry = ldap_first_entry(lmap->ldap_ld, lmap->ldap_res); + entry != NULL; + entry = ldap_next_entry(lmap->ldap_ld, lmap->ldap_res)) + { + BerElement *ber; + char *attr; + char **vals = NULL; + char *dn; + + /* + ** If matching only and found an entry, + ** no need to spin through attributes + */ + + if (bitset(SM_LDAP_MATCHONLY, flags)) + { + statp = EX_OK; + continue; + } + + /* record completed DN's to prevent loops */ + dn = ldap_get_dn(lmap->ldap_ld, entry); + if (dn == NULL) + { + save_errno = sm_ldap_geterrno(lmap->ldap_ld); + save_errno += E_LDAPBASE; + SM_LDAP_ERROR_CLEANUP(); + errno = save_errno; + return EX_TEMPFAIL; + } + + rl = sm_ldap_add_recurse(&recurse, dn, + SM_LDAP_ATTR_DN, + rpool); + + if (rl == NULL) + { + ldap_memfree(dn); + SM_LDAP_ERROR_CLEANUP(); + errno = ENOMEM; + return EX_OSERR; + } + else if (rl->lr_done) + { + /* already on list, skip it */ + ldap_memfree(dn); + continue; + } + ldap_memfree(dn); + +# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT) + /* + ** Reset value to prevent lingering + ** LDAP_DECODING_ERROR due to + ** OpenLDAP 1.X's hack (see below) + */ + + lmap->ldap_ld->ld_errno = LDAP_SUCCESS; +# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */ + + for (attr = ldap_first_attribute(lmap->ldap_ld, entry, + &ber); + attr != NULL; + attr = ldap_next_attribute(lmap->ldap_ld, entry, + ber)) + { + char *tmp, *vp_tmp; + int type; + char *needobjclass = NULL; + + type = SM_LDAP_ATTR_NONE; + for (i = 0; lmap->ldap_attr[i] != NULL; i++) + { + if (sm_strcasecmp(lmap->ldap_attr[i], + attr) == 0) + { + type = lmap->ldap_attr_type[i]; + needobjclass = lmap->ldap_attr_needobjclass[i]; + break; + } + } + + if (bitset(SM_LDAP_USE_ALLATTR, flags) && + type == SM_LDAP_ATTR_NONE) + { + /* URL lookups specify attrs to use */ + type = SM_LDAP_ATTR_NORMAL; + needobjclass = NULL; + } + + if (type == SM_LDAP_ATTR_NONE) + { + /* attribute not requested */ + ldap_memfree(attr); + SM_LDAP_ERROR_CLEANUP(); + errno = EFAULT; + return EX_SOFTWARE; + } + + /* + ** For recursion on a particular attribute, + ** we may need to see if this entry is + ** part of a particular objectclass. + ** Also, ignore objectClass attribute. + ** Otherwise we just ignore this attribute. + */ + + if (type == SM_LDAP_ATTR_OBJCLASS || + (needobjclass != NULL && + !sm_ldap_has_objectclass(lmap, entry, + needobjclass))) + { + ldap_memfree(attr); + continue; + } + + if (lmap->ldap_attrsonly == LDAPMAP_FALSE) + { + vals = ldap_get_values(lmap->ldap_ld, + entry, + attr); + if (vals == NULL) + { + save_errno = sm_ldap_geterrno(lmap->ldap_ld); + if (save_errno == LDAP_SUCCESS) + { + ldap_memfree(attr); + continue; + } + + /* Must be an error */ + save_errno += E_LDAPBASE; + ldap_memfree(attr); + SM_LDAP_ERROR_CLEANUP(); + errno = save_errno; + return EX_TEMPFAIL; + } + } + + statp = EX_OK; + +# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT) + /* + ** Reset value to prevent lingering + ** LDAP_DECODING_ERROR due to + ** OpenLDAP 1.X's hack (see below) + */ + + lmap->ldap_ld->ld_errno = LDAP_SUCCESS; +# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */ + + /* + ** If matching only, + ** no need to spin through entries + */ + + if (bitset(SM_LDAP_MATCHONLY, flags)) + { + if (lmap->ldap_attrsonly == LDAPMAP_FALSE) + ldap_value_free(vals); + ldap_memfree(attr); + continue; + } + + /* + ** If we don't want multiple values, + ** return first found. + */ + + if ((char) delim == '\0') + { + if (*result != NULL) + { + /* already have a value */ + if (bitset(SM_LDAP_SINGLEMATCH, + flags)) + { + /* only wanted one match */ + SM_LDAP_ERROR_CLEANUP(); + errno = ENOENT; + return EX_NOTFOUND; + } + break; + } + + if (lmap->ldap_attrsonly == LDAPMAP_TRUE) + { + *result = sm_rpool_strdup_x(rpool, + attr); + ldap_memfree(attr); + break; + } + + if (vals[0] == NULL) + { + ldap_value_free(vals); + ldap_memfree(attr); + continue; + } + + vsize = strlen(vals[0]) + 1; + if (lmap->ldap_attrsep != '\0') + vsize += strlen(attr) + 1; + *result = sm_rpool_malloc_x(rpool, + vsize); + if (lmap->ldap_attrsep != '\0') + sm_snprintf(*result, vsize, + "%s%c%s", + attr, + lmap->ldap_attrsep, + vals[0]); + else + sm_strlcpy(*result, vals[0], + vsize); + ldap_value_free(vals); + ldap_memfree(attr); + break; + } + + /* attributes only */ + if (lmap->ldap_attrsonly == LDAPMAP_TRUE) + { + if (*result == NULL) + *result = sm_rpool_strdup_x(rpool, + attr); + else + { + if (bitset(SM_LDAP_SINGLEMATCH, + flags) && + *result != NULL) + { + /* only wanted one match */ + SM_LDAP_ERROR_CLEANUP(); + errno = ENOENT; + return EX_NOTFOUND; + } + + vsize = strlen(*result) + + strlen(attr) + 2; + tmp = sm_rpool_malloc_x(rpool, + vsize); + (void) sm_snprintf(tmp, + vsize, "%s%c%s", + *result, (char) delim, + attr); + *result = tmp; + } + ldap_memfree(attr); + continue; + } + + /* + ** If there is more than one, munge then + ** into a map_coldelim separated string. + ** If we are recursing we may have an entry + ** with no 'normal' values to put in the + ** string. + ** This is not an error. + */ + + if (type == SM_LDAP_ATTR_NORMAL && + bitset(SM_LDAP_SINGLEMATCH, flags) && + *result != NULL) + { + /* only wanted one match */ + SM_LDAP_ERROR_CLEANUP(); + errno = ENOENT; + return EX_NOTFOUND; + } + + vsize = 0; + for (i = 0; vals[i] != NULL; i++) + { + if (type == SM_LDAP_ATTR_DN || + type == SM_LDAP_ATTR_FILTER || + type == SM_LDAP_ATTR_URL) + { + /* add to recursion */ + if (sm_ldap_add_recurse(&recurse, + vals[i], + type, + rpool) == NULL) + { + SM_LDAP_ERROR_CLEANUP(); + errno = ENOMEM; + return EX_OSERR; + } + continue; + } + + vsize += strlen(vals[i]) + 1; + if (lmap->ldap_attrsep != '\0') + vsize += strlen(attr) + 1; + } + + /* + ** Create/Append to string any normal + ** attribute values. Otherwise, just free + ** memory and move on to the next + ** attribute in this entry. + */ + + if (type == SM_LDAP_ATTR_NORMAL && vsize > 0) + { + char *pe; + + /* Grow result string if needed */ + if ((*resultln + vsize) >= *resultsz) + { + while ((*resultln + vsize) >= *resultsz) + { + if (*resultsz == 0) + *resultsz = 1024; + else + *resultsz *= 2; + } + + vp_tmp = sm_rpool_malloc_x(rpool, *resultsz); + *vp_tmp = '\0'; + + if (*result != NULL) + sm_strlcpy(vp_tmp, + *result, + *resultsz); + *result = vp_tmp; + } + + p = *result + *resultln; + pe = *result + *resultsz; + + for (i = 0; vals[i] != NULL; i++) + { + if (*resultln > 0 && + p < pe) + *p++ = (char) delim; + + if (lmap->ldap_attrsep != '\0') + { + p += sm_strlcpy(p, attr, + pe - p); + if (p < pe) + *p++ = lmap->ldap_attrsep; + } + + p += sm_strlcpy(p, vals[i], + pe - p); + *resultln = p - (*result); + if (p >= pe) + { + /* Internal error: buffer too small for LDAP values */ + SM_LDAP_ERROR_CLEANUP(); + errno = ENOMEM; + return EX_OSERR; + } + } + } + + ldap_value_free(vals); + ldap_memfree(attr); + } + save_errno = sm_ldap_geterrno(lmap->ldap_ld); + + /* + ** We check save_errno != LDAP_DECODING_ERROR since + ** OpenLDAP 1.X has a very ugly *undocumented* + ** hack of returning this error code from + ** ldap_next_attribute() if the library freed the + ** ber attribute. See: + ** http://www.openldap.org/lists/openldap-devel/9901/msg00064.html + */ + + if (save_errno != LDAP_SUCCESS && + save_errno != LDAP_DECODING_ERROR) + { + /* Must be an error */ + save_errno += E_LDAPBASE; + SM_LDAP_ERROR_CLEANUP(); + errno = save_errno; + return EX_TEMPFAIL; + } + + /* mark this DN as done */ + rl->lr_done = true; + if (rl->lr_ludp != NULL) + { + ldap_free_urldesc(rl->lr_ludp); + rl->lr_ludp = NULL; + } + if (rl->lr_attrs != NULL) + { + free(rl->lr_attrs); + rl->lr_attrs = NULL; + } + + /* We don't want multiple values and we have one */ + if ((char) delim == '\0' && + !bitset(SM_LDAP_SINGLEMATCH, flags) && + *result != NULL) + break; + } + save_errno = sm_ldap_geterrno(lmap->ldap_ld); + if (save_errno != LDAP_SUCCESS && + save_errno != LDAP_DECODING_ERROR) + { + /* Must be an error */ + save_errno += E_LDAPBASE; + SM_LDAP_ERROR_CLEANUP(); + errno = save_errno; + return EX_TEMPFAIL; + } + ldap_msgfree(lmap->ldap_res); + lmap->ldap_res = NULL; + } + + if (ret == 0) + save_errno = ETIMEDOUT; + else + save_errno = sm_ldap_geterrno(lmap->ldap_ld); + if (save_errno != LDAP_SUCCESS) + { + statp = EX_TEMPFAIL; + if (ret != 0) + { + switch (save_errno) + { +#ifdef LDAP_SERVER_DOWN + case LDAP_SERVER_DOWN: +#endif /* LDAP_SERVER_DOWN */ + case LDAP_TIMEOUT: + case LDAP_UNAVAILABLE: + + /* + ** server disappeared, + ** try reopen on next search + */ + + statp = EX_RESTART; + break; + } + save_errno += E_LDAPBASE; + } + SM_LDAP_ERROR_CLEANUP(); + errno = save_errno; + return statp; + } + + if (lmap->ldap_res != NULL) + { + ldap_msgfree(lmap->ldap_res); + lmap->ldap_res = NULL; + } + + if (toplevel) + { + int rlidx; + + /* + ** Spin through the built-up recurse list at the top + ** of the recursion. Since new items are added at the + ** end of the shared list, we actually only ever get + ** one level of recursion before things pop back to the + ** top. Any items added to the list during that recursion + ** will be expanded by the top level. + */ + + for (rlidx = 0; recurse != NULL && rlidx < recurse->lr_cnt; rlidx++) + { + int newflags; + int sid; + int status; + + rl = recurse->lr_data[rlidx]; + + newflags = flags; + if (rl->lr_done) + { + /* already expanded */ + continue; + } + + if (rl->lr_type == SM_LDAP_ATTR_DN) + { + /* do DN search */ + sid = ldap_search(lmap->ldap_ld, + rl->lr_search, + lmap->ldap_scope, + "(objectClass=*)", + (lmap->ldap_attr[0] == NULL ? + NULL : lmap->ldap_attr), + lmap->ldap_attrsonly); + } + else if (rl->lr_type == SM_LDAP_ATTR_FILTER) + { + /* do new search */ + sid = ldap_search(lmap->ldap_ld, + lmap->ldap_base, + lmap->ldap_scope, + rl->lr_search, + (lmap->ldap_attr[0] == NULL ? + NULL : lmap->ldap_attr), + lmap->ldap_attrsonly); + } + else if (rl->lr_type == SM_LDAP_ATTR_URL) + { + /* Parse URL */ + sid = ldap_url_parse(rl->lr_search, + &rl->lr_ludp); + + if (sid != 0) + { + errno = sid + E_LDAPURLBASE; + return EX_TEMPFAIL; + } + + /* We need to add objectClass */ + if (rl->lr_ludp->lud_attrs != NULL) + { + int attrnum = 0; + + while (rl->lr_ludp->lud_attrs[attrnum] != NULL) + { + if (strcasecmp(rl->lr_ludp->lud_attrs[attrnum], + "objectClass") == 0) + { + /* already requested */ + attrnum = -1; + break; + } + attrnum++; + } + + if (attrnum >= 0) + { + int i; + + rl->lr_attrs = (char **)malloc(sizeof(char *) * (attrnum + 2)); + if (rl->lr_attrs == NULL) + { + save_errno = errno; + ldap_free_urldesc(rl->lr_ludp); + errno = save_errno; + return EX_TEMPFAIL; + } + for (i = 0 ; i < attrnum; i++) + { + rl->lr_attrs[i] = rl->lr_ludp->lud_attrs[i]; + } + rl->lr_attrs[i++] = "objectClass"; + rl->lr_attrs[i++] = NULL; + } + } + + /* + ** Use the existing connection + ** for this search. It really + ** should use lud_scheme://lud_host:lud_port/ + ** instead but that would require + ** opening a new connection. + ** This should be fixed ASAP. + */ + + sid = ldap_search(lmap->ldap_ld, + rl->lr_ludp->lud_dn, + rl->lr_ludp->lud_scope, + rl->lr_ludp->lud_filter, + rl->lr_attrs, + lmap->ldap_attrsonly); + + /* Use the attributes specified by URL */ + newflags |= SM_LDAP_USE_ALLATTR; + } + else + { + /* unknown or illegal attribute type */ + errno = EFAULT; + return EX_SOFTWARE; + } + + /* Collect results */ + if (sid == -1) + { + save_errno = sm_ldap_geterrno(lmap->ldap_ld); + statp = EX_TEMPFAIL; + switch (save_errno) + { +#ifdef LDAP_SERVER_DOWN + case LDAP_SERVER_DOWN: +#endif /* LDAP_SERVER_DOWN */ + case LDAP_TIMEOUT: + case LDAP_UNAVAILABLE: + + /* + ** server disappeared, + ** try reopen on next search + */ + + statp = EX_RESTART; + break; + } + errno = save_errno + E_LDAPBASE; + return statp; + } + + status = sm_ldap_results(lmap, sid, newflags, delim, + rpool, result, resultln, + resultsz, recurse); + save_errno = errno; + if (status != EX_OK && status != EX_NOTFOUND) + { + errno = save_errno; + return status; + } + + /* Mark as done */ + rl->lr_done = true; + if (rl->lr_ludp != NULL) + { + ldap_free_urldesc(rl->lr_ludp); + rl->lr_ludp = NULL; + } + if (rl->lr_attrs != NULL) + { + free(rl->lr_attrs); + rl->lr_attrs = NULL; + } + + /* Reset rlidx as new items may have been added */ + rlidx = -1; + } + } + return statp; +} + +/* +** SM_LDAP_CLOSE -- close LDAP connection +** +** Parameters: +** lmap -- LDAP map information +** +** Returns: +** None. +** +*/ + +void +sm_ldap_close(lmap) + SM_LDAP_STRUCT *lmap; +{ + if (lmap->ldap_ld == NULL) + return; + + if (lmap->ldap_pid == getpid()) + ldap_unbind(lmap->ldap_ld); + lmap->ldap_ld = NULL; + lmap->ldap_pid = 0; +} + +/* +** SM_LDAP_SETOPTS -- set LDAP options +** +** Parameters: +** ld -- LDAP session handle +** lmap -- LDAP map information +** +** Returns: +** None. +** +*/ + +void +sm_ldap_setopts(ld, lmap) + LDAP *ld; + SM_LDAP_STRUCT *lmap; +{ +# if USE_LDAP_SET_OPTION + if (lmap->ldap_version != 0) + { + ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, + &lmap->ldap_version); + } + ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->ldap_deref); + if (bitset(LDAP_OPT_REFERRALS, lmap->ldap_options)) + ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON); + else + ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); + ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->ldap_sizelimit); + ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->ldap_timelimit); +# ifdef LDAP_OPT_RESTART + ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON); +# endif /* LDAP_OPT_RESTART */ +# else /* USE_LDAP_SET_OPTION */ + /* From here on in we can use ldap internal timelimits */ + ld->ld_deref = lmap->ldap_deref; + ld->ld_options = lmap->ldap_options; + ld->ld_sizelimit = lmap->ldap_sizelimit; + ld->ld_timelimit = lmap->ldap_timelimit; +# endif /* USE_LDAP_SET_OPTION */ +} + +/* +** SM_LDAP_GETERRNO -- get ldap errno value +** +** Parameters: +** ld -- LDAP session handle +** +** Returns: +** LDAP errno. +** +*/ + +int +sm_ldap_geterrno(ld) + LDAP *ld; +{ + int err = LDAP_SUCCESS; + +# if defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 + (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err); +# else /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */ +# ifdef LDAP_OPT_SIZELIMIT + err = ldap_get_lderrno(ld, NULL, NULL); +# else /* LDAP_OPT_SIZELIMIT */ + err = ld->ld_errno; + + /* + ** Reset value to prevent lingering LDAP_DECODING_ERROR due to + ** OpenLDAP 1.X's hack (see above) + */ + + ld->ld_errno = LDAP_SUCCESS; +# endif /* LDAP_OPT_SIZELIMIT */ +# endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */ + return err; +} +# endif /* LDAPMAP */ diff --git a/usr/src/cmd/sendmail/libsm/local.h b/usr/src/cmd/sendmail/libsm/local.h new file mode 100644 index 0000000000..a52c786329 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/local.h @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2000-2002, 2004 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + * $Id: local.h,v 1.51.2.2 2004/01/09 18:32:44 ca Exp $ + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* +** Information local to this implementation of stdio, +** in particular, macros and private variables. +*/ + +#include <sys/time.h> +#if !SM_CONF_MEMCHR +# include <memory.h> +#endif /* !SM_CONF_MEMCHR */ +#include <sm/heap.h> + +int sm_flush __P((SM_FILE_T *, int *)); +SM_FILE_T *smfp __P((void)); +int sm_refill __P((SM_FILE_T *, int)); +void sm_init __P((void)); +void sm_cleanup __P((void)); +void sm_makebuf __P((SM_FILE_T *)); +int sm_whatbuf __P((SM_FILE_T *, size_t *, int *)); +int sm_fwalk __P((int (*)(SM_FILE_T *, int *), int *)); +int sm_wsetup __P((SM_FILE_T *)); +int sm_flags __P((int)); +SM_FILE_T *sm_fp __P((const SM_FILE_T *, const int, SM_FILE_T *)); +int sm_vprintf __P((int, char const *, va_list)); + +/* std io functions */ +ssize_t sm_stdread __P((SM_FILE_T *, char *, size_t)); +ssize_t sm_stdwrite __P((SM_FILE_T *, char const *, size_t)); +off_t sm_stdseek __P((SM_FILE_T *, off_t, int)); +int sm_stdclose __P((SM_FILE_T *)); +int sm_stdopen __P((SM_FILE_T *, const void *, int, const void *)); +int sm_stdfdopen __P((SM_FILE_T *, const void *, int, const void *)); +int sm_stdsetinfo __P((SM_FILE_T *, int , void *)); +int sm_stdgetinfo __P((SM_FILE_T *, int , void *)); + +/* stdio io functions */ +ssize_t sm_stdioread __P((SM_FILE_T *, char *, size_t)); +ssize_t sm_stdiowrite __P((SM_FILE_T *, char const *, size_t)); +off_t sm_stdioseek __P((SM_FILE_T *, off_t, int)); +int sm_stdioclose __P((SM_FILE_T *)); +int sm_stdioopen __P((SM_FILE_T *, const void *, int, const void *)); +int sm_stdiosetinfo __P((SM_FILE_T *, int , void *)); +int sm_stdiogetinfo __P((SM_FILE_T *, int , void *)); + +/* string io functions */ +ssize_t sm_strread __P((SM_FILE_T *, char *, size_t)); +ssize_t sm_strwrite __P((SM_FILE_T *, char const *, size_t)); +off_t sm_strseek __P((SM_FILE_T *, off_t, int)); +int sm_strclose __P((SM_FILE_T *)); +int sm_stropen __P((SM_FILE_T *, const void *, int, const void *)); +int sm_strsetinfo __P((SM_FILE_T *, int , void *)); +int sm_strgetinfo __P((SM_FILE_T *, int , void *)); + +/* syslog io functions */ +ssize_t sm_syslogread __P((SM_FILE_T *, char *, size_t)); +ssize_t sm_syslogwrite __P((SM_FILE_T *, char const *, size_t)); +off_t sm_syslogseek __P((SM_FILE_T *, off_t, int)); +int sm_syslogclose __P((SM_FILE_T *)); +int sm_syslogopen __P((SM_FILE_T *, const void *, int, const void *)); +int sm_syslogsetinfo __P((SM_FILE_T *, int , void *)); +int sm_sysloggetinfo __P((SM_FILE_T *, int , void *)); + +/* should be defined in sys/time.h */ +#ifndef timersub +# define timersub(tvp, uvp, vvp) \ + do \ + { \ + (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ + (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ + if ((vvp)->tv_usec < 0) \ + { \ + (vvp)->tv_sec--; \ + (vvp)->tv_usec += 1000000; \ + } \ + } while (0) +#endif /* !timersub */ + +#ifndef timeradd +# define timeradd(tvp, uvp, vvp) \ + do \ + { \ + (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ + (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \ + if ((vvp)->tv_usec >= 1000000) \ + { \ + (vvp)->tv_sec++; \ + (vvp)->tv_usec -= 1000000; \ + } \ + } while (0) +#endif /* !timeradd */ + +#ifndef timercmp +# define timercmp(tvp, uvp, cmp) \ + (((tvp)->tv_sec == (uvp)->tv_sec) ? \ + ((tvp)->tv_usec cmp (uvp)->tv_usec) : \ + ((tvp)->tv_sec cmp (uvp)->tv_sec)) +#endif /* !timercmp */ + +extern bool Sm_IO_DidInit; + +/* Return true iff the given SM_FILE_T cannot be written now. */ +#define cantwrite(fp) \ + ((((fp)->f_flags & SMWR) == 0 || (fp)->f_bf.smb_base == NULL) && \ + sm_wsetup(fp)) + +/* +** Test whether the given stdio file has an active ungetc buffer; +** release such a buffer, without restoring ordinary unread data. +*/ + +#define HASUB(fp) ((fp)->f_ub.smb_base != NULL) +#define FREEUB(fp) \ +{ \ + if ((fp)->f_ub.smb_base != (fp)->f_ubuf) \ + sm_free((char *)(fp)->f_ub.smb_base); \ + (fp)->f_ub.smb_base = NULL; \ +} + +extern const char SmFileMagic[]; + +#define SM_ALIGN(p) (((unsigned long)(p) + SM_ALIGN_BITS) & ~SM_ALIGN_BITS) + +#define sm_io_flockfile(fp) ((void) 0) +#define sm_io_funlockfile(fp) ((void) 0) + +#ifndef FDSET_CAST +# define FDSET_CAST /* empty cast for fd_set arg to select */ +#endif + +/* +** SM_CONVERT_TIME -- convert the API timeout flag for select() usage. +** +** This takes a 'fp' (a file type pointer) and obtains the "raw" +** file descriptor (fd) if possible. The 'fd' is needed to possibly +** switch the mode of the file (blocking/non-blocking) to match +** the type of timeout. If timeout is SM_TIME_FOREVER then the +** timeout using select won't be needed and the file is best placed +** in blocking mode. If there is to be a finite timeout then the file +** is best placed in non-blocking mode. Then, if not enough can be +** written, select() can be used to test when something can be written +** yet still timeout if the wait is too long. +** If the mode is already in the correct state we don't change it. +** Iff (yes "iff") the 'fd' is "-1" in value then the mode change +** will not happen. This situation arises when a late-binding-to-disk +** file type is in use. An example of this is the sendmail buffered +** file type (in sendmail/bf.c). +** +** Parameters +** fp -- the file pointer the timeout is for +** fd -- to become the file descriptor value from 'fp' +** val -- the timeout value to be converted +** time -- a struct timeval holding the converted value +** +** Returns +** nothing, this is flow-through code +** +** Side Effects: +** May or may not change the mode of a currently open file. +** The file mode may be changed to O_NONBLOCK or ~O_NONBLOCK +** (meaning block). This is done to best match the type of +** timeout and for (possible) use with select(). +*/ + +# define SM_CONVERT_TIME(fp, fd, val, time) { \ + if (((fd) = sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL)) == -1) \ + { \ + /* can't get an fd, likely internal 'fake' fp */ \ + errno = 0; \ + } \ + if ((val) == SM_TIME_DEFAULT) \ + (val) = (fp)->f_timeout; \ + if ((val) == SM_TIME_IMMEDIATE || (val) == SM_TIME_FOREVER) \ + { \ + (time)->tv_sec = 0; \ + (time)->tv_usec = 0; \ + } \ + else \ + { \ + (time)->tv_sec = (val) / 1000; \ + (time)->tv_usec = ((val) - ((time)->tv_sec * 1000)) * 10; \ + } \ + if ((val) == SM_TIME_FOREVER) \ + { \ + if ((fp)->f_timeoutstate == SM_TIME_NONBLOCK && (fd) != -1) \ + { \ + int ret; \ + ret = fcntl((fd), F_GETFL, 0); \ + if (ret == -1 || fcntl((fd), F_SETFL, \ + ret & ~O_NONBLOCK) == -1) \ + { \ + /* errno should be set */ \ + return SM_IO_EOF; \ + } \ + (fp)->f_timeoutstate = SM_TIME_BLOCK; \ + if ((fp)->f_modefp != NULL) \ + (fp)->f_modefp->f_timeoutstate = SM_TIME_BLOCK; \ + } \ + } \ + else { \ + if ((fp)->f_timeoutstate == SM_TIME_BLOCK && (fd) != -1) \ + { \ + int ret; \ + ret = fcntl((fd), F_GETFL, 0); \ + if (ret == -1 || fcntl((fd), F_SETFL, \ + ret | O_NONBLOCK) == -1) \ + { \ + /* errno should be set */ \ + return SM_IO_EOF; \ + } \ + (fp)->f_timeoutstate = SM_TIME_NONBLOCK; \ + if ((fp)->f_modefp != NULL) \ + (fp)->f_modefp->f_timeoutstate = SM_TIME_NONBLOCK; \ + } \ + } \ +} + +/* +** SM_IO_WR_TIMEOUT -- setup the timeout for the write +** +** This #define uses a select() to wait for the 'fd' to become writable. +** The select() can be active for up to 'to' time. The select may not +** use all of the the 'to' time. Hence, the amount of "wall-clock" time is +** measured to decide how much to subtract from 'to' to update it. On some +** BSD-based/like systems the timeout for a select is updated for the +** amount of time used. On many/most systems this does not happen. Therefore +** the updating of 'to' must be done ourselves; a copy of 'to' is passed +** since a BSD-like system will have updated it and we don't want to +** double the time used! +** Note: if a valid 'fd' doesn't exist yet, don't use this (e.g. the +** sendmail buffered file type in sendmail/bf.c; see fvwrite.c). +** +** Parameters +** fd -- a file descriptor for doing select() with +** timeout -- the original user set value. +** +** Returns +** nothing, this is flow through code +** +** Side Effects: +** adjusts 'timeout' for time used +*/ + +#define SM_IO_WR_TIMEOUT(fp, fd, to) { \ + struct timeval sm_io_to_before, sm_io_to_after, sm_io_to_diff; \ + struct timeval sm_io_to; \ + int sm_io_to_sel; \ + fd_set sm_io_to_mask, sm_io_x_mask; \ + errno = 0; \ + if ((to) == SM_TIME_DEFAULT) \ + (to) = (fp)->f_timeout; \ + if ((to) == SM_TIME_IMMEDIATE) \ + { \ + errno = EAGAIN; \ + return SM_IO_EOF; \ + } \ + else if ((to) == SM_TIME_FOREVER) \ + { \ + errno = EINVAL; \ + return SM_IO_EOF; \ + } \ + else \ + { \ + sm_io_to.tv_sec = (to) / 1000; \ + sm_io_to.tv_usec = ((to) - (sm_io_to.tv_sec * 1000)) * 10; \ + } \ + if (FD_SETSIZE > 0 && (fd) >= FD_SETSIZE) \ + { \ + errno = EINVAL; \ + return SM_IO_EOF; \ + } \ + FD_ZERO(&sm_io_to_mask); \ + FD_SET((fd), &sm_io_to_mask); \ + FD_ZERO(&sm_io_x_mask); \ + FD_SET((fd), &sm_io_x_mask); \ + if (gettimeofday(&sm_io_to_before, NULL) < 0) \ + return SM_IO_EOF; \ + sm_io_to_sel = select((fd) + 1, NULL, &sm_io_to_mask, &sm_io_x_mask, \ + &sm_io_to); \ + if (sm_io_to_sel < 0) \ + { \ + /* something went wrong, errno set */ \ + return SM_IO_EOF; \ + } \ + else if (sm_io_to_sel == 0) \ + { \ + /* timeout */ \ + errno = EAGAIN; \ + return SM_IO_EOF; \ + } \ + /* else loop again */ \ + if (gettimeofday(&sm_io_to_after, NULL) < 0) \ + return SM_IO_EOF; \ + timersub(&sm_io_to_before, &sm_io_to_after, &sm_io_to_diff); \ + timersub(&sm_io_to, &sm_io_to_diff, &sm_io_to); \ + (to) -= (sm_io_to.tv_sec * 1000); \ + (to) -= (sm_io_to.tv_usec / 10); \ + if ((to) < 0) \ + (to) = 0; \ +} + +/* +** If there is no 'fd' just error (we can't timeout). If the timeout +** is SM_TIME_FOREVER then there is no need to do a timeout with +** select since this will be a real error. If the error is not +** EAGAIN/EWOULDBLOCK (from a nonblocking) then it's a real error. +** Specify the condition here as macro so it can be used in several places. +*/ + +#define IS_IO_ERROR(fd, ret, to) \ + ((fd) < 0 || \ + ((ret) < 0 && errno != EAGAIN && errno != EWOULDBLOCK) || \ + (to) == SM_TIME_FOREVER) + diff --git a/usr/src/cmd/sendmail/libsm/makebuf.c b/usr/src/cmd/sendmail/libsm/makebuf.c new file mode 100644 index 0000000000..09ca853cc8 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/makebuf.c @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: makebuf.c,v 1.26 2001/10/31 16:04:08 ca Exp $") +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sm/io.h> +#include <sm/heap.h> +#include <sm/conf.h> +#include "local.h" + +/* +** SM_MAKEBUF -- make a buffer for the file +** +** Parameters: +** fp -- the file to be buffered +** +** Returns: +** nothing +** +** Allocate a file buffer, or switch to unbuffered I/O. +** By default tty devices default to line buffered. +*/ + +void +sm_makebuf(fp) + register SM_FILE_T *fp; +{ + register void *p; + register int flags; + size_t size; + int couldbetty; + + if (fp->f_flags & SMNBF) + { + fp->f_bf.smb_base = fp->f_p = fp->f_nbuf; + fp->f_bf.smb_size = 1; + return; + } + flags = sm_whatbuf(fp, &size, &couldbetty); + if ((p = sm_malloc(size)) == NULL) + { + fp->f_flags |= SMNBF; + fp->f_bf.smb_base = fp->f_p = fp->f_nbuf; + fp->f_bf.smb_size = 1; + return; + } + if (!Sm_IO_DidInit) + sm_init(); + flags |= SMMBF; + fp->f_bf.smb_base = fp->f_p = p; + fp->f_bf.smb_size = size; + if (couldbetty && isatty(fp->f_file)) + flags |= SMLBF; + fp->f_flags |= flags; +} + +/* +** SM_WHATBUF -- determine proper buffer for a file (internal) +** +** Plus it fills in 'bufsize' for recommended buffer size and +** fills in flag to indicate if 'fp' could be a tty (nothing +** to do with "betty" :-) ). +** +** Parameters: +** fp -- file pointer to be buffered +** bufsize -- new buffer size (a return) +** couldbetty -- could be a tty (returns) +** +** Returns: +** Success: +** on error: +** SMNPT -- not seek opimized +** SMOPT -- seek opimized +*/ + +int +sm_whatbuf(fp, bufsize, couldbetty) + register SM_FILE_T *fp; + size_t *bufsize; + int *couldbetty; +{ + struct stat st; + + if (fp->f_file < 0 || fstat(fp->f_file, &st) < 0) + { + *couldbetty = 0; + *bufsize = SM_IO_BUFSIZ; + return SMNPT; + } + + /* could be a tty iff it is a character device */ + *couldbetty = S_ISCHR(st.st_mode); + if (st.st_blksize == 0) + { + *bufsize = SM_IO_BUFSIZ; + return SMNPT; + } + +#if SM_IO_MAX_BUF_FILE > 0 + if (S_ISREG(st.st_mode) && st.st_blksize > SM_IO_MAX_BUF_FILE) + st.st_blksize = SM_IO_MAX_BUF_FILE; +#endif /* SM_IO_MAX_BUF_FILE > 0 */ + +#if SM_IO_MAX_BUF > 0 || SM_IO_MIN_BUF > 0 + if (!S_ISREG(st.st_mode)) + { +# if SM_IO_MAX_BUF > 0 + if (st.st_blksize > SM_IO_MAX_BUF) + st.st_blksize = SM_IO_MAX_BUF; +# if SM_IO_MIN_BUF > 0 + else +# endif /* SM_IO_MIN_BUF > 0 */ +# endif /* SM_IO_MAX_BUF > 0 */ +# if SM_IO_MIN_BUF > 0 + if (st.st_blksize < SM_IO_MIN_BUF) + st.st_blksize = SM_IO_MIN_BUF; +# endif /* SM_IO_MIN_BUF > 0 */ + } +#endif /* SM_IO_MAX_BUF > 0 || SM_IO_MIN_BUF > 0 */ + + /* + ** Optimise fseek() only if it is a regular file. (The test for + ** sm_std_seek is mainly paranoia.) It is safe to set _blksize + ** unconditionally; it will only be used if SMOPT is also set. + */ + + if ((fp->f_flags & SMSTR) == 0) + { + *bufsize = st.st_blksize; + fp->f_blksize = st.st_blksize; + } + else + *bufsize = SM_IO_BUFSIZ; + if ((st.st_mode & S_IFMT) == S_IFREG && + fp->f_seek == sm_stdseek) + return SMOPT; + else + return SMNPT; +} diff --git a/usr/src/cmd/sendmail/libsm/match.c b/usr/src/cmd/sendmail/libsm/match.c new file mode 100644 index 0000000000..944f30bc64 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/match.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: match.c,v 1.8 2001/03/02 19:57:08 ca Exp $") + +#include <sm/string.h> + +/* +** SM_MATCH -- Match a character string against a glob pattern. +** +** Parameters: +** str -- string. +** par -- pattern to find in str. +** +** Returns: +** true on match, false on non-match. +** +** A pattern consists of normal characters, which match themselves, +** and meta-sequences. A * matches any sequence of characters. +** A ? matches any single character. A [ introduces a character class. +** A ] marks the end of a character class; if the ] is missing then +** the [ matches itself rather than introducing a character class. +** A character class matches any of the characters between the brackets. +** The range of characters from X to Y inclusive is written X-Y. +** If the first character after the [ is ! then the character class is +** complemented. +** +** To include a ] in a character class, make it the first character +** listed (after the !, if any). To include a -, make it the first +** character listed (after the !, if any) or the last character. +** It is impossible for a ] to be the final character in a range. +** For glob patterns that literally match "*", "?" or "[", +** use [*], [?] or [[]. +*/ + +bool +sm_match(str, pat) + const char *str; + const char *pat; +{ + bool ccnot, ccmatch, ccfirst; + const char *ccstart; + char c, c2; + + for (;;) + { + switch (*pat) + { + case '\0': + return *str == '\0'; + case '?': + if (*str == '\0') + return false; + ++pat; + ++str; + continue; + case '*': + ++pat; + if (*pat == '\0') + { + /* optimize case of trailing '*' */ + return true; + } + for (;;) + { + if (sm_match(pat, str)) + return true; + if (*str == '\0') + return false; + ++str; + } + /* NOTREACHED */ + case '[': + ccstart = pat++; + ccnot = false; + if (*pat == '!') + { + ccnot = true; + ++pat; + } + ccmatch = false; + ccfirst = true; + for (;;) + { + if (*pat == '\0') + { + pat = ccstart; + goto defl; + } + if (*pat == ']' && !ccfirst) + break; + c = *pat++; + ccfirst = false; + if (*pat == '-' && pat[1] != ']') + { + ++pat; + if (*pat == '\0') + { + pat = ccstart; + goto defl; + } + c2 = *pat++; + if (*str >= c && *str <= c2) + ccmatch = true; + } + else + { + if (*str == c) + ccmatch = true; + } + } + if (ccmatch ^ ccnot) + { + ++pat; + ++str; + } + else + return false; + continue; + default: + defl: + if (*pat != *str) + return false; + ++pat; + ++str; + continue; + } + } +} diff --git a/usr/src/cmd/sendmail/libsm/mbdb.c b/usr/src/cmd/sendmail/libsm/mbdb.c new file mode 100644 index 0000000000..d1a66d5894 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/mbdb.c @@ -0,0 +1,776 @@ +/* + * Copyright (c) 2001-2002 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: mbdb.c,v 1.40 2003/12/10 03:19:07 gshapiro Exp $") + +#include <sys/param.h> + +#include <ctype.h> +#include <errno.h> +#include <pwd.h> +#include <stdlib.h> +#include <setjmp.h> +#include <unistd.h> + +#include <sm/limits.h> +#include <sm/conf.h> +#include <sm/assert.h> +#include <sm/bitops.h> +#include <sm/errstring.h> +#include <sm/heap.h> +#include <sm/mbdb.h> +#include <sm/string.h> +# ifdef EX_OK +# undef EX_OK /* for SVr4.2 SMP */ +# endif /* EX_OK */ +#include <sm/sysexits.h> + +#if LDAPMAP +# if _LDAP_EXAMPLE_ +# include <sm/ldap.h> +# endif /* _LDAP_EXAMPLE_ */ +#endif /* LDAPMAP */ + +typedef struct +{ + char *mbdb_typename; + int (*mbdb_initialize) __P((char *)); + int (*mbdb_lookup) __P((char *name, SM_MBDB_T *user)); + void (*mbdb_terminate) __P((void)); +} SM_MBDB_TYPE_T; + +static int mbdb_pw_initialize __P((char *)); +static int mbdb_pw_lookup __P((char *name, SM_MBDB_T *user)); +static void mbdb_pw_terminate __P((void)); + +#if LDAPMAP +# if _LDAP_EXAMPLE_ +static struct sm_ldap_struct LDAPLMAP; +static int mbdb_ldap_initialize __P((char *)); +static int mbdb_ldap_lookup __P((char *name, SM_MBDB_T *user)); +static void mbdb_ldap_terminate __P((void)); +# endif /* _LDAP_EXAMPLE_ */ +#endif /* LDAPMAP */ + +static SM_MBDB_TYPE_T SmMbdbTypes[] = +{ + { "pw", mbdb_pw_initialize, mbdb_pw_lookup, mbdb_pw_terminate }, +#if LDAPMAP +# if _LDAP_EXAMPLE_ + { "ldap", mbdb_ldap_initialize, mbdb_ldap_lookup, mbdb_ldap_terminate }, +# endif /* _LDAP_EXAMPLE_ */ +#endif /* LDAPMAP */ + { NULL, NULL, NULL, NULL } +}; + +static SM_MBDB_TYPE_T *SmMbdbType = &SmMbdbTypes[0]; + +/* +** SM_MBDB_INITIALIZE -- specify which mailbox database to use +** +** If this function is not called, then the "pw" implementation +** is used by default; this implementation uses getpwnam(). +** +** Parameters: +** mbdb -- Which mailbox database to use. +** The argument has the form "name" or "name.arg". +** "pw" means use getpwnam(). +** +** Results: +** EX_OK on success, or an EX_* code on failure. +*/ + +int +sm_mbdb_initialize(mbdb) + char *mbdb; +{ + size_t namelen; + int err; + char *name; + char *arg; + SM_MBDB_TYPE_T *t; + + SM_REQUIRE(mbdb != NULL); + + name = mbdb; + arg = strchr(mbdb, '.'); + if (arg == NULL) + namelen = strlen(name); + else + { + namelen = arg - name; + ++arg; + } + + for (t = SmMbdbTypes; t->mbdb_typename != NULL; ++t) + { + if (strlen(t->mbdb_typename) == namelen && + strncmp(name, t->mbdb_typename, namelen) == 0) + { + err = EX_OK; + if (t->mbdb_initialize != NULL) + err = t->mbdb_initialize(arg); + if (err == EX_OK) + SmMbdbType = t; + return err; + } + } + return EX_UNAVAILABLE; +} + +/* +** SM_MBDB_TERMINATE -- terminate connection to the mailbox database +** +** Because this function closes any cached file descriptors that +** are being held open for the connection to the mailbox database, +** it should be called for security reasons prior to dropping privileges +** and execing another process. +** +** Parameters: +** none. +** +** Results: +** none. +*/ + +void +sm_mbdb_terminate() +{ + if (SmMbdbType->mbdb_terminate != NULL) + SmMbdbType->mbdb_terminate(); +} + +/* +** SM_MBDB_LOOKUP -- look up a local mail recipient, given name +** +** Parameters: +** name -- name of local mail recipient +** user -- pointer to structure to fill in on success +** +** Results: +** On success, fill in *user and return EX_OK. +** If the user does not exist, return EX_NOUSER. +** If a temporary failure (eg, a network failure) occurred, +** return EX_TEMPFAIL. Otherwise return EX_OSERR. +*/ + +int +sm_mbdb_lookup(name, user) + char *name; + SM_MBDB_T *user; +{ + int ret = EX_NOUSER; + + if (SmMbdbType->mbdb_lookup != NULL) + ret = SmMbdbType->mbdb_lookup(name, user); + return ret; +} + +/* +** SM_MBDB_FROMPW -- copy from struct pw to SM_MBDB_T +** +** Parameters: +** user -- destination user information structure +** pw -- source passwd structure +** +** Results: +** none. +*/ + +void +sm_mbdb_frompw(user, pw) + SM_MBDB_T *user; + struct passwd *pw; +{ + SM_REQUIRE(user != NULL); + (void) sm_strlcpy(user->mbdb_name, pw->pw_name, + sizeof(user->mbdb_name)); + user->mbdb_uid = pw->pw_uid; + user->mbdb_gid = pw->pw_gid; + sm_pwfullname(pw->pw_gecos, pw->pw_name, user->mbdb_fullname, + sizeof(user->mbdb_fullname)); + (void) sm_strlcpy(user->mbdb_homedir, pw->pw_dir, + sizeof(user->mbdb_homedir)); + (void) sm_strlcpy(user->mbdb_shell, pw->pw_shell, + sizeof(user->mbdb_shell)); +} + +/* +** SM_PWFULLNAME -- build full name of user from pw_gecos field. +** +** This routine interprets the strange entry that would appear +** in the GECOS field of the password file. +** +** Parameters: +** gecos -- name to build. +** user -- the login name of this user (for &). +** buf -- place to put the result. +** buflen -- length of buf. +** +** Returns: +** none. +*/ + +#if _FFR_HANDLE_ISO8859_GECOS +static char Latin1ToASCII[128] = +{ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, + 99, 80, 36, 89, 124, 36, 34, 99, 97, 60, 45, 45, 114, 45, 111, 42, + 50, 51, 39, 117, 80, 46, 44, 49, 111, 62, 42, 42, 42, 63, 65, 65, + 65, 65, 65, 65, 65, 67, 69, 69, 69, 69, 73, 73, 73, 73, 68, 78, 79, + 79, 79, 79, 79, 88, 79, 85, 85, 85, 85, 89, 80, 66, 97, 97, 97, 97, + 97, 97, 97, 99, 101, 101, 101, 101, 105, 105, 105, 105, 100, 110, + 111, 111, 111, 111, 111, 47, 111, 117, 117, 117, 117, 121, 112, 121 +}; +#endif /* _FFR_HANDLE_ISO8859_GECOS */ + +void +sm_pwfullname(gecos, user, buf, buflen) + register char *gecos; + char *user; + char *buf; + size_t buflen; +{ + register char *p; + register char *bp = buf; + + if (*gecos == '*') + gecos++; + + /* copy gecos, interpolating & to be full name */ + for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) + { + if (bp >= &buf[buflen - 1]) + { + /* buffer overflow -- just use login name */ + (void) sm_strlcpy(buf, user, buflen); + return; + } + if (*p == '&') + { + /* interpolate full name */ + (void) sm_strlcpy(bp, user, buflen - (bp - buf)); + *bp = toupper(*bp); + bp += strlen(bp); + } + else + { +#if _FFR_HANDLE_ISO8859_GECOS + if ((unsigned char) *p >= 128) + *bp++ = Latin1ToASCII[(unsigned char) *p - 128]; + else +#endif /* _FFR_HANDLE_ISO8859_GECOS */ + *bp++ = *p; + } + } + *bp = '\0'; +} + +/* +** /etc/passwd implementation. +*/ + +/* +** MBDB_PW_INITIALIZE -- initialize getpwnam() version +** +** Parameters: +** arg -- unused. +** +** Results: +** EX_OK. +*/ + +/* ARGSUSED0 */ +static int +mbdb_pw_initialize(arg) + char *arg; +{ + return EX_OK; +} + +/* +** MBDB_PW_LOOKUP -- look up a local mail recipient, given name +** +** Parameters: +** name -- name of local mail recipient +** user -- pointer to structure to fill in on success +** +** Results: +** On success, fill in *user and return EX_OK. +** Failure: EX_NOUSER. +*/ + +static int +mbdb_pw_lookup(name, user) + char *name; + SM_MBDB_T *user; +{ + struct passwd *pw; + +#ifdef HESIOD + /* DEC Hesiod getpwnam accepts numeric strings -- short circuit it */ + { + char *p; + + for (p = name; *p != '\0'; p++) + if (!isascii(*p) || !isdigit(*p)) + break; + if (*p == '\0') + return EX_NOUSER; + } +#endif /* HESIOD */ + + errno = 0; + pw = getpwnam(name); + if (pw == NULL) + { +#if 0 + /* + ** getpwnam() isn't advertised as setting errno. + ** In fact, under FreeBSD, non-root getpwnam() on + ** non-existant users returns NULL with errno = EPERM. + ** This test won't work. + */ + switch (errno) + { + case 0: + return EX_NOUSER; + case EIO: + return EX_OSERR; + default: + return EX_TEMPFAIL; + } +#endif /* 0 */ + return EX_NOUSER; + } + + sm_mbdb_frompw(user, pw); + return EX_OK; +} + +/* +** MBDB_PW_TERMINATE -- terminate connection to the mailbox database +** +** Parameters: +** none. +** +** Results: +** none. +*/ + +static void +mbdb_pw_terminate() +{ + endpwent(); +} + +#if LDAPMAP +# if _LDAP_EXAMPLE_ +/* +** LDAP example implementation based on RFC 2307, "An Approach for Using +** LDAP as a Network Information Service": +** +** ( nisSchema.1.0 NAME 'uidNumber' +** DESC 'An integer uniquely identifying a user in an +** administrative domain' +** EQUALITY integerMatch SYNTAX 'INTEGER' SINGLE-VALUE ) +** +** ( nisSchema.1.1 NAME 'gidNumber' +** DESC 'An integer uniquely identifying a group in an +** administrative domain' +** EQUALITY integerMatch SYNTAX 'INTEGER' SINGLE-VALUE ) +** +** ( nisSchema.1.2 NAME 'gecos' +** DESC 'The GECOS field; the common name' +** EQUALITY caseIgnoreIA5Match +** SUBSTRINGS caseIgnoreIA5SubstringsMatch +** SYNTAX 'IA5String' SINGLE-VALUE ) +** +** ( nisSchema.1.3 NAME 'homeDirectory' +** DESC 'The absolute path to the home directory' +** EQUALITY caseExactIA5Match +** SYNTAX 'IA5String' SINGLE-VALUE ) +** +** ( nisSchema.1.4 NAME 'loginShell' +** DESC 'The path to the login shell' +** EQUALITY caseExactIA5Match +** SYNTAX 'IA5String' SINGLE-VALUE ) +** +** ( nisSchema.2.0 NAME 'posixAccount' SUP top AUXILIARY +** DESC 'Abstraction of an account with POSIX attributes' +** MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory ) +** MAY ( userPassword $ loginShell $ gecos $ description ) ) +** +*/ + +# define MBDB_LDAP_LABEL "MailboxDatabase" + +# ifndef MBDB_LDAP_FILTER +# define MBDB_LDAP_FILTER "(&(objectClass=posixAccount)(uid=%0))" +# endif /* MBDB_LDAP_FILTER */ + +# ifndef MBDB_DEFAULT_LDAP_BASEDN +# define MBDB_DEFAULT_LDAP_BASEDN NULL +# endif /* MBDB_DEFAULT_LDAP_BASEDN */ + +# ifndef MBDB_DEFAULT_LDAP_SERVER +# define MBDB_DEFAULT_LDAP_SERVER NULL +# endif /* MBDB_DEFAULT_LDAP_SERVER */ + +/* +** MBDB_LDAP_INITIALIZE -- initialize LDAP version +** +** Parameters: +** arg -- LDAP specification +** +** Results: +** EX_OK on success, or an EX_* code on failure. +*/ + +static int +mbdb_ldap_initialize(arg) + char *arg; +{ + sm_ldap_clear(&LDAPLMAP); + LDAPLMAP.ldap_base = MBDB_DEFAULT_LDAP_BASEDN; + LDAPLMAP.ldap_host = MBDB_DEFAULT_LDAP_SERVER; + LDAPLMAP.ldap_filter = MBDB_LDAP_FILTER; + + /* Only want one match */ + LDAPLMAP.ldap_sizelimit = 1; + + /* interpolate new ldap_base and ldap_host from arg if given */ + if (arg != NULL && *arg != '\0') + { + char *new; + char *sep; + size_t len; + + len = strlen(arg) + 1; + new = sm_malloc(len); + if (new == NULL) + return EX_TEMPFAIL; + (void) sm_strlcpy(new, arg, len); + sep = strrchr(new, '@'); + if (sep != NULL) + { + *sep++ = '\0'; + LDAPLMAP.ldap_host = sep; + } + LDAPLMAP.ldap_base = new; + } + return EX_OK; +} + + +/* +** MBDB_LDAP_LOOKUP -- look up a local mail recipient, given name +** +** Parameters: +** name -- name of local mail recipient +** user -- pointer to structure to fill in on success +** +** Results: +** On success, fill in *user and return EX_OK. +** Failure: EX_NOUSER. +*/ + +#define NEED_FULLNAME 0x01 +#define NEED_HOMEDIR 0x02 +#define NEED_SHELL 0x04 +#define NEED_UID 0x08 +#define NEED_GID 0x10 + +static int +mbdb_ldap_lookup(name, user) + char *name; + SM_MBDB_T *user; +{ + int msgid; + int need; + int ret; + int save_errno; + LDAPMessage *entry; + BerElement *ber; + char *attr = NULL; + + if (strlen(name) >= sizeof(user->mbdb_name)) + { + errno = EINVAL; + return EX_NOUSER; + } + + if (LDAPLMAP.ldap_filter == NULL) + { + /* map not initialized, but don't have arg here */ + errno = EFAULT; + return EX_TEMPFAIL; + } + + if (LDAPLMAP.ldap_pid != getpid()) + { + /* re-open map in this child process */ + LDAPLMAP.ldap_ld = NULL; + } + + if (LDAPLMAP.ldap_ld == NULL) + { + /* map not open, try to open now */ + if (!sm_ldap_start(MBDB_LDAP_LABEL, &LDAPLMAP)) + return EX_TEMPFAIL; + } + + sm_ldap_setopts(LDAPLMAP.ldap_ld, &LDAPLMAP); + msgid = sm_ldap_search(&LDAPLMAP, name); + if (msgid == -1) + { + save_errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld) + E_LDAPBASE; +# ifdef LDAP_SERVER_DOWN + if (errno == LDAP_SERVER_DOWN) + { + /* server disappeared, try reopen on next search */ + sm_ldap_close(&LDAPLMAP); + } +# endif /* LDAP_SERVER_DOWN */ + errno = save_errno; + return EX_TEMPFAIL; + } + + /* Get results */ + ret = ldap_result(LDAPLMAP.ldap_ld, msgid, 1, + (LDAPLMAP.ldap_timeout.tv_sec == 0 ? NULL : + &(LDAPLMAP.ldap_timeout)), + &(LDAPLMAP.ldap_res)); + + if (ret != LDAP_RES_SEARCH_RESULT && + ret != LDAP_RES_SEARCH_ENTRY) + { + if (ret == 0) + errno = ETIMEDOUT; + else + errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld); + ret = EX_TEMPFAIL; + goto abort; + } + + entry = ldap_first_entry(LDAPLMAP.ldap_ld, LDAPLMAP.ldap_res); + if (entry == NULL) + { + save_errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld); + if (save_errno == LDAP_SUCCESS) + { + errno = ENOENT; + ret = EX_NOUSER; + } + else + { + errno = save_errno; + ret = EX_TEMPFAIL; + } + goto abort; + } + +# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT) + /* + ** Reset value to prevent lingering + ** LDAP_DECODING_ERROR due to + ** OpenLDAP 1.X's hack (see below) + */ + + LDAPLMAP.ldap_ld->ld_errno = LDAP_SUCCESS; +# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */ + + ret = EX_OK; + need = NEED_FULLNAME|NEED_HOMEDIR|NEED_SHELL|NEED_UID|NEED_GID; + for (attr = ldap_first_attribute(LDAPLMAP.ldap_ld, entry, &ber); + attr != NULL; + attr = ldap_next_attribute(LDAPLMAP.ldap_ld, entry, ber)) + { + char **vals; + + vals = ldap_get_values(LDAPLMAP.ldap_ld, entry, attr); + if (vals == NULL) + { + errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld); + if (errno == LDAP_SUCCESS) + { + ldap_memfree(attr); + continue; + } + + /* Must be an error */ + errno += E_LDAPBASE; + ret = EX_TEMPFAIL; + goto abort; + } + +# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT) + /* + ** Reset value to prevent lingering + ** LDAP_DECODING_ERROR due to + ** OpenLDAP 1.X's hack (see below) + */ + + LDAPLMAP.ldap_ld->ld_errno = LDAP_SUCCESS; +# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */ + + if (vals[0] == NULL || vals[0][0] == '\0') + goto skip; + + if (strcasecmp(attr, "gecos") == 0) + { + if (!bitset(NEED_FULLNAME, need) || + strlen(vals[0]) >= sizeof(user->mbdb_fullname)) + goto skip; + + sm_pwfullname(vals[0], name, user->mbdb_fullname, + sizeof(user->mbdb_fullname)); + need &= ~NEED_FULLNAME; + } + else if (strcasecmp(attr, "homeDirectory") == 0) + { + if (!bitset(NEED_HOMEDIR, need) || + strlen(vals[0]) >= sizeof(user->mbdb_homedir)) + goto skip; + + (void) sm_strlcpy(user->mbdb_homedir, vals[0], + sizeof(user->mbdb_homedir)); + need &= ~NEED_HOMEDIR; + } + else if (strcasecmp(attr, "loginShell") == 0) + { + if (!bitset(NEED_SHELL, need) || + strlen(vals[0]) >= sizeof(user->mbdb_shell)) + goto skip; + + (void) sm_strlcpy(user->mbdb_shell, vals[0], + sizeof(user->mbdb_shell)); + need &= ~NEED_SHELL; + } + else if (strcasecmp(attr, "uidNumber") == 0) + { + char *p; + + if (!bitset(NEED_UID, need)) + goto skip; + + for (p = vals[0]; *p != '\0'; p++) + { + /* allow negative numbers */ + if (p == vals[0] && *p == '-') + { + /* but not simply '-' */ + if (*(p + 1) == '\0') + goto skip; + } + else if (!isascii(*p) || !isdigit(*p)) + goto skip; + } + user->mbdb_uid = atoi(vals[0]); + need &= ~NEED_UID; + } + else if (strcasecmp(attr, "gidNumber") == 0) + { + char *p; + + if (!bitset(NEED_GID, need)) + goto skip; + + for (p = vals[0]; *p != '\0'; p++) + { + /* allow negative numbers */ + if (p == vals[0] && *p == '-') + { + /* but not simply '-' */ + if (*(p + 1) == '\0') + goto skip; + } + else if (!isascii(*p) || !isdigit(*p)) + goto skip; + } + user->mbdb_gid = atoi(vals[0]); + need &= ~NEED_GID; + } + +skip: + ldap_value_free(vals); + ldap_memfree(attr); + } + + errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld); + + /* + ** We check errno != LDAP_DECODING_ERROR since + ** OpenLDAP 1.X has a very ugly *undocumented* + ** hack of returning this error code from + ** ldap_next_attribute() if the library freed the + ** ber attribute. See: + ** http://www.openldap.org/lists/openldap-devel/9901/msg00064.html + */ + + if (errno != LDAP_SUCCESS && + errno != LDAP_DECODING_ERROR) + { + /* Must be an error */ + errno += E_LDAPBASE; + ret = EX_TEMPFAIL; + goto abort; + } + + abort: + save_errno = errno; + if (attr != NULL) + { + ldap_memfree(attr); + attr = NULL; + } + if (LDAPLMAP.ldap_res != NULL) + { + ldap_msgfree(LDAPLMAP.ldap_res); + LDAPLMAP.ldap_res = NULL; + } + if (ret == EX_OK) + { + if (need == 0) + { + (void) sm_strlcpy(user->mbdb_name, name, + sizeof(user->mbdb_name)); + save_errno = 0; + } + else + { + ret = EX_NOUSER; + save_errno = EINVAL; + } + } + errno = save_errno; + return ret; +} + +/* +** MBDB_LDAP_TERMINATE -- terminate connection to the mailbox database +** +** Parameters: +** none. +** +** Results: +** none. +*/ + +static void +mbdb_ldap_terminate() +{ + sm_ldap_close(&LDAPLMAP); +} +# endif /* _LDAP_EXAMPLE_ */ +#endif /* LDAPMAP */ diff --git a/usr/src/cmd/sendmail/libsm/niprop.c b/usr/src/cmd/sendmail/libsm/niprop.c new file mode 100644 index 0000000000..ed1201d479 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/niprop.c @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: niprop.c,v 1.6 2001/09/04 22:41:27 ca Exp $") + +#if NETINFO +#include <ctype.h> +#include <stdlib.h> +#include <sm/io.h> +#include <sm/assert.h> +#include <sm/debug.h> +#include <sm/string.h> +#include <sm/varargs.h> +#include <sm/heap.h> + +/* +** NI_PROPVAL -- NetInfo property value lookup routine +** +** Parameters: +** keydir -- the NetInfo directory name in which to search +** for the key. +** keyprop -- the name of the property in which to find the +** property we are interested. Defaults to "name". +** keyval -- the value for which we are really searching. +** valprop -- the property name for the value in which we +** are interested. +** sepchar -- if non-nil, this can be multiple-valued, and +** we should return a string separated by this +** character. +** +** Returns: +** NULL -- if: +** 1. the directory is not found +** 2. the property name is not found +** 3. the property contains multiple values +** 4. some error occurred +** else -- the value of the lookup. +** +** Example: +** To search for an alias value, use: +** ni_propval("/aliases", "name", aliasname, "members", ',') +** +** Notes: +** Caller should free the return value of ni_proval +*/ + +# include <netinfo/ni.h> + +# define LOCAL_NETINFO_DOMAIN "." +# define PARENT_NETINFO_DOMAIN ".." +# define MAX_NI_LEVELS 256 + +char * +ni_propval(keydir, keyprop, keyval, valprop, sepchar) + char *keydir; + char *keyprop; + char *keyval; + char *valprop; + int sepchar; +{ + char *propval = NULL; + int i; + int j, alen, l; + void *ni = NULL; + void *lastni = NULL; + ni_status nis; + ni_id nid; + ni_namelist ninl; + register char *p; + char keybuf[1024]; + + /* + ** Create the full key from the two parts. + ** + ** Note that directory can end with, e.g., "name=" to specify + ** an alternate search property. + */ + + i = strlen(keydir) + strlen(keyval) + 2; + if (keyprop != NULL) + i += strlen(keyprop) + 1; + if (i >= sizeof keybuf) + return NULL; + (void) sm_strlcpyn(keybuf, sizeof keybuf, 2, keydir, "/"); + if (keyprop != NULL) + { + (void) sm_strlcat2(keybuf, keyprop, "=", sizeof keybuf); + } + (void) sm_strlcat(keybuf, keyval, sizeof keybuf); + +#if 0 + if (tTd(38, 21)) + sm_dprintf("ni_propval(%s, %s, %s, %s, %d) keybuf='%s'\n", + keydir, keyprop, keyval, valprop, sepchar, keybuf); +#endif /* 0 */ + + /* + ** If the passed directory and property name are found + ** in one of netinfo domains we need to search (starting + ** from the local domain moving all the way back to the + ** root domain) set propval to the property's value + ** and return it. + */ + + for (i = 0; i < MAX_NI_LEVELS && propval == NULL; i++) + { + if (i == 0) + { + nis = ni_open(NULL, LOCAL_NETINFO_DOMAIN, &ni); +#if 0 + if (tTd(38, 20)) + sm_dprintf("ni_open(LOCAL) = %d\n", nis); +#endif /* 0 */ + } + else + { + if (lastni != NULL) + ni_free(lastni); + lastni = ni; + nis = ni_open(lastni, PARENT_NETINFO_DOMAIN, &ni); +#if 0 + if (tTd(38, 20)) + sm_dprintf("ni_open(PARENT) = %d\n", nis); +#endif /* 0 */ + } + + /* + ** Don't bother if we didn't get a handle on a + ** proper domain. This is not necessarily an error. + ** We would get a positive ni_status if, for instance + ** we never found the directory or property and tried + ** to open the parent of the root domain! + */ + + if (nis != 0) + break; + + /* + ** Find the path to the server information. + */ + + if (ni_pathsearch(ni, &nid, keybuf) != 0) + continue; + + /* + ** Find associated value information. + */ + + if (ni_lookupprop(ni, &nid, valprop, &ninl) != 0) + continue; + +#if 0 + if (tTd(38, 20)) + sm_dprintf("ni_lookupprop: len=%d\n", + ninl.ni_namelist_len); +#endif /* 0 */ + + /* + ** See if we have an acceptable number of values. + */ + + if (ninl.ni_namelist_len <= 0) + continue; + + if (sepchar == '\0' && ninl.ni_namelist_len > 1) + { + ni_namelist_free(&ninl); + continue; + } + + /* + ** Calculate number of bytes needed and build result + */ + + alen = 1; + for (j = 0; j < ninl.ni_namelist_len; j++) + alen += strlen(ninl.ni_namelist_val[j]) + 1; + propval = p = sm_malloc(alen); + if (propval == NULL) + goto cleanup; + for (j = 0; j < ninl.ni_namelist_len; j++) + { + (void) sm_strlcpy(p, ninl.ni_namelist_val[j], alen); + l = strlen(p); + p += l; + *p++ = sepchar; + alen -= l + 1; + } + *--p = '\0'; + + ni_namelist_free(&ninl); + } + + cleanup: + if (ni != NULL) + ni_free(ni); + if (lastni != NULL && ni != lastni) + ni_free(lastni); +#if 0 + if (tTd(38, 20)) + sm_dprintf("ni_propval returns: '%s'\n", propval); +#endif /* 0 */ + + return propval; +} +#endif /* NETINFO */ diff --git a/usr/src/cmd/sendmail/libsm/path.c b/usr/src/cmd/sendmail/libsm/path.c new file mode 100644 index 0000000000..517a948dd5 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/path.c @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: path.c,v 1.7 2001/08/28 16:06:59 gshapiro Exp $") + +#include <sm/path.h> +#include <sm/string.h> + diff --git a/usr/src/cmd/sendmail/libsm/put.c b/usr/src/cmd/sendmail/libsm/put.c new file mode 100644 index 0000000000..7940d102b3 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/put.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: put.c,v 1.27 2001/12/19 05:19:35 ca Exp $") +#include <string.h> +#include <errno.h> +#include <sm/io.h> +#include <sm/assert.h> +#include <sm/errstring.h> +#include <sm/string.h> +#include "local.h" +#include "fvwrite.h" + +/* +** SM_IO_PUTC -- output a character to the file +** +** Function version of the macro sm_io_putc (in <sm/io.h>). +** +** Parameters: +** fp -- file to output to +** timeout -- time to complete putc +** c -- int value of character to output +** +** Returns: +** Failure: returns SM_IO_EOF _and_ sets errno +** Success: returns sm_putc() value. +** +*/ + +#undef sm_io_putc + +int +sm_io_putc(fp, timeout, c) + SM_FILE_T *fp; + int timeout; + int c; +{ + SM_REQUIRE_ISA(fp, SmFileMagic); + if (cantwrite(fp)) + { + errno = EBADF; + return SM_IO_EOF; + } + return sm_putc(fp, timeout, c); +} + + +/* +** SM_PERROR -- print system error messages to smioerr +** +** Parameters: +** s -- message to print +** +** Returns: +** none +*/ + +void +sm_perror(s) + const char *s; +{ + int save_errno = errno; + + if (s != NULL && *s != '\0') + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s: ", s); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s\n", + sm_errstring(save_errno)); +} diff --git a/usr/src/cmd/sendmail/libsm/refill.c b/usr/src/cmd/sendmail/libsm/refill.c new file mode 100644 index 0000000000..6e1eb5b2a6 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/refill.c @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: refill.c,v 1.49.2.1 2002/09/09 21:38:08 gshapiro Exp $") +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <setjmp.h> +#include <signal.h> +#include <sys/time.h> +#include <fcntl.h> +#include <string.h> +#include <sm/io.h> +#include <sm/conf.h> +#include <sm/assert.h> +#include "local.h" + +static int sm_lflush __P((SM_FILE_T *, int *)); + +/* +** SM_IO_RD_TIMEOUT -- measured timeout for reads +** +** This #define uses a select() to wait for the 'fd' to become readable. +** The select() can be active for up to 'To' time. The select() may not +** use all of the the 'To' time. Hence, the amount of "wall-clock" time is +** measured to decide how much to subtract from 'To' to update it. On some +** BSD-based/like systems the timeout for a select() is updated for the +** amount of time used. On many/most systems this does not happen. Therefore +** the updating of 'To' must be done ourselves; a copy of 'To' is passed +** since a BSD-like system will have updated it and we don't want to +** double the time used! +** Note: if a valid 'fd' doesn't exist yet, don't use this (e.g. the +** sendmail buffered file type in sendmail/bf.c; see use below). +** +** Parameters +** fp -- the file pointer for the active file +** fd -- raw file descriptor (from 'fp') to use for select() +** to -- struct timeval of the timeout +** timeout -- the original timeout value +** sel_ret -- the return value from the select() +** +** Returns: +** nothing, flow through code +*/ + +#define SM_IO_RD_TIMEOUT(fp, fd, to, timeout, sel_ret) \ +{ \ + struct timeval sm_io_to_before, sm_io_to_after, sm_io_to_diff; \ + fd_set sm_io_to_mask, sm_io_x_mask; \ + errno = 0; \ + if (timeout == SM_TIME_IMMEDIATE) \ + { \ + errno = EAGAIN; \ + return SM_IO_EOF; \ + } \ + if (FD_SETSIZE > 0 && (fd) >= FD_SETSIZE) \ + { \ + errno = EINVAL; \ + return SM_IO_EOF; \ + } \ + FD_ZERO(&sm_io_to_mask); \ + FD_SET((fd), &sm_io_to_mask); \ + FD_ZERO(&sm_io_x_mask); \ + FD_SET((fd), &sm_io_x_mask); \ + if (gettimeofday(&sm_io_to_before, NULL) < 0) \ + return SM_IO_EOF; \ + (sel_ret) = select((fd) + 1, &sm_io_to_mask, NULL, \ + &sm_io_x_mask, (to)); \ + if ((sel_ret) < 0) \ + { \ + /* something went wrong, errno set */ \ + fp->f_r = 0; \ + fp->f_flags |= SMERR; \ + return SM_IO_EOF; \ + } \ + else if ((sel_ret) == 0) \ + { \ + /* timeout */ \ + errno = EAGAIN; \ + return SM_IO_EOF; \ + } \ + /* calulate wall-clock time used */ \ + if (gettimeofday(&sm_io_to_after, NULL) < 0) \ + return SM_IO_EOF; \ + timersub(&sm_io_to_before, &sm_io_to_after, &sm_io_to_diff); \ + timersub((to), &sm_io_to_diff, (to)); \ +} + +/* +** SM_LFLUSH -- flush a file if it is line buffered and writable +** +** Parameters: +** fp -- file pointer to flush +** timeout -- original timeout value (in milliseconds) +** +** Returns: +** Failure: returns SM_IO_EOF and sets errno +** Success: returns 0 +*/ + +static int +sm_lflush(fp, timeout) + SM_FILE_T *fp; + int *timeout; +{ + + if ((fp->f_flags & (SMLBF|SMWR)) == (SMLBF|SMWR)) + return sm_flush(fp, timeout); + return 0; +} + +/* +** SM_REFILL -- refill a buffer +** +** Parameters: +** fp -- file pointer for buffer refill +** timeout -- time to complete filling the buffer in milliseconds +** +** Returns: +** Success: returns 0 +** Failure: returns SM_IO_EOF +*/ + +int +sm_refill(fp, timeout) + register SM_FILE_T *fp; + int timeout; +{ + int ret, r; + struct timeval to; + int fd; + + if (timeout == SM_TIME_DEFAULT) + timeout = fp->f_timeout; + if (timeout == SM_TIME_IMMEDIATE) + { + /* + ** Filling the buffer will take time and we are wanted to + ** return immediately. And we're not EOF or ERR really. + ** So... the failure is we couldn't do it in time. + */ + + errno = EAGAIN; + fp->f_r = 0; /* just to be sure */ + return 0; + } + + /* make sure stdio is set up */ + if (!Sm_IO_DidInit) + sm_init(); + + fp->f_r = 0; /* largely a convenience for callers */ + + if (fp->f_flags & SMFEOF) + return SM_IO_EOF; + + SM_CONVERT_TIME(fp, fd, timeout, &to); + + /* if not already reading, have to be reading and writing */ + if ((fp->f_flags & SMRD) == 0) + { + if ((fp->f_flags & SMRW) == 0) + { + errno = EBADF; + fp->f_flags |= SMERR; + return SM_IO_EOF; + } + + /* switch to reading */ + if (fp->f_flags & SMWR) + { + if (sm_flush(fp, &timeout)) + return SM_IO_EOF; + fp->f_flags &= ~SMWR; + fp->f_w = 0; + fp->f_lbfsize = 0; + } + fp->f_flags |= SMRD; + } + else + { + /* + ** We were reading. If there is an ungetc buffer, + ** we must have been reading from that. Drop it, + ** restoring the previous buffer (if any). If there + ** is anything in that buffer, return. + */ + + if (HASUB(fp)) + { + FREEUB(fp); + if ((fp->f_r = fp->f_ur) != 0) + { + fp->f_p = fp->f_up; + + /* revert blocking state */ + return 0; + } + } + } + + if (fp->f_bf.smb_base == NULL) + sm_makebuf(fp); + + /* + ** Before reading from a line buffered or unbuffered file, + ** flush all line buffered output files, per the ANSI C standard. + */ + + if (fp->f_flags & (SMLBF|SMNBF)) + (void) sm_fwalk(sm_lflush, &timeout); + + /* + ** If this file is linked to another, and we are going to hang + ** on the read, flush the linked file before continuing. + */ + + if (fp->f_flushfp != NULL && + (*fp->f_getinfo)(fp, SM_IO_IS_READABLE, NULL) <= 0) + sm_flush(fp->f_flushfp, &timeout); + + fp->f_p = fp->f_bf.smb_base; + + /* + ** The do-while loop stops trying to read when something is read + ** or it appears that the timeout has expired before finding + ** something available to be read (via select()). + */ + + ret = 0; + do + { + errno = 0; /* needed to ensure EOF correctly found */ + r = (*fp->f_read)(fp, (char *)fp->f_p, fp->f_bf.smb_size); + if (r <= 0) + { + if (r == 0 && errno == 0) + break; /* EOF found */ + if (IS_IO_ERROR(fd, r, timeout)) + goto err; /* errno set */ + + /* read would block */ + SM_IO_RD_TIMEOUT(fp, fd, &to, timeout, ret); + } + } while (r <= 0 && ret > 0); + +err: + if (r <= 0) + { + if (r == 0) + fp->f_flags |= SMFEOF; + else + fp->f_flags |= SMERR; + fp->f_r = 0; + return SM_IO_EOF; + } + fp->f_r = r; + return 0; +} + +/* +** SM_RGET -- refills buffer and returns first character +** +** Handle sm_getc() when the buffer ran out: +** Refill, then return the first character in the newly-filled buffer. +** +** Parameters: +** fp -- file pointer to work on +** timeout -- time to complete refill +** +** Returns: +** Success: first character in refilled buffer as an int +** Failure: SM_IO_EOF +*/ + +int +sm_rget(fp, timeout) + register SM_FILE_T *fp; + int timeout; +{ + if (sm_refill(fp, timeout) == 0) + { + fp->f_r--; + return *fp->f_p++; + } + return SM_IO_EOF; +} diff --git a/usr/src/cmd/sendmail/libsm/rewind.c b/usr/src/cmd/sendmail/libsm/rewind.c new file mode 100644 index 0000000000..dc290affcf --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/rewind.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: rewind.c,v 1.16 2001/04/03 01:46:40 ca Exp $") +#include <errno.h> +#include <sm/io.h> +#include <sm/assert.h> +#include "local.h" + +/* +** SM_IO_REWIND -- rewind the file +** +** Seeks the file to the begining and clears any outstanding errors. +** +** Parameters: +** fp -- the flie pointer for rewind +** timeout -- time to complete the rewind +** +** Returns: +** none. +*/ + +void +sm_io_rewind(fp, timeout) + register SM_FILE_T *fp; + int timeout; +{ + SM_REQUIRE_ISA(fp, SmFileMagic); + (void) sm_io_seek(fp, timeout, 0L, SM_IO_SEEK_SET); + (void) sm_io_clearerr(fp); + errno = 0; /* not required, but seems reasonable */ +} diff --git a/usr/src/cmd/sendmail/libsm/rpool.c b/usr/src/cmd/sendmail/libsm/rpool.c new file mode 100644 index 0000000000..2597052f27 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/rpool.c @@ -0,0 +1,526 @@ +/* + * Copyright (c) 2000-2004 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: rpool.c,v 1.28 2004/08/03 20:44:04 ca Exp $") + +/* +** resource pools +** For documentation, see rpool.html +*/ + +#include <sm/exc.h> +#include <sm/heap.h> +#include <sm/rpool.h> +#include <sm/varargs.h> +#include <sm/conf.h> +#if _FFR_PERF_RPOOL +# include <syslog.h> +#endif /* _FFR_PERF_RPOOL */ + +const char SmRpoolMagic[] = "sm_rpool"; + +typedef union +{ + SM_POOLLINK_T link; + char align[SM_ALIGN_SIZE]; +} SM_POOLHDR_T; + +static char *sm_rpool_allocblock_x __P((SM_RPOOL_T *, size_t)); +static char *sm_rpool_allocblock __P((SM_RPOOL_T *, size_t)); + +/* +** Tune this later +*/ + +#define POOLSIZE 4096 +#define BIG_OBJECT_RATIO 10 + +/* +** SM_RPOOL_ALLOCBLOCK_X -- allocate a new block for an rpool. +** +** Parameters: +** rpool -- rpool to which the block should be added. +** size -- size of block. +** +** Returns: +** Pointer to block. +** +** Exceptions: +** F:sm_heap -- out of memory +*/ + +static char * +sm_rpool_allocblock_x(rpool, size) + SM_RPOOL_T *rpool; + size_t size; +{ + SM_POOLLINK_T *p; + + p = sm_malloc_x(sizeof(SM_POOLHDR_T) + size); + p->sm_pnext = rpool->sm_pools; + rpool->sm_pools = p; + return (char*) p + sizeof(SM_POOLHDR_T); +} + +/* +** SM_RPOOL_ALLOCBLOCK -- allocate a new block for an rpool. +** +** Parameters: +** rpool -- rpool to which the block should be added. +** size -- size of block. +** +** Returns: +** Pointer to block, NULL on failure. +*/ + +static char * +sm_rpool_allocblock(rpool, size) + SM_RPOOL_T *rpool; + size_t size; +{ + SM_POOLLINK_T *p; + + p = sm_malloc(sizeof(SM_POOLHDR_T) + size); + if (p == NULL) + return NULL; + p->sm_pnext = rpool->sm_pools; + rpool->sm_pools = p; + return (char*) p + sizeof(SM_POOLHDR_T); +} + +/* +** SM_RPOOL_MALLOC_TAGGED_X -- allocate memory from rpool +** +** Parameters: +** rpool -- rpool from which memory should be allocated; +** can be NULL, use sm_malloc() then. +** size -- size of block. +** file -- filename. +** line -- line number in file. +** group -- heap group for debugging. +** +** Returns: +** Pointer to block. +** +** Exceptions: +** F:sm_heap -- out of memory +** +** Notice: XXX +** if size == 0 and the rpool is new (no memory +** allocated yet) NULL is returned! +** We could solve this by +** - wasting 1 byte (size < avail) +** - checking for rpool->sm_poolptr != NULL +** - not asking for 0 sized buffer +*/ + +void * +#if SM_HEAP_CHECK +sm_rpool_malloc_tagged_x(rpool, size, file, line, group) + SM_RPOOL_T *rpool; + size_t size; + char *file; + int line; + int group; +#else /* SM_HEAP_CHECK */ +sm_rpool_malloc_x(rpool, size) + SM_RPOOL_T *rpool; + size_t size; +#endif /* SM_HEAP_CHECK */ +{ + char *ptr; + + if (rpool == NULL) + return sm_malloc_tagged_x(size, file, line, group); + + /* Ensure that size is properly aligned. */ + if (size & SM_ALIGN_BITS) + size = (size & ~SM_ALIGN_BITS) + SM_ALIGN_SIZE; + + /* The common case. This is optimized for speed. */ + if (size <= rpool->sm_poolavail) + { + ptr = rpool->sm_poolptr; + rpool->sm_poolptr += size; + rpool->sm_poolavail -= size; + return ptr; + } + + /* + ** The slow case: we need to call malloc. + ** The SM_REQUIRE assertion is deferred until now, for speed. + ** That's okay: we set rpool->sm_poolavail to 0 when we free an rpool, + ** so the common case code won't be triggered on a dangling pointer. + */ + + SM_REQUIRE(rpool->sm_magic == SmRpoolMagic); + + /* + ** If size > sm_poolsize, then malloc a new block especially for + ** this request. Future requests will be allocated from the + ** current pool. + ** + ** What if the current pool is mostly unallocated, and the current + ** request is larger than the available space, but < sm_poolsize? + ** If we discard the current pool, and start allocating from a new + ** pool, then we will be wasting a lot of space. For this reason, + ** we malloc a block just for the current request if size > + ** sm_bigobjectsize, where sm_bigobjectsize <= sm_poolsize. + ** Thus, the most space that we will waste at the end of a pool + ** is sm_bigobjectsize - 1. + */ + + if (size > rpool->sm_bigobjectsize) + { +#if _FFR_PERF_RPOOL + ++rpool->sm_nbigblocks; +#endif /* _FFR_PERF_RPOOL */ + return sm_rpool_allocblock_x(rpool, size); + } + SM_ASSERT(rpool->sm_bigobjectsize <= rpool->sm_poolsize); + ptr = sm_rpool_allocblock_x(rpool, rpool->sm_poolsize); + rpool->sm_poolptr = ptr + size; + rpool->sm_poolavail = rpool->sm_poolsize - size; +#if _FFR_PERF_RPOOL + ++rpool->sm_npools; +#endif /* _FFR_PERF_RPOOL */ + return ptr; +} + +/* +** SM_RPOOL_MALLOC_TAGGED -- allocate memory from rpool +** +** Parameters: +** rpool -- rpool from which memory should be allocated; +** can be NULL, use sm_malloc() then. +** size -- size of block. +** file -- filename. +** line -- line number in file. +** group -- heap group for debugging. +** +** Returns: +** Pointer to block, NULL on failure. +** +** Notice: XXX +** if size == 0 and the rpool is new (no memory +** allocated yet) NULL is returned! +** We could solve this by +** - wasting 1 byte (size < avail) +** - checking for rpool->sm_poolptr != NULL +** - not asking for 0 sized buffer +*/ + +void * +#if SM_HEAP_CHECK +sm_rpool_malloc_tagged(rpool, size, file, line, group) + SM_RPOOL_T *rpool; + size_t size; + char *file; + int line; + int group; +#else /* SM_HEAP_CHECK */ +sm_rpool_malloc(rpool, size) + SM_RPOOL_T *rpool; + size_t size; +#endif /* SM_HEAP_CHECK */ +{ + char *ptr; + + if (rpool == NULL) + return sm_malloc_tagged(size, file, line, group); + + /* Ensure that size is properly aligned. */ + if (size & SM_ALIGN_BITS) + size = (size & ~SM_ALIGN_BITS) + SM_ALIGN_SIZE; + + /* The common case. This is optimized for speed. */ + if (size <= rpool->sm_poolavail) + { + ptr = rpool->sm_poolptr; + rpool->sm_poolptr += size; + rpool->sm_poolavail -= size; + return ptr; + } + + /* + ** The slow case: we need to call malloc. + ** The SM_REQUIRE assertion is deferred until now, for speed. + ** That's okay: we set rpool->sm_poolavail to 0 when we free an rpool, + ** so the common case code won't be triggered on a dangling pointer. + */ + + SM_REQUIRE(rpool->sm_magic == SmRpoolMagic); + + /* + ** If size > sm_poolsize, then malloc a new block especially for + ** this request. Future requests will be allocated from the + ** current pool. + ** + ** What if the current pool is mostly unallocated, and the current + ** request is larger than the available space, but < sm_poolsize? + ** If we discard the current pool, and start allocating from a new + ** pool, then we will be wasting a lot of space. For this reason, + ** we malloc a block just for the current request if size > + ** sm_bigobjectsize, where sm_bigobjectsize <= sm_poolsize. + ** Thus, the most space that we will waste at the end of a pool + ** is sm_bigobjectsize - 1. + */ + + if (size > rpool->sm_bigobjectsize) + { +#if _FFR_PERF_RPOOL + ++rpool->sm_nbigblocks; +#endif /* _FFR_PERF_RPOOL */ + return sm_rpool_allocblock(rpool, size); + } + SM_ASSERT(rpool->sm_bigobjectsize <= rpool->sm_poolsize); + ptr = sm_rpool_allocblock(rpool, rpool->sm_poolsize); + if (ptr == NULL) + return NULL; + rpool->sm_poolptr = ptr + size; + rpool->sm_poolavail = rpool->sm_poolsize - size; +#if _FFR_PERF_RPOOL + ++rpool->sm_npools; +#endif /* _FFR_PERF_RPOOL */ + return ptr; +} + +/* +** SM_RPOOL_NEW_X -- create a new rpool. +** +** Parameters: +** parent -- pointer to parent rpool, can be NULL. +** +** Returns: +** Pointer to new rpool. +*/ + +SM_RPOOL_T * +sm_rpool_new_x(parent) + SM_RPOOL_T *parent; +{ + SM_RPOOL_T *rpool; + + rpool = sm_malloc_x(sizeof(SM_RPOOL_T)); + if (parent == NULL) + rpool->sm_parentlink = NULL; + else + { + SM_TRY + rpool->sm_parentlink = sm_rpool_attach_x(parent, + (SM_RPOOL_RFREE_T) sm_rpool_free, + (void *) rpool); + SM_EXCEPT(exc, "*") + sm_free(rpool); + sm_exc_raise_x(exc); + SM_END_TRY + } + rpool->sm_magic = SmRpoolMagic; + + rpool->sm_poolsize = POOLSIZE - sizeof(SM_POOLHDR_T); + rpool->sm_bigobjectsize = rpool->sm_poolsize / BIG_OBJECT_RATIO; + rpool->sm_poolptr = NULL; + rpool->sm_poolavail = 0; + rpool->sm_pools = NULL; + + rpool->sm_rptr = NULL; + rpool->sm_ravail = 0; + rpool->sm_rlists = NULL; +#if _FFR_PERF_RPOOL + rpool->sm_nbigblocks = 0; + rpool->sm_npools = 0; +#endif /* _FFR_PERF_RPOOL */ + + return rpool; +} + +/* +** SM_RPOOL_SETSIZES -- set sizes for rpool. +** +** Parameters: +** poolsize -- size of a single rpool block. +** bigobjectsize -- if this size is exceeded, an individual +** block is allocated (must be less or equal poolsize). +** +** Returns: +** none. +*/ + +void +sm_rpool_setsizes(rpool, poolsize, bigobjectsize) + SM_RPOOL_T *rpool; + size_t poolsize; + size_t bigobjectsize; +{ + SM_REQUIRE(poolsize >= bigobjectsize); + if (poolsize == 0) + poolsize = POOLSIZE - sizeof(SM_POOLHDR_T); + if (bigobjectsize == 0) + bigobjectsize = poolsize / BIG_OBJECT_RATIO; + rpool->sm_poolsize = poolsize; + rpool->sm_bigobjectsize = bigobjectsize; +} + +/* +** SM_RPOOL_FREE -- free an rpool and release all of its resources. +** +** Parameters: +** rpool -- rpool to free. +** +** Returns: +** none. +*/ + +void +sm_rpool_free(rpool) + SM_RPOOL_T *rpool; +{ + SM_RLIST_T *rl, *rnext; + SM_RESOURCE_T *r, *rmax; + SM_POOLLINK_T *pp, *pnext; + + if (rpool == NULL) + return; + + /* + ** It's important to free the resources before the memory pools, + ** because the resource free functions might modify the contents + ** of the memory pools. + */ + + rl = rpool->sm_rlists; + if (rl != NULL) + { + rmax = rpool->sm_rptr; + for (;;) + { + for (r = rl->sm_rvec; r < rmax; ++r) + { + if (r->sm_rfree != NULL) + r->sm_rfree(r->sm_rcontext); + } + rnext = rl->sm_rnext; + sm_free(rl); + if (rnext == NULL) + break; + rl = rnext; + rmax = &rl->sm_rvec[SM_RLIST_MAX]; + } + } + + /* + ** Now free the memory pools. + */ + + for (pp = rpool->sm_pools; pp != NULL; pp = pnext) + { + pnext = pp->sm_pnext; + sm_free(pp); + } + + /* + ** Disconnect rpool from its parent. + */ + + if (rpool->sm_parentlink != NULL) + *rpool->sm_parentlink = NULL; + + /* + ** Setting these fields to zero means that any future to attempt + ** to use the rpool after it is freed will cause an assertion failure. + */ + + rpool->sm_magic = NULL; + rpool->sm_poolavail = 0; + rpool->sm_ravail = 0; + +#if _FFR_PERF_RPOOL + if (rpool->sm_nbigblocks > 0 || rpool->sm_npools > 1) + syslog(LOG_NOTICE, + "perf: rpool=%lx, sm_nbigblocks=%d, sm_npools=%d", + (long) rpool, rpool->sm_nbigblocks, rpool->sm_npools); + rpool->sm_nbigblocks = 0; + rpool->sm_npools = 0; +#endif /* _FFR_PERF_RPOOL */ + sm_free(rpool); +} + +/* +** SM_RPOOL_ATTACH_X -- attach a resource to an rpool. +** +** Parameters: +** rpool -- rpool to which resource should be attached. +** rfree -- function to call when rpool is freed. +** rcontext -- argument for function to call when rpool is freed. +** +** Returns: +** Pointer to allocated function. +** +** Exceptions: +** F:sm_heap -- out of memory +*/ + +SM_RPOOL_ATTACH_T +sm_rpool_attach_x(rpool, rfree, rcontext) + SM_RPOOL_T *rpool; + SM_RPOOL_RFREE_T rfree; + void *rcontext; +{ + SM_RLIST_T *rl; + SM_RPOOL_ATTACH_T a; + + SM_REQUIRE_ISA(rpool, SmRpoolMagic); + + if (rpool->sm_ravail == 0) + { + rl = sm_malloc_x(sizeof(SM_RLIST_T)); + rl->sm_rnext = rpool->sm_rlists; + rpool->sm_rlists = rl; + rpool->sm_rptr = rl->sm_rvec; + rpool->sm_ravail = SM_RLIST_MAX; + } + + a = &rpool->sm_rptr->sm_rfree; + rpool->sm_rptr->sm_rfree = rfree; + rpool->sm_rptr->sm_rcontext = rcontext; + ++rpool->sm_rptr; + --rpool->sm_ravail; + return a; +} + +#if DO_NOT_USE_STRCPY +/* +** SM_RPOOL_STRDUP_X -- Create a copy of a C string +** +** Parameters: +** rpool -- rpool to use. +** s -- the string to copy. +** +** Returns: +** pointer to newly allocated string. +*/ + +char * +sm_rpool_strdup_x(rpool, s) + SM_RPOOL_T *rpool; + const char *s; +{ + size_t l; + char *n; + + l = strlen(s); + SM_ASSERT(l + 1 > l); + n = sm_rpool_malloc_x(rpool, l + 1); + sm_strlcpy(n, s, l + 1); + return n; +} +#endif /* DO_NOT_USE_STRCPY */ diff --git a/usr/src/cmd/sendmail/libsm/sem.c b/usr/src/cmd/sendmail/libsm/sem.c new file mode 100644 index 0000000000..2d33427ead --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/sem.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2000-2001, 2005 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: sem.c,v 1.12 2005/03/25 21:27:02 ca Exp $") + +#if SM_CONF_SEM +# include <stdlib.h> +# include <unistd.h> +# include <sm/sem.h> +# include <sm/heap.h> + +/* +** SM_SEM_START -- initialize semaphores +** +** Parameters: +** key -- key for semaphores. +** nsem -- number of semaphores. +** semflg -- flag for semget(), if 0, use a default. +** owner -- create semaphores. +** +** Returns: +** id for semaphores. +** < 0 on failure. +*/ + +int +sm_sem_start(key, nsem, semflg, owner) + key_t key; + int nsem; + int semflg; + bool owner; +{ + int semid, i; + unsigned short *semvals; + + semvals = NULL; + if (semflg == 0) + semflg = (SEM_A|SEM_R)|((SEM_A|SEM_R) >> 3); + if (owner) + semflg |= IPC_CREAT|IPC_EXCL; + semid = semget(key, nsem, semflg); + if (semid < 0) + goto error; + + if (owner) + { + union semun semarg; + + semvals = (unsigned short *) sm_malloc(nsem * sizeof semvals); + if (semvals == NULL) + goto error; + semarg.array = semvals; + + /* initialize semaphore values to be available */ + for (i = 0; i < nsem; i++) + semvals[i] = 1; + if (semctl(semid, 0, SETALL, semarg) < 0) + goto error; + } + return semid; + +error: + if (semvals != NULL) + sm_free(semvals); + if (semid >= 0) + sm_sem_stop(semid); + return -1; +} + +/* +** SM_SEM_STOP -- stop using semaphores. +** +** Parameters: +** semid -- id for semaphores. +** +** Returns: +** 0 on success. +** < 0 on failure. +*/ + +int +sm_sem_stop(semid) + int semid; +{ + return semctl(semid, 0, IPC_RMID, NULL); +} + +/* +** SM_SEM_ACQ -- acquire semaphore. +** +** Parameters: +** semid -- id for semaphores. +** semnum -- number of semaphore. +** timeout -- how long to wait for operation to succeed. +** +** Returns: +** 0 on success. +** < 0 on failure. +*/ + +int +sm_sem_acq(semid, semnum, timeout) + int semid; + int semnum; + int timeout; +{ + int r; + struct sembuf semops[1]; + + semops[0].sem_num = semnum; + semops[0].sem_op = -1; + semops[0].sem_flg = SEM_UNDO | + (timeout != SM_TIME_FOREVER ? 0 : IPC_NOWAIT); + if (timeout == SM_TIME_IMMEDIATE || timeout == SM_TIME_FOREVER) + return semop(semid, semops, 1); + do + { + r = semop(semid, semops, 1); + if (r == 0) + return r; + sleep(1); + --timeout; + } while (timeout > 0); + return r; +} + +/* +** SM_SEM_REL -- release semaphore. +** +** Parameters: +** semid -- id for semaphores. +** semnum -- number of semaphore. +** timeout -- how long to wait for operation to succeed. +** +** Returns: +** 0 on success. +** < 0 on failure. +*/ + +int +sm_sem_rel(semid, semnum, timeout) + int semid; + int semnum; + int timeout; +{ + int r; + struct sembuf semops[1]; + +#if PARANOID + /* XXX should we check whether the value is already 0 ? */ + SM_REQUIRE(sm_get_sem(semid, semnum) > 0); +#endif /* PARANOID */ + + semops[0].sem_num = semnum; + semops[0].sem_op = 1; + semops[0].sem_flg = SEM_UNDO | + (timeout != SM_TIME_FOREVER ? 0 : IPC_NOWAIT); + if (timeout == SM_TIME_IMMEDIATE || timeout == SM_TIME_FOREVER) + return semop(semid, semops, 1); + do + { + r = semop(semid, semops, 1); + if (r == 0) + return r; + sleep(1); + --timeout; + } while (timeout > 0); + return r; +} + +/* +** SM_SEM_GET -- get semaphore value. +** +** Parameters: +** semid -- id for semaphores. +** semnum -- number of semaphore. +** +** Returns: +** value of semaphore on success. +** < 0 on failure. +*/ + +int +sm_sem_get(semid, semnum) + int semid; + int semnum; +{ + int semval; + + if ((semval = semctl(semid, semnum, GETVAL, NULL)) < 0) + return -1; + return semval; +} +#endif /* SM_CONF_SEM */ diff --git a/usr/src/cmd/sendmail/libsm/setvbuf.c b/usr/src/cmd/sendmail/libsm/setvbuf.c new file mode 100644 index 0000000000..ccc5da99e2 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/setvbuf.c @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: setvbuf.c,v 1.30 2001/02/28 20:25:18 rodney Exp $") +#include <stdlib.h> +#include <errno.h> +#include <fcntl.h> +#include <sm/io.h> +#include <sm/heap.h> +#include <sm/assert.h> +#include <sm/conf.h> +#include "local.h" + +/* +** SM_IO_SETVBUF -- set the buffering type for a file +** +** Set one of the different kinds of buffering, optionally including +** a buffer. +** If 'size' is == 0 then an "optimal" size will be selected. +** If 'buf' is == NULL then space will be allocated at 'size'. +** +** Parameters: +** fp -- the file that buffering is to be changed for +** timeout -- time allowed for completing the function +** buf -- buffer to use +** mode -- buffering method to use +** size -- size of 'buf' +** +** Returns: +** Failure: SM_IO_EOF +** Success: 0 (zero) +*/ + +int +sm_io_setvbuf(fp, timeout, buf, mode, size) + SM_FILE_T *fp; + int timeout; + char *buf; + int mode; + size_t size; +{ + int ret, flags; + size_t iosize; + int ttyflag; + int fd; + struct timeval to; + + SM_REQUIRE_ISA(fp, SmFileMagic); + + /* + ** Verify arguments. The `int' limit on `size' is due to this + ** particular implementation. Note, buf and size are ignored + ** when setting SM_IO_NBF. + */ + + if (mode != SM_IO_NBF) + if ((mode != SM_IO_FBF && mode != SM_IO_LBF && + mode != SM_IO_NOW) || (int) size < 0) + return SM_IO_EOF; + + /* + ** Write current buffer, if any. Discard unread input (including + ** ungetc data), cancel line buffering, and free old buffer if + ** malloc()ed. We also clear any eof condition, as if this were + ** a seek. + */ + + ret = 0; + SM_CONVERT_TIME(fp, fd, timeout, &to); + (void) sm_flush(fp, &timeout); + if (HASUB(fp)) + FREEUB(fp); + fp->f_r = fp->f_lbfsize = 0; + flags = fp->f_flags; + if (flags & SMMBF) + { + sm_free((void *) fp->f_bf.smb_base); + fp->f_bf.smb_base = NULL; + } + flags &= ~(SMLBF | SMNBF | SMMBF | SMOPT | SMNPT | SMFEOF | SMNOW | + SMFBF); + + /* If setting unbuffered mode, skip all the hard work. */ + if (mode == SM_IO_NBF) + goto nbf; + + /* + ** Find optimal I/O size for seek optimization. This also returns + ** a `tty flag' to suggest that we check isatty(fd), but we do not + ** care since our caller told us how to buffer. + */ + + flags |= sm_whatbuf(fp, &iosize, &ttyflag); + if (size == 0) + { + buf = NULL; /* force local allocation */ + size = iosize; + } + + /* Allocate buffer if needed. */ + if (buf == NULL) + { + if ((buf = sm_malloc(size)) == NULL) + { + /* + ** Unable to honor user's request. We will return + ** failure, but try again with file system size. + */ + + ret = SM_IO_EOF; + if (size != iosize) + { + size = iosize; + buf = sm_malloc(size); + } + } + if (buf == NULL) + { + /* No luck; switch to unbuffered I/O. */ +nbf: + fp->f_flags = flags | SMNBF; + fp->f_w = 0; + fp->f_bf.smb_base = fp->f_p = fp->f_nbuf; + fp->f_bf.smb_size = 1; + return ret; + } + flags |= SMMBF; + } + + /* + ** Kill any seek optimization if the buffer is not the + ** right size. + ** + ** SHOULD WE ALLOW MULTIPLES HERE (i.e., ok iff (size % iosize) == 0)? + */ + + if (size != iosize) + flags |= SMNPT; + + /* + ** Fix up the SM_FILE_T fields, and set sm_cleanup for output flush on + ** exit (since we are buffered in some way). + */ + + if (mode == SM_IO_LBF) + flags |= SMLBF; + else if (mode == SM_IO_NOW) + flags |= SMNOW; + else if (mode == SM_IO_FBF) + flags |= SMFBF; + fp->f_flags = flags; + fp->f_bf.smb_base = fp->f_p = (unsigned char *)buf; + fp->f_bf.smb_size = size; + /* fp->f_lbfsize is still 0 */ + if (flags & SMWR) + { + /* + ** Begin or continue writing: see sm_wsetup(). Note + ** that SMNBF is impossible (it was handled earlier). + */ + + if (flags & SMLBF) + { + fp->f_w = 0; + fp->f_lbfsize = -fp->f_bf.smb_size; + } + else + fp->f_w = size; + } + else + { + /* begin/continue reading, or stay in intermediate state */ + fp->f_w = 0; + } + + atexit(sm_cleanup); + return ret; +} diff --git a/usr/src/cmd/sendmail/libsm/shm.c b/usr/src/cmd/sendmail/libsm/shm.c new file mode 100644 index 0000000000..3da9c96dd1 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/shm.c @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2000-2001, 2003, 2005 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: shm.c,v 1.18 2005/02/09 01:54:51 ca Exp $") + +#if SM_CONF_SHM +# include <stdlib.h> +# include <unistd.h> +# include <errno.h> +# include <sm/shm.h> + + +/* +** SM_SHMSTART -- initialize shared memory segment. +** +** Parameters: +** key -- key for shared memory. +** size -- size of segment. +** shmflag -- initial flags. +** shmid -- pointer to return id. +** owner -- create segment. +** +** Returns: +** pointer to shared memory segment, +** NULL on failure. +** +** Side Effects: +** attaches shared memory segment. +*/ + +void * +sm_shmstart(key, size, shmflg, shmid, owner) + key_t key; + int size; + int shmflg; + int *shmid; + bool owner; +{ + int save_errno; + void *shm = SM_SHM_NULL; + + /* default: user/group accessible */ + if (shmflg == 0) + shmflg = SHM_R|SHM_W|(SHM_R>>3)|(SHM_W>>3); + if (owner) + shmflg |= IPC_CREAT|IPC_EXCL; + *shmid = shmget(key, size, shmflg); + if (*shmid < 0) + goto error; + + shm = shmat(*shmid, (void *) 0, 0); + if (shm == SM_SHM_NULL) + goto error; + + return shm; + + error: + save_errno = errno; + if (shm != SM_SHM_NULL || *shmid >= 0) + sm_shmstop(shm, *shmid, owner); + *shmid = SM_SHM_NO_ID; + errno = save_errno; + return (void *) 0; +} + + +/* +** SM_SHMSTOP -- stop using shared memory segment. +** +** Parameters: +** shm -- pointer to shared memory. +** shmid -- id. +** owner -- delete segment. +** +** Returns: +** 0 on success. +** < 0 on failure. +** +** Side Effects: +** detaches (and maybe removes) shared memory segment. +*/ + + +int +sm_shmstop(shm, shmid, owner) + void *shm; + int shmid; + bool owner; +{ + int r; + + if (shm != SM_SHM_NULL && (r = shmdt(shm)) < 0) + return r; + if (owner && shmid >= 0 && (r = shmctl(shmid, IPC_RMID, NULL)) < 0) + return r; + return 0; +} + + +/* +** SM_SHMSETOWNER -- set owner/group/mode of shared memory segment. +** +** Parameters: +** shmid -- id. +** uid -- uid to use +** gid -- gid to use +** mode -- mode to use +** +** Returns: +** 0 on success. +** < 0 on failure. +*/ + +int +sm_shmsetowner(shmid, uid, gid, mode) + int shmid; + uid_t uid; + gid_t gid; + mode_t mode; +{ + int r; + struct shmid_ds shmid_ds; + + memset(&shmid_ds, 0, sizeof(shmid_ds)); + if ((r = shmctl(shmid, IPC_STAT, &shmid_ds)) < 0) + return r; + shmid_ds.shm_perm.uid = uid; + shmid_ds.shm_perm.gid = gid; + shmid_ds.shm_perm.mode = mode; + if ((r = shmctl(shmid, IPC_SET, &shmid_ds)) < 0) + return r; + return 0; +} +#endif /* SM_CONF_SHM */ diff --git a/usr/src/cmd/sendmail/libsm/signal.c b/usr/src/cmd/sendmail/libsm/signal.c new file mode 100644 index 0000000000..c5807e7f1f --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/signal.c @@ -0,0 +1,342 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: signal.c,v 1.13 2001/08/14 16:05:47 ca Exp $") + +#if SM_CONF_SETITIMER +# include <sys/time.h> +#endif /* SM_CONF_SETITIMER */ +#include <errno.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> +#include <sm/clock.h> +#include <sm/signal.h> +#include <signal.h> +#include <sm/string.h> + +unsigned int volatile InCriticalSection; /* >0 if inside critical section */ +int volatile PendingSignal; /* pending signal to resend */ + +/* +** SM_SIGNAL -- set a signal handler +** +** This is essentially old BSD "signal(3)". +** +** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD +** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE +** DOING. +*/ + +sigfunc_t +sm_signal(sig, handler) + int sig; + sigfunc_t handler; +{ +# if defined(SA_RESTART) || (!defined(SYS5SIGNALS) && !defined(BSD4_3)) + struct sigaction n, o; +# endif /* defined(SA_RESTART) || (!defined(SYS5SIGNALS) && !defined(BSD4_3)) */ + + /* + ** First, try for modern signal calls + ** and restartable syscalls + */ + +# ifdef SA_RESTART + (void) memset(&n, '\0', sizeof n); +# if USE_SA_SIGACTION + n.sa_sigaction = (void(*)(int, siginfo_t *, void *)) handler; + n.sa_flags = SA_RESTART|SA_SIGINFO; +# else /* USE_SA_SIGACTION */ + n.sa_handler = handler; + n.sa_flags = SA_RESTART; +# endif /* USE_SA_SIGACTION */ + if (sigaction(sig, &n, &o) < 0) + return SIG_ERR; + return o.sa_handler; +# else /* SA_RESTART */ + + /* + ** Else check for SYS5SIGNALS or + ** BSD4_3 signals + */ + +# if defined(SYS5SIGNALS) || defined(BSD4_3) +# ifdef BSD4_3 + return signal(sig, handler); +# else /* BSD4_3 */ + return sigset(sig, handler); +# endif /* BSD4_3 */ +# else /* defined(SYS5SIGNALS) || defined(BSD4_3) */ + + /* + ** Finally, if nothing else is available, + ** go for a default + */ + + (void) memset(&n, '\0', sizeof n); + n.sa_handler = handler; + if (sigaction(sig, &n, &o) < 0) + return SIG_ERR; + return o.sa_handler; +# endif /* defined(SYS5SIGNALS) || defined(BSD4_3) */ +# endif /* SA_RESTART */ +} +/* +** SM_BLOCKSIGNAL -- hold a signal to prevent delivery +** +** Parameters: +** sig -- the signal to block. +** +** Returns: +** 1 signal was previously blocked +** 0 signal was not previously blocked +** -1 on failure. +*/ + +int +sm_blocksignal(sig) + int sig; +{ +# ifdef BSD4_3 +# ifndef sigmask +# define sigmask(s) (1 << ((s) - 1)) +# endif /* ! sigmask */ + return (sigblock(sigmask(sig)) & sigmask(sig)) != 0; +# else /* BSD4_3 */ +# ifdef ALTOS_SYSTEM_V + sigfunc_t handler; + + handler = sigset(sig, SIG_HOLD); + if (handler == SIG_ERR) + return -1; + else + return handler == SIG_HOLD; +# else /* ALTOS_SYSTEM_V */ + sigset_t sset, oset; + + (void) sigemptyset(&sset); + (void) sigaddset(&sset, sig); + if (sigprocmask(SIG_BLOCK, &sset, &oset) < 0) + return -1; + else + return sigismember(&oset, sig); +# endif /* ALTOS_SYSTEM_V */ +# endif /* BSD4_3 */ +} +/* +** SM_RELEASESIGNAL -- release a held signal +** +** Parameters: +** sig -- the signal to release. +** +** Returns: +** 1 signal was previously blocked +** 0 signal was not previously blocked +** -1 on failure. +*/ + +int +sm_releasesignal(sig) + int sig; +{ +# ifdef BSD4_3 + return (sigsetmask(sigblock(0) & ~sigmask(sig)) & sigmask(sig)) != 0; +# else /* BSD4_3 */ +# ifdef ALTOS_SYSTEM_V + sigfunc_t handler; + + handler = sigset(sig, SIG_HOLD); + if (sigrelse(sig) < 0) + return -1; + else + return handler == SIG_HOLD; +# else /* ALTOS_SYSTEM_V */ + sigset_t sset, oset; + + (void) sigemptyset(&sset); + (void) sigaddset(&sset, sig); + if (sigprocmask(SIG_UNBLOCK, &sset, &oset) < 0) + return -1; + else + return sigismember(&oset, sig); +# endif /* ALTOS_SYSTEM_V */ +# endif /* BSD4_3 */ +} +/* +** PEND_SIGNAL -- Add a signal to the pending signal list +** +** Parameters: +** sig -- signal to add +** +** Returns: +** none. +** +** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD +** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE +** DOING. +*/ + +void +pend_signal(sig) + int sig; +{ + int sigbit; + int save_errno = errno; +#if SM_CONF_SETITIMER + struct itimerval clr; +#endif /* SM_CONF_SETITIMER */ + + /* + ** Don't want to interrupt something critical, hence delay + ** the alarm for one second. Hopefully, by then we + ** will be out of the critical section. If not, then + ** we will just delay again. The events to be run will + ** still all be run, maybe just a little bit late. + */ + + switch (sig) + { + case SIGHUP: + sigbit = PEND_SIGHUP; + break; + + case SIGINT: + sigbit = PEND_SIGINT; + break; + + case SIGTERM: + sigbit = PEND_SIGTERM; + break; + + case SIGUSR1: + sigbit = PEND_SIGUSR1; + break; + + case SIGALRM: + /* don't have to pend these */ + sigbit = 0; + break; + + default: + /* If we get here, we are in trouble */ + abort(); + + /* NOTREACHED */ + /* shut up stupid compiler warning on HP-UX 11 */ + sigbit = 0; + break; + } + + if (sigbit != 0) + PendingSignal |= sigbit; + (void) sm_signal(SIGALRM, sm_tick); +#if SM_CONF_SETITIMER + clr.it_interval.tv_sec = 0; + clr.it_interval.tv_usec = 0; + clr.it_value.tv_sec = 1; + clr.it_value.tv_usec = 0; + (void) setitimer(ITIMER_REAL, &clr, NULL); +#else /* SM_CONF_SETITIMER */ + (void) alarm(1); +#endif /* SM_CONF_SETITIMER */ + errno = save_errno; +} +/* +** SM_ALLSIGNALS -- act on all signals +** +** Parameters: +** block -- whether to block or release all signals. +** +** Returns: +** none. +*/ + +void +sm_allsignals(block) + bool block; +{ +# ifdef BSD4_3 +# ifndef sigmask +# define sigmask(s) (1 << ((s) - 1)) +# endif /* ! sigmask */ + if (block) + { + int mask = 0; + + mask |= sigmask(SIGALRM); + mask |= sigmask(SIGCHLD); + mask |= sigmask(SIGHUP); + mask |= sigmask(SIGINT); + mask |= sigmask(SIGTERM); + mask |= sigmask(SIGUSR1); + + (void) sigblock(mask); + } + else + sigsetmask(0); +# else /* BSD4_3 */ +# ifdef ALTOS_SYSTEM_V + if (block) + { + (void) sigset(SIGALRM, SIG_HOLD); + (void) sigset(SIGCHLD, SIG_HOLD); + (void) sigset(SIGHUP, SIG_HOLD); + (void) sigset(SIGINT, SIG_HOLD); + (void) sigset(SIGTERM, SIG_HOLD); + (void) sigset(SIGUSR1, SIG_HOLD); + } + else + { + (void) sigset(SIGALRM, SIG_DFL); + (void) sigset(SIGCHLD, SIG_DFL); + (void) sigset(SIGHUP, SIG_DFL); + (void) sigset(SIGINT, SIG_DFL); + (void) sigset(SIGTERM, SIG_DFL); + (void) sigset(SIGUSR1, SIG_DFL); + } +# else /* ALTOS_SYSTEM_V */ + sigset_t sset; + + (void) sigemptyset(&sset); + (void) sigaddset(&sset, SIGALRM); + (void) sigaddset(&sset, SIGCHLD); + (void) sigaddset(&sset, SIGHUP); + (void) sigaddset(&sset, SIGINT); + (void) sigaddset(&sset, SIGTERM); + (void) sigaddset(&sset, SIGUSR1); + (void) sigprocmask(block ? SIG_BLOCK : SIG_UNBLOCK, &sset, NULL); +# endif /* ALTOS_SYSTEM_V */ +# endif /* BSD4_3 */ +} +/* +** SM_SIGNAL_NOOP -- A signal no-op function +** +** Parameters: +** sig -- signal received +** +** Returns: +** SIGFUNC_RETURN +*/ + +/* ARGSUSED */ +SIGFUNC_DECL +sm_signal_noop(sig) + int sig; +{ + int save_errno = errno; + + FIX_SYSV_SIGNAL(sig, sm_signal_noop); + errno = save_errno; + return SIGFUNC_RETURN; +} + diff --git a/usr/src/cmd/sendmail/libsm/sm_os.h b/usr/src/cmd/sendmail/libsm/sm_os.h new file mode 100644 index 0000000000..4c28daf335 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/sm_os.h @@ -0,0 +1,8 @@ +/* + * Copyright (c) 2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "../include/sm/os/sm_os_sunos.h" diff --git a/usr/src/cmd/sendmail/libsm/smstdio.c b/usr/src/cmd/sendmail/libsm/smstdio.c new file mode 100644 index 0000000000..b99c5efb68 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/smstdio.c @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2000-2002, 2004 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_IDSTR(id, "@(#)$Id: smstdio.c,v 1.34 2004/08/03 20:46:34 ca Exp $") +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/stat.h> +#include <sm/assert.h> +#include <sm/io.h> +#include <sm/string.h> +#include "local.h" + +static void setup __P((SM_FILE_T *)); + +/* +** Overall: +** This is a file type which implements a layer on top of the system +** stdio. fp->f_cookie is the FILE* of stdio. The cookie may be +** "bound late" because of the manner which Linux implements stdio. +** When binding late (when fp->f_cookie==NULL) then the value of +** fp->f_ival is used (0, 1 or 2) to map to stdio's stdin, stdout or +** stderr. +*/ + +/* +** SM_STDIOOPEN -- open a file to system stdio implementation +** +** Parameters: +** fp -- file pointer assign for this open +** info -- info about file to open +** flags -- indicating method of opening +** rpool -- ignored +** +** Returns: +** Failure: -1 +** Success: 0 (zero) +*/ + +/* ARGSUSED3 */ +int +sm_stdioopen(fp, info, flags, rpool) + SM_FILE_T *fp; + const void *info; + int flags; + const void *rpool; +{ + register FILE *s; + char *stdiomode; + + switch (flags) + { + case SM_IO_RDONLY: + stdiomode = "r"; + break; + case SM_IO_WRONLY: + stdiomode = "w"; + break; + case SM_IO_APPEND: + stdiomode = "a"; + break; + case SM_IO_APPENDRW: + stdiomode = "a+"; + break; +#if SM_IO_BINARY != 0 + case SM_IO_RDONLY_B: + stdiomode = "rb"; + break; + case SM_IO_WRONLY_B: + stdiomode = "wb"; + break; + case SM_IO_APPEND_B: + stdiomode = "ab"; + break; + case SM_IO_APPENDRW_B: + stdiomode = "a+b"; + break; + case SM_IO_RDWR_B: + stdiomode = "r+b"; + break; +#endif /* SM_IO_BINARY != 0 */ + case SM_IO_RDWR: + default: + stdiomode = "r+"; + break; + } + + if ((s = fopen((char *)info, stdiomode)) == NULL) + return -1; + fp->f_cookie = s; + return 0; +} + +/* +** SETUP -- assign file type cookie when not already assigned +** +** Parameters: +** fp - the file pointer to get the cookie assigned +** +** Return: +** none. +*/ + +static void +setup(fp) + SM_FILE_T *fp; +{ + if (fp->f_cookie == NULL) + { + switch (fp->f_ival) + { + case 0: + fp->f_cookie = stdin; + break; + case 1: + fp->f_cookie = stdout; + break; + case 2: + fp->f_cookie = stderr; + break; + default: + sm_abort("fp->f_ival=%d: out of range (0...2)", fp->f_ival); + break; + } + } +} + +/* +** SM_STDIOREAD -- read from the file +** +** Parameters: +** fp -- the file pointer +** buf -- location to place the read data +** n - number of bytes to read +** +** Returns: +** result from fread(). +*/ + +ssize_t +sm_stdioread(fp, buf, n) + SM_FILE_T *fp; + char *buf; + size_t n; +{ + register FILE *s; + + if (fp->f_cookie == NULL) + setup(fp); + s = fp->f_cookie; + return fread(buf, 1, n, s); +} + +/* +** SM_STDIOWRITE -- write to the file +** +** Parameters: +** fp -- the file pointer +** buf -- location of data to write +** n - number of bytes to write +** +** Returns: +** result from fwrite(). +*/ + +ssize_t +sm_stdiowrite(fp, buf, n) + SM_FILE_T *fp; + char const *buf; + size_t n; +{ + register FILE *s; + + if (fp->f_cookie == NULL) + setup(fp); + s = fp->f_cookie; + return fwrite(buf, 1, n, s); +} + +/* +** SM_STDIOSEEK -- set position within file +** +** Parameters: +** fp -- the file pointer +** offset -- new location based on 'whence' +** whence -- indicates "base" for 'offset' +** +** Returns: +** result from fseek(). +*/ + +off_t +sm_stdioseek(fp, offset, whence) + SM_FILE_T *fp; + off_t offset; + int whence; +{ + register FILE *s; + + if (fp->f_cookie == NULL) + setup(fp); + s = fp->f_cookie; + return fseek(s, offset, whence); +} + +/* +** SM_STDIOCLOSE -- close the file +** +** Parameters: +** fp -- close file pointer +** +** Return: +** status from fclose() +*/ + +int +sm_stdioclose(fp) + SM_FILE_T *fp; +{ + register FILE *s; + + if (fp->f_cookie == NULL) + setup(fp); + s = fp->f_cookie; + return fclose(s); +} + +/* +** SM_STDIOSETINFO -- set info for this open file +** +** Parameters: +** fp -- the file pointer +** what -- type of information setting +** valp -- memory location of info to set +** +** Return: +** Failure: -1 and sets errno +** Success: none (currently). +*/ + +/* ARGSUSED2 */ +int +sm_stdiosetinfo(fp, what, valp) + SM_FILE_T *fp; + int what; + void *valp; +{ + switch (what) + { + case SM_IO_WHAT_MODE: + default: + errno = EINVAL; + return -1; + } +} + +/* +** SM_STDIOGETINFO -- get info for this open file +** +** Parameters: +** fp -- the file pointer +** what -- type of information request +** valp -- memory location to place info +** +** Return: +** Failure: -1 and sets errno +** Success: none (currently). +*/ + +/* ARGSUSED2 */ +int +sm_stdiogetinfo(fp, what, valp) + SM_FILE_T *fp; + int what; + void *valp; +{ + switch (what) + { + case SM_IO_WHAT_SIZE: + { + int fd; + struct stat st; + + if (fp->f_cookie == NULL) + setup(fp); + fd = fileno((FILE *) fp->f_cookie); + if (fd < 0) + return -1; + if (fstat(fd, &st) == 0) + return st.st_size; + else + return -1; + } + + case SM_IO_WHAT_MODE: + default: + errno = EINVAL; + return -1; + } +} + +/* +** SM_IO_STDIOOPEN -- create an SM_FILE which interfaces to a stdio FILE +** +** Parameters: +** stream -- an open stdio stream, as returned by fopen() +** mode -- the mode argument to fopen() which describes stream +** +** Return: +** On success, return a pointer to an SM_FILE object which +** can be used for reading and writing 'stream'. +** Abort if mode is gibberish or stream is bad. +** Raise an exception if we can't allocate memory. +*/ + +SM_FILE_T * +sm_io_stdioopen(stream, mode) + FILE *stream; + char *mode; +{ + int fd; + bool r, w; + int ioflags; + SM_FILE_T *fp; + + fd = fileno(stream); + SM_REQUIRE(fd >= 0); + + r = w = false; + switch (mode[0]) + { + case 'r': + r = true; + break; + case 'w': + case 'a': + w = true; + break; + default: + sm_abort("sm_io_stdioopen: mode '%s' is bad", mode); + } + if (strchr(&mode[1], '+') != NULL) + r = w = true; + if (r && w) + ioflags = SMRW; + else if (r) + ioflags = SMRD; + else + ioflags = SMWR; + + fp = sm_fp(SmFtRealStdio, ioflags, NULL); + fp->f_file = fd; + fp->f_cookie = stream; + return fp; +} diff --git a/usr/src/cmd/sendmail/libsm/snprintf.c b/usr/src/cmd/sendmail/libsm/snprintf.c new file mode 100644 index 0000000000..c31f8cf227 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/snprintf.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: snprintf.c,v 1.21 2001/03/02 23:53:41 ca Exp $") +#include <limits.h> +#include <sm/varargs.h> +#include <sm/io.h> +#include "local.h" + +/* +** SM_SNPRINTF -- format a string to a memory location of restricted size +** +** Parameters: +** str -- memory location to place formatted string +** n -- size of buffer pointed to by str, capped to +** a maximum of INT_MAX +** fmt -- the formatting directives +** ... -- the data to satisfy the formatting +** +** Returns: +** Failure: -1 +** Success: number of bytes that would have been written +** to str, not including the trailing '\0', +** up to a maximum of INT_MAX, as if there was +** no buffer size limitation. If the result >= n +** then the output was truncated. +** +** Side Effects: +** If n > 0, then between 0 and n-1 bytes of formatted output +** are written into 'str', followed by a '\0'. +*/ + +int +#if SM_VA_STD +sm_snprintf(char *str, size_t n, char const *fmt, ...) +#else /* SM_VA_STD */ +sm_snprintf(str, n, fmt, va_alist) + char *str; + size_t n; + char *fmt; + va_dcl +#endif /* SM_VA_STD */ +{ + int ret; + SM_VA_LOCAL_DECL + SM_FILE_T fake; + + /* While snprintf(3) specifies size_t stdio uses an int internally */ + if (n > INT_MAX) + n = INT_MAX; + SM_VA_START(ap, fmt); + + /* XXX put this into a static? */ + fake.sm_magic = SmFileMagic; + fake.f_file = -1; + fake.f_flags = SMWR | SMSTR; + fake.f_cookie = &fake; + fake.f_bf.smb_base = fake.f_p = (unsigned char *)str; + fake.f_bf.smb_size = fake.f_w = n ? n - 1 : 0; + fake.f_timeout = SM_TIME_FOREVER; + fake.f_timeoutstate = SM_TIME_BLOCK; + fake.f_close = NULL; + fake.f_open = NULL; + fake.f_read = NULL; + fake.f_write = NULL; + fake.f_seek = NULL; + fake.f_setinfo = fake.f_getinfo = NULL; + fake.f_type = "sm_snprintf:fake"; + ret = sm_io_vfprintf(&fake, SM_TIME_FOREVER, fmt, ap); + if (n > 0) + *fake.f_p = '\0'; + SM_VA_END(ap); + return ret; +} diff --git a/usr/src/cmd/sendmail/libsm/sscanf.c b/usr/src/cmd/sendmail/libsm/sscanf.c new file mode 100644 index 0000000000..9be1e58869 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/sscanf.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: sscanf.c,v 1.25 2002/02/01 02:28:00 ca Exp $") +#include <string.h> +#include <sm/varargs.h> +#include <sm/io.h> +#include "local.h" + +/* +** SM_EOFREAD -- dummy read function for faked file below +** +** Parameters: +** fp -- file pointer +** buf -- location to place read data +** len -- number of bytes to read +** +** Returns: +** 0 (zero) always +*/ + +static ssize_t +sm_eofread __P(( + SM_FILE_T *fp, + char *buf, + size_t len)); + +/* ARGSUSED0 */ +static ssize_t +sm_eofread(fp, buf, len) + SM_FILE_T *fp; + char *buf; + size_t len; +{ + return 0; +} + +/* +** SM_IO_SSCANF -- scan a string to find data units +** +** Parameters: +** str -- strings containing data +** fmt -- format directive for finding data units +** ... -- memory locations to place format found data units +** +** Returns: +** Failure: SM_IO_EOF +** Success: number of data units found +** +** Side Effects: +** Attempts to strlen() 'str'; if not a '\0' terminated string +** then the call may SEGV/fail. +** Faking the string 'str' as a file. +*/ + +int +#if SM_VA_STD +sm_io_sscanf(const char *str, char const *fmt, ...) +#else /* SM_VA_STD */ +sm_io_sscanf(str, fmt, va_alist) + const char *str; + char *fmt; + va_dcl +#endif /* SM_VA_STD */ +{ + int ret; + SM_FILE_T fake; + SM_VA_LOCAL_DECL + + fake.sm_magic = SmFileMagic; + fake.f_flags = SMRD; + fake.f_bf.smb_base = fake.f_p = (unsigned char *) str; + fake.f_bf.smb_size = fake.f_r = strlen(str); + fake.f_file = -1; + fake.f_read = sm_eofread; + fake.f_write = NULL; + fake.f_close = NULL; + fake.f_open = NULL; + fake.f_seek = NULL; + fake.f_setinfo = fake.f_getinfo = NULL; + fake.f_type = "sm_io_sscanf:fake"; + fake.f_flushfp = NULL; + fake.f_ub.smb_base = NULL; + fake.f_timeout = SM_TIME_FOREVER; + fake.f_timeoutstate = SM_TIME_BLOCK; + SM_VA_START(ap, fmt); + ret = sm_vfscanf(&fake, SM_TIME_FOREVER, fmt, ap); + SM_VA_END(ap); + return ret; +} diff --git a/usr/src/cmd/sendmail/libsm/stdio.c b/usr/src/cmd/sendmail/libsm/stdio.c new file mode 100644 index 0000000000..84d538733f --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/stdio.c @@ -0,0 +1,521 @@ +/* + * Copyright (c) 2000-2004 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: stdio.c,v 1.69 2004/08/03 20:46:34 ca Exp $") +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> /* FreeBSD: FD_ZERO needs <string.h> */ +#include <sys/stat.h> +#include <sys/time.h> +#include <sm/heap.h> +#include <sm/assert.h> +#include <sm/varargs.h> +#include <sm/io.h> +#include <sm/setjmp.h> +#include <sm/conf.h> +#include <sm/fdset.h> +#include "local.h" + +static int sm_stdsetmode __P((SM_FILE_T *, const int *)); +static int sm_stdgetmode __P((SM_FILE_T *, int *)); + +/* +** Overall: +** Small standard I/O/seek/close functions. +** These maintain the `known seek offset' for seek optimization. +*/ + +/* +** SM_STDOPEN -- open a file with stdio behavior +** +** Not associated with the system's stdio in libc. +** +** Parameters: +** fp -- file pointer to be associated with the open +** info -- pathname of the file to be opened +** flags -- indicates type of access methods +** rpool -- ignored +** +** Returns: +** Failure: -1 and set errno +** Success: 0 or greater (fd of file from open(2)). +** +*/ + +/* ARGSUSED3 */ +int +sm_stdopen(fp, info, flags, rpool) + SM_FILE_T *fp; + const void *info; + int flags; + const void *rpool; +{ + char *path = (char *) info; + int oflags; + + switch (SM_IO_MODE(flags)) + { + case SM_IO_RDWR: + oflags = O_RDWR; + break; + case SM_IO_RDWRTR: + oflags = O_RDWR | O_CREAT | O_TRUNC; + break; + case SM_IO_RDONLY: + oflags = O_RDONLY; + break; + case SM_IO_WRONLY: + oflags = O_WRONLY | O_CREAT | O_TRUNC; + break; + case SM_IO_APPEND: + oflags = O_APPEND | O_WRONLY | O_CREAT; + break; + case SM_IO_APPENDRW: + oflags = O_APPEND | O_RDWR | O_CREAT; + break; + default: + errno = EINVAL; + return -1; + } +#ifdef O_BINARY + if (SM_IS_BINARY(flags)) + oflags |= O_BINARY; +#endif /* O_BINARY */ + fp->f_file = open(path, oflags, + S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); + if (fp->f_file < 0) + return -1; /* errno set by open() */ + + if (oflags & O_APPEND) + (void) (*fp->f_seek)((void *)fp, (off_t)0, SEEK_END); + + return fp->f_file; +} + +/* +** SM_STDREAD -- read from the file +** +** Parameters: +** fp -- file pointer to read from +** buf -- location to place read data +** n -- number of bytes to read +** +** Returns: +** Failure: -1 and sets errno +** Success: number of bytes read +** +** Side Effects: +** Updates internal offset into file. +*/ + +ssize_t +sm_stdread(fp, buf, n) + SM_FILE_T *fp; + char *buf; + size_t n; +{ + register int ret; + + ret = read(fp->f_file, buf, n); + + /* if the read succeeded, update the current offset */ + if (ret > 0) + fp->f_lseekoff += ret; + return ret; +} + +/* +** SM_STDWRITE -- write to the file +** +** Parameters: +** fp -- file pointer ro write to +** buf -- location of data to be written +** n - number of bytes to write +** +** Returns: +** Failure: -1 and sets errno +** Success: number of bytes written +*/ + +ssize_t +sm_stdwrite(fp, buf, n) + SM_FILE_T *fp; + char const *buf; + size_t n; +{ + return write(fp->f_file, buf, n); +} + +/* +** SM_STDSEEK -- set the file offset position +** +** Parmeters: +** fp -- file pointer to position +** offset -- how far to position from "base" (set by 'whence') +** whence -- indicates where the "base" of the 'offset' to start +** +** Results: +** Failure: -1 and sets errno +** Success: the current offset +** +** Side Effects: +** Updates the internal value of the offset. +*/ + +off_t +sm_stdseek(fp, offset, whence) + SM_FILE_T *fp; + off_t offset; + int whence; +{ + register off_t ret; + + ret = lseek(fp->f_file, (off_t) offset, whence); + if (ret != (off_t) -1) + fp->f_lseekoff = ret; + return ret; +} + +/* +** SM_STDCLOSE -- close the file +** +** Parameters: +** fp -- the file pointer to close +** +** Returns: +** Success: 0 (zero) +** Failure: -1 and sets errno +*/ + +int +sm_stdclose(fp) + SM_FILE_T *fp; +{ + return close(fp->f_file); +} + +/* +** SM_STDSETMODE -- set the access mode for the file +** +** Called by sm_stdsetinfo(). +** +** Parameters: +** fp -- file pointer +** mode -- new mode to set the file access to +** +** Results: +** Success: 0 (zero); +** Failure: -1 and sets errno +*/ + +int +sm_stdsetmode(fp, mode) + SM_FILE_T *fp; + const int *mode; +{ + int flags = 0; + + switch (SM_IO_MODE(*mode)) + { + case SM_IO_RDWR: + flags |= SMRW; + break; + case SM_IO_RDONLY: + flags |= SMRD; + break; + case SM_IO_WRONLY: + flags |= SMWR; + break; + case SM_IO_APPEND: + default: + errno = EINVAL; + return -1; + } + fp->f_flags = fp->f_flags & ~SMMODEMASK; + fp->f_flags |= flags; + return 0; +} + +/* +** SM_STDGETMODE -- for getinfo determine open mode +** +** Called by sm_stdgetinfo(). +** +** Parameters: +** fp -- the file mode being determined +** mode -- internal mode to map to external value +** +** Results: +** Failure: -1 and sets errno +** Success: external mode value +*/ + +static int +sm_stdgetmode(fp, mode) + SM_FILE_T *fp; + int *mode; +{ + switch (fp->f_flags & SMMODEMASK) + { + case SMRW: + *mode = SM_IO_RDWR; + break; + case SMRD: + *mode = SM_IO_RDONLY; + break; + case SMWR: + *mode = SM_IO_WRONLY; + break; + default: + errno = EINVAL; + return -1; + } + return 0; +} + +/* +** SM_STDSETINFO -- set/modify information for a file +** +** Parameters: +** fp -- file to set info for +** what -- type of info to set +** valp -- location of data used for setting +** +** Returns: +** Failure: -1 and sets errno +** Success: >=0 +*/ + +int +sm_stdsetinfo(fp, what, valp) + SM_FILE_T *fp; + int what; + void *valp; +{ + switch (what) + { + case SM_IO_WHAT_MODE: + return sm_stdsetmode(fp, (const int *)valp); + + default: + errno = EINVAL; + return -1; + } +} + +/* +** SM_GETINFO -- get information about the open file +** +** Parameters: +** fp -- file to get info for +** what -- type of info to get +** valp -- location to place found info +** +** Returns: +** Success: may or may not place info in 'valp' depending +** on 'what' value, and returns values >=0. Return +** value may be the obtained info +** Failure: -1 and sets errno +*/ + +int +sm_stdgetinfo(fp, what, valp) + SM_FILE_T *fp; + int what; + void *valp; +{ + switch (what) + { + case SM_IO_WHAT_MODE: + return sm_stdgetmode(fp, (int *)valp); + + case SM_IO_WHAT_FD: + return fp->f_file; + + case SM_IO_WHAT_SIZE: + { + struct stat st; + + if (fstat(fp->f_file, &st) == 0) + return st.st_size; + else + return -1; + } + + case SM_IO_IS_READABLE: + { + fd_set readfds; + struct timeval timeout; + + if (SM_FD_SETSIZE > 0 && fp->f_file >= SM_FD_SETSIZE) + { + errno = EINVAL; + return -1; + } + FD_ZERO(&readfds); + SM_FD_SET(fp->f_file, &readfds); + timeout.tv_sec = 0; + timeout.tv_usec = 0; + if (select(fp->f_file + 1, FDSET_CAST &readfds, + NULL, NULL, &timeout) > 0 && + SM_FD_ISSET(fp->f_file, &readfds)) + return 1; + return 0; + } + + default: + errno = EINVAL; + return -1; + } +} + +/* +** SM_STDFDOPEN -- open file by primitive 'fd' rather than pathname +** +** I/O function to handle fdopen() stdio equivalence. The rest of +** the functions are the same as the sm_stdopen() above. +** +** Parameters: +** fp -- the file pointer to be associated with the open +** name -- the primitive file descriptor for association +** flags -- indicates type of access methods +** rpool -- ignored +** +** Results: +** Success: primitive file descriptor value +** Failure: -1 and sets errno +*/ + +/* ARGSUSED3 */ +int +sm_stdfdopen(fp, info, flags, rpool) + SM_FILE_T *fp; + const void *info; + int flags; + const void *rpool; +{ + int oflags, tmp, fdflags, fd = *((int *) info); + + switch (SM_IO_MODE(flags)) + { + case SM_IO_RDWR: + oflags = O_RDWR | O_CREAT; + break; + case SM_IO_RDONLY: + oflags = O_RDONLY; + break; + case SM_IO_WRONLY: + oflags = O_WRONLY | O_CREAT | O_TRUNC; + break; + case SM_IO_APPEND: + oflags = O_APPEND | O_WRONLY | O_CREAT; + break; + case SM_IO_APPENDRW: + oflags = O_APPEND | O_RDWR | O_CREAT; + break; + default: + errno = EINVAL; + return -1; + } +#ifdef O_BINARY + if (SM_IS_BINARY(flags)) + oflags |= O_BINARY; +#endif /* O_BINARY */ + + /* Make sure the mode the user wants is a subset of the actual mode. */ + if ((fdflags = fcntl(fd, F_GETFL, 0)) < 0) + return -1; + tmp = fdflags & O_ACCMODE; + if (tmp != O_RDWR && (tmp != (oflags & O_ACCMODE))) + { + errno = EINVAL; + return -1; + } + fp->f_file = fd; + if (oflags & O_APPEND) + (void) (*fp->f_seek)(fp, (off_t)0, SEEK_END); + return fp->f_file; +} + +/* +** SM_IO_FOPEN -- open a file +** +** Same interface and semantics as the open() system call, +** except that it returns SM_FILE_T* instead of a file descriptor. +** +** Parameters: +** pathname -- path of file to open +** flags -- flags controlling the open +** ... -- option "mode" for opening the file +** +** Returns: +** Raises an exception on heap exhaustion. +** Returns NULL and sets errno if open() fails. +** Returns an SM_FILE_T pointer on success. +*/ + +SM_FILE_T * +#if SM_VA_STD +sm_io_fopen(char *pathname, int flags, ...) +#else /* SM_VA_STD */ +sm_io_fopen(pathname, flags, va_alist) + char *pathname; + int flags; + va_dcl +#endif /* SM_VA_STD */ +{ + MODE_T mode; + SM_FILE_T *fp; + int ioflags; + + if (flags & O_CREAT) + { + SM_VA_LOCAL_DECL + + SM_VA_START(ap, flags); + mode = (MODE_T) SM_VA_ARG(ap, int); + SM_VA_END(ap); + } + else + mode = 0; + + switch (flags & O_ACCMODE) + { + case O_RDONLY: + ioflags = SMRD; + break; + case O_WRONLY: + ioflags = SMWR; + break; + case O_RDWR: + ioflags = SMRW; + break; + default: + sm_abort("sm_io_fopen: bad flags 0%o", flags); + } + + fp = sm_fp(SmFtStdio, ioflags, NULL); + fp->f_file = open(pathname, flags, mode); + if (fp->f_file == -1) + { + fp->f_flags = 0; + fp->sm_magic = NULL; + return NULL; + } + return fp; +} diff --git a/usr/src/cmd/sendmail/libsm/strcasecmp.c b/usr/src/cmd/sendmail/libsm/strcasecmp.c new file mode 100644 index 0000000000..e2a7b798e8 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/strcasecmp.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. + * Copyright (c) 1987, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: strcasecmp.c,v 1.12 2001/08/27 22:09:15 gshapiro Exp $") + +#include <sm/config.h> +#include <sm/string.h> +#include <string.h> + +/* +** SM_STRCASECMP -- 8-bit clean version of strcasecmp +** +** Thank you, vendors, for making this all necessary. +*/ + +/* +** This array is designed for mapping upper and lower case letter +** together for a case independent comparison. The mappings are +** based upon ascii character sequences. +*/ + +const unsigned char charmap[] = +{ + 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007, + 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017, + 0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027, + 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037, + 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047, + 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057, + 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, + 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077, + 0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147, + 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157, + 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, + 0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137, + 0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147, + 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157, + 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, + 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, + 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207, + 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217, + 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227, + 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237, + 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247, + 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257, + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, + 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307, + 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317, + 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327, + 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, + 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, + 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357, + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, + 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377, +}; + +int +sm_strcasecmp(s1, s2) + const char *s1, *s2; +{ + const unsigned char *us1 = (const unsigned char *)s1; + const unsigned char *us2 = (const unsigned char *)s2; + + while (charmap[*us1] == charmap[*us2]) + { + if (*us1 == '\0') + return 0; + ++us1; + ++us2; + } + return charmap[*us1] - charmap[*us2]; +} + +int +sm_strncasecmp(s1, s2, n) + const char *s1, *s2; + register size_t n; +{ + if (n != 0) + { + register const unsigned char *cm = charmap; + register const unsigned char *us1 = (const unsigned char *)s1; + register const unsigned char *us2 = (const unsigned char *)s2; + + do + { + if (cm[*us1] != cm[*us2++]) + return (cm[*us1] - cm[*--us2]); + if (*us1++ == '\0') + break; + } while (--n != 0); + } + return 0; +} diff --git a/usr/src/cmd/sendmail/libsm/strdup.c b/usr/src/cmd/sendmail/libsm/strdup.c new file mode 100644 index 0000000000..13425ddc70 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/strdup.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2000-2001, 2003 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: strdup.c,v 1.15 2003/10/10 17:56:57 ca Exp $") + +#include <sm/heap.h> +#include <sm/string.h> + +/* +** SM_STRNDUP_X -- Duplicate a string of a given length +** +** Allocates memory and copies source string (of given length) into it. +** +** Parameters: +** s -- string to copy. +** n -- length to copy. +** +** Returns: +** copy of string, raises exception if out of memory. +** +** Side Effects: +** allocate memory for new string. +*/ + +char * +sm_strndup_x(s, n) + const char *s; + size_t n; +{ + char *d = sm_malloc_x(n + 1); + + (void) memcpy(d, s, n); + d[n] = '\0'; + return d; +} + +/* +** SM_STRDUP -- Duplicate a string +** +** Allocates memory and copies source string into it. +** +** Parameters: +** s -- string to copy. +** +** Returns: +** copy of string, NULL if out of memory. +** +** Side Effects: +** allocate memory for new string. +*/ + +char * +sm_strdup(s) + char *s; +{ + size_t l; + char *d; + + l = strlen(s) + 1; + d = sm_malloc_tagged(l, "sm_strdup", 0, sm_heap_group()); + if (d != NULL) + (void) sm_strlcpy(d, s, l); + return d; +} + +#if DO_NOT_USE_STRCPY + +/* +** SM_STRDUP_X -- Duplicate a string +** +** Allocates memory and copies source string into it. +** +** Parameters: +** s -- string to copy. +** +** Returns: +** copy of string, exception if out of memory. +** +** Side Effects: +** allocate memory for new string. +*/ + +char * +sm_strdup_x(s) + const char *s; +{ + size_t l; + char *d; + + l = strlen(s) + 1; + d = sm_malloc_tagged_x(l, "sm_strdup_x", 0, sm_heap_group()); + (void) sm_strlcpy(d, s, l); + return d; +} + +/* +** SM_PSTRDUP_X -- Duplicate a string (using "permanent" memory) +** +** Allocates memory and copies source string into it. +** +** Parameters: +** s -- string to copy. +** +** Returns: +** copy of string, exception if out of memory. +** +** Side Effects: +** allocate memory for new string. +*/ + +char * +sm_pstrdup_x(s) + const char *s; +{ + size_t l; + char *d; + + l = strlen(s) + 1; + d = sm_pmalloc_x(l); + (void) sm_strlcpy(d, s, l); + return d; +} + +/* +** SM_STRDUP_X -- Duplicate a string +** +** Allocates memory and copies source string into it. +** +** Parameters: +** s -- string to copy. +** file -- name of source file +** line -- line in source file +** group -- heap group +** +** Returns: +** copy of string, exception if out of memory. +** +** Side Effects: +** allocate memory for new string. +*/ + +char * +sm_strdup_tagged_x(s, file, line, group) + const char *s; + char *file; + int line, group; +{ + size_t l; + char *d; + + l = strlen(s) + 1; + d = sm_malloc_tagged_x(l, file, line, group); + (void) sm_strlcpy(d, s, l); + return d; +} + +#endif /* DO_NOT_USE_STRCPY */ + diff --git a/usr/src/cmd/sendmail/libsm/strerror.c b/usr/src/cmd/sendmail/libsm/strerror.c new file mode 100644 index 0000000000..f58f6626b2 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/strerror.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: strerror.c,v 1.21 2001/06/17 21:31:41 ca Exp $") + +/* +** define strerror for platforms that lack it. +*/ + +#include <errno.h> +#include <stdio.h> /* sys_errlist, on some platforms */ + +#include <sm/io.h> /* sm_snprintf */ +#include <sm/string.h> +#include <sm/conf.h> +#include <sm/errstring.h> + +#if !defined(ERRLIST_PREDEFINED) +extern char *sys_errlist[]; +extern int sys_nerr; +#endif /* !defined(ERRLIST_PREDEFINED) */ + +#if !HASSTRERROR + +/* +** STRERROR -- return error message string corresponding to an error number. +** +** Parameters: +** err -- error number. +** +** Returns: +** Error string (might be pointer to static buffer). +*/ + +char * +strerror(err) + int err; +{ + static char buf[64]; + + if (err >= 0 && err < sys_nerr) + return (char *) sys_errlist[err]; + else + { + (void) sm_snprintf(buf, sizeof(buf), "Error %d", err); + return buf; + } +} +#endif /* !HASSTRERROR */ diff --git a/usr/src/cmd/sendmail/libsm/strexit.c b/usr/src/cmd/sendmail/libsm/strexit.c new file mode 100644 index 0000000000..4c2b5be0ef --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/strexit.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: strexit.c,v 1.3 2001/01/15 18:39:11 ca Exp $") +#include <sm/string.h> +#include <sm/sysexits.h> + +/* +** SM_STREXIT -- convert EX_* value from <sm/sysexits.h> to a character string +** +** This function is analogous to strerror(), except that it +** operates on EX_* values from <sm/sysexits.h>. +** +** Parameters: +** ex -- EX_* value +** +** Results: +** pointer to a static message string +*/ + +char * +sm_strexit(ex) + int ex; +{ + char *msg; + static char buf[64]; + + msg = sm_sysexitmsg(ex); + if (msg == NULL) + { + (void) sm_snprintf(buf, sizeof buf, "Unknown exit status %d", + ex); + msg = buf; + } + return msg; +} + +/* +** SM_SYSEXITMSG -- convert an EX_* value to a character string, or NULL +** +** Parameters: +** ex -- EX_* value +** +** Results: +** If ex is a known exit value, then a pointer to a static +** message string is returned. Otherwise NULL is returned. +*/ + +char * +sm_sysexitmsg(ex) + int ex; +{ + char *msg; + + msg = sm_sysexmsg(ex); + if (msg != NULL) + return &msg[11]; + else + return msg; +} + +/* +** SM_SYSEXMSG -- convert an EX_* value to a character string, or NULL +** +** Parameters: +** ex -- EX_* value +** +** Results: +** If ex is a known exit value, then a pointer to a static +** string is returned. Otherwise NULL is returned. +** The string contains the following fixed width fields: +** [0] ':' if there is an errno value associated with this +** exit value, otherwise ' '. +** [1,3] 3 digit SMTP error code +** [4] ' ' +** [5,9] 3 digit SMTP extended error code +** [10] ' ' +** [11,] message string +*/ + +char * +sm_sysexmsg(ex) + int ex; +{ + switch (ex) + { + case EX_USAGE: + return " 500 5.0.0 Command line usage error"; + case EX_DATAERR: + return " 501 5.6.0 Data format error"; + case EX_NOINPUT: + return ":550 5.3.0 Cannot open input"; + case EX_NOUSER: + return " 550 5.1.1 User unknown"; + case EX_NOHOST: + return " 550 5.1.2 Host unknown"; + case EX_UNAVAILABLE: + return " 554 5.0.0 Service unavailable"; + case EX_SOFTWARE: + return ":554 5.3.0 Internal error"; + case EX_OSERR: + return ":451 4.0.0 Operating system error"; + case EX_OSFILE: + return ":554 5.3.5 System file missing"; + case EX_CANTCREAT: + return ":550 5.0.0 Can't create output"; + case EX_IOERR: + return ":451 4.0.0 I/O error"; + case EX_TEMPFAIL: + return " 450 4.0.0 Deferred"; + case EX_PROTOCOL: + return " 554 5.5.0 Remote protocol error"; + case EX_NOPERM: + return ":550 5.0.0 Insufficient permission"; + case EX_CONFIG: + return " 554 5.3.5 Local configuration error"; + default: + return NULL; + } +} diff --git a/usr/src/cmd/sendmail/libsm/string.c b/usr/src/cmd/sendmail/libsm/string.c new file mode 100644 index 0000000000..9e7715b89c --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/string.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: string.c,v 1.1 2001/02/15 21:04:50 ca Exp $") + +#include <ctype.h> +#include <errno.h> + +#include <sm/string.h> + +/* +** STRIPQUOTES -- Strip quotes & quote bits from a string. +** +** Runs through a string and strips off unquoted quote +** characters and quote bits. This is done in place. +** +** Parameters: +** s -- the string to strip. +** +** Returns: +** none. +** +** Side Effects: +** none. +*/ + +void +stripquotes(s) + char *s; +{ + register char *p; + register char *q; + register char c; + + if (s == NULL) + return; + + p = q = s; + do + { + c = *p++; + if (c == '\\') + c = *p++; + else if (c == '"') + continue; + *q++ = c; + } while (c != '\0'); +} diff --git a/usr/src/cmd/sendmail/libsm/stringf.c b/usr/src/cmd/sendmail/libsm/stringf.c new file mode 100644 index 0000000000..b839f2cef1 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/stringf.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: stringf.c,v 1.13 2001/03/03 03:40:43 ca Exp $") +#include <errno.h> +#include <stdio.h> +#include <sm/exc.h> +#include <sm/heap.h> +#include <sm/string.h> +#include <sm/varargs.h> + +/* +** SM_STRINGF_X -- printf() to dynamically allocated string. +** +** Takes the same arguments as printf. +** It returns a pointer to a dynamically allocated string +** containing the text that printf would print to standard output. +** It raises an exception on error. +** The name comes from a PWB Unix function called stringf. +** +** Parameters: +** fmt -- format string. +** ... -- arguments for format. +** +** Returns: +** Pointer to a dynamically allocated string. +** +** Exceptions: +** F:sm_heap -- out of memory (via sm_vstringf_x()). +*/ + +char * +#if SM_VA_STD +sm_stringf_x(const char *fmt, ...) +#else /* SM_VA_STD */ +sm_stringf_x(fmt, va_alist) + const char *fmt; + va_dcl +#endif /* SM_VA_STD */ +{ + SM_VA_LOCAL_DECL + char *s; + + SM_VA_START(ap, fmt); + s = sm_vstringf_x(fmt, ap); + SM_VA_END(ap); + return s; +} + +/* +** SM_VSTRINGF_X -- printf() to dynamically allocated string. +** +** Parameters: +** fmt -- format string. +** ap -- arguments for format. +** +** Returns: +** Pointer to a dynamically allocated string. +** +** Exceptions: +** F:sm_heap -- out of memory +*/ + +char * +sm_vstringf_x(fmt, ap) + const char *fmt; + SM_VA_LOCAL_DECL +{ + char *s; + + sm_vasprintf(&s, fmt, ap); + if (s == NULL) + { + if (errno == ENOMEM) + sm_exc_raise_x(&SmHeapOutOfMemory); + sm_exc_raisenew_x(&SmEtypeOs, errno, "sm_vasprintf", NULL); + } + return s; +} diff --git a/usr/src/cmd/sendmail/libsm/strio.c b/usr/src/cmd/sendmail/libsm/strio.c new file mode 100644 index 0000000000..7948f12613 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/strio.c @@ -0,0 +1,492 @@ +/* + * Copyright (c) 2000-2002, 2004 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_IDSTR(id, "@(#)$Id: strio.c,v 1.43 2004/08/03 20:48:30 ca Exp $") +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <sm/rpool.h> +#include <sm/io.h> +#include <sm/heap.h> +#include <sm/conf.h> +#include "local.h" + +static int sm_strsetmode __P((SM_FILE_T*, const int *)); +static int sm_strgetmode __P((SM_FILE_T*, int *)); + +/* +** Cookie structure for the "strio" file type +*/ + +struct sm_str_obj +{ + char *strio_base; + char *strio_end; + size_t strio_size; + size_t strio_offset; + int strio_flags; + const void *strio_rpool; +}; + +typedef struct sm_str_obj SM_STR_OBJ_T; + +/* +** SM_STRGROW -- increase storage space for string +** +** Parameters: +** s -- current cookie +** size -- new storage size request +** +** Returns: +** true iff successful. +*/ + +static bool sm_strgrow __P((SM_STR_OBJ_T *, size_t)); + +static bool +sm_strgrow(s, size) + SM_STR_OBJ_T *s; + size_t size; +{ + register void *p; + + if (s->strio_size >= size) + return true; + p = sm_realloc(s->strio_base, size); + if (p == NULL) + return false; + s->strio_base = p; + s->strio_end = s->strio_base + size; + s->strio_size = size; + return true; +} + +/* +** SM_STRREAD -- read a portion of the string +** +** Parameters: +** fp -- the file pointer +** buf -- location to place read data +** n -- number of bytes to read +** +** Returns: +** Failure: -1 and sets errno +** Success: >=0, number of bytes read +*/ + +ssize_t +sm_strread(fp, buf, n) + SM_FILE_T *fp; + char *buf; + size_t n; +{ + register SM_STR_OBJ_T *s = fp->f_cookie; + int len; + + if (!(s->strio_flags & SMRD) && !(s->strio_flags & SMRW)) + { + errno = EBADF; + return -1; + } + len = SM_MIN(s->strio_size - s->strio_offset, n); + (void) memmove(buf, s->strio_base + s->strio_offset, len); + s->strio_offset += len; + return len; +} + +/* +** SM_STRWRITE -- write a portion of the string +** +** Parameters: +** fp -- the file pointer +** buf -- location of data for writing +** n -- number of bytes to write +** +** Returns: +** Failure: -1 and sets errno +** Success: >=0, number of bytes written +*/ + +ssize_t +sm_strwrite(fp, buf, n) + SM_FILE_T *fp; + char const *buf; + size_t n; +{ + register SM_STR_OBJ_T *s = fp->f_cookie; + + if (!(s->strio_flags & SMWR) && !(s->strio_flags & SMRW)) + { + errno = EBADF; + return -1; + } + if (n + s->strio_offset > s->strio_size) + { + if (!sm_strgrow(s, n + s->strio_offset)) + return 0; + } + (void) memmove(s->strio_base + s->strio_offset, buf, n); + s->strio_offset += n; + return n; +} + +/* +** SM_STRSEEK -- position the offset pointer for the string +** +** Only SM_IO_SEEK_SET, SM_IO_SEEK_CUR and SM_IO_SEEK_END are valid +** values for whence. +** +** Parameters: +** fp -- the file pointer +** offset -- number of bytes offset from "base" +** whence -- determines "base" for 'offset' +** +** Returns: +** Failure: -1 and sets errno +** Success: >=0, number of bytes read +*/ + +off_t +sm_strseek(fp, offset, whence) + SM_FILE_T *fp; + off_t offset; + int whence; +{ + register off_t ret; + register SM_STR_OBJ_T *s = fp->f_cookie; + +reseek: + switch (whence) + { + case SM_IO_SEEK_SET: + ret = offset; + break; + case SM_IO_SEEK_CUR: + ret = s->strio_offset + offset; + break; + case SM_IO_SEEK_END: + ret = s->strio_size; + break; + default: + errno = EINVAL; + return -1; + } + if (ret < 0 || ret > (off_t)(size_t)(-1)) /* XXX ugly */ + return -1; + if ((size_t) ret > s->strio_size) + { + if (sm_strgrow(s, (size_t)ret)) + goto reseek; + + /* errno set by sm_strgrow */ + return -1; + } + s->strio_offset = (size_t) ret; + return ret; +} + +/* +** SM_STROPEN -- open a string file type +** +** Parameters: +** fp -- file pointer open to be associated with +** info -- initial contents (NULL for none) +** flags -- flags for methods of access (was mode) +** rpool -- resource pool to use memory from (if applicable) +** +** Results: +** Success: 0 (zero) +** Failure: -1 and sets errno +*/ + +int +sm_stropen(fp, info, flags, rpool) + SM_FILE_T *fp; + const void *info; + int flags; + const void *rpool; +{ + register SM_STR_OBJ_T *s; + +#if SM_RPOOL + s = sm_rpool_malloc_x(rpool, sizeof(SM_STR_OBJ_T)); +#else /* SM_RPOOL */ + s = sm_malloc(sizeof(SM_STR_OBJ_T)); + if (s == NULL) + return -1; +#endif /* SM_RPOOL */ + + fp->f_cookie = s; + s->strio_rpool = rpool; + s->strio_offset = 0; + s->strio_size = 0; + s->strio_base = NULL; + s->strio_end = 0; + + switch (flags) + { + case SM_IO_RDWR: + s->strio_flags = SMRW; + break; + case SM_IO_RDONLY: + s->strio_flags = SMRD; + break; + case SM_IO_WRONLY: + s->strio_flags = SMWR; + break; + case SM_IO_APPEND: + if (s->strio_rpool == NULL) + sm_free(s); + errno = EINVAL; + return -1; + default: + if (s->strio_rpool == NULL) + sm_free(s); + errno = EINVAL; + return -1; + } + + if (info != NULL) + { + s->strio_base = sm_strdup_x(info); + if (s->strio_base == NULL) + { + int save_errno = errno; + + if (s->strio_rpool == NULL) + sm_free(s); + errno = save_errno; + return -1; + } + s->strio_size = strlen(info); + s->strio_end = s->strio_base + s->strio_size; + } + return 0; +} + +/* +** SM_STRCLOSE -- close the string file type and free resources +** +** Parameters: +** fp -- file pointer +** +** Results: +** Success: 0 (zero) +*/ + +int +sm_strclose(fp) + SM_FILE_T *fp; +{ + SM_STR_OBJ_T *s = fp->f_cookie; + +#if !SM_RPOOL + sm_free(s->strio_base); + s->strio_base = NULL; +#endif /* !SM_RPOOL */ + return 0; +} + +/* +** SM_STRSETMODE -- set mode info for the file +** +** Note: changing the mode can be a safe way to have the "parent" +** set up a string that the "child" is not to modify +** +** Parameters: +** fp -- the file pointer +** mode -- location of new mode to set +** +** Results: +** Success: 0 (zero) +** Failure: -1 and sets errno +*/ + +static int +sm_strsetmode(fp, mode) + SM_FILE_T *fp; + const int *mode; +{ + register SM_STR_OBJ_T *s = fp->f_cookie; + int flags; + + switch (*mode) + { + case SM_IO_RDWR: + flags = SMRW; + break; + case SM_IO_RDONLY: + flags = SMRD; + break; + case SM_IO_WRONLY: + flags = SMWR; + break; + case SM_IO_APPEND: + errno = EINVAL; + return -1; + default: + errno = EINVAL; + return -1; + } + s->strio_flags &= ~SMMODEMASK; + s->strio_flags |= flags; + return 0; +} + +/* +** SM_STRGETMODE -- get mode info for the file +** +** Parameters: +** fp -- the file pointer +** mode -- location to store current mode +** +** Results: +** Success: 0 (zero) +** Failure: -1 and sets errno +*/ + +int +sm_strgetmode(fp, mode) + SM_FILE_T *fp; + int *mode; +{ + register SM_STR_OBJ_T *s = fp->f_cookie; + + switch (s->strio_flags & SMMODEMASK) + { + case SMRW: + *mode = SM_IO_RDWR; + break; + case SMRD: + *mode = SM_IO_RDONLY; + break; + case SMWR: + *mode = SM_IO_WRONLY; + break; + default: + errno = EINVAL; + return -1; + } + return 0; +} + +/* +** SM_STRSETINFO -- set info for the file +** +** Currently only SM_IO_WHAT_MODE is supported for 'what'. +** +** Parameters: +** fp -- the file pointer +** what -- type of information to set +** valp -- location to data for doing set +** +** Results: +** Failure: -1 and sets errno +** Success: sm_strsetmode() return [0 (zero)] +*/ + +int +sm_strsetinfo(fp, what, valp) + SM_FILE_T *fp; + int what; + void *valp; +{ + switch(what) + { + case SM_IO_WHAT_MODE: + return sm_strsetmode(fp, (int *) valp); + default: + errno = EINVAL; + return -1; + } +} + +/* +** SM_STRGETINFO -- get info for the file +** +** Currently only SM_IO_WHAT_MODE is supported for 'what'. +** +** Parameters: +** fp -- the file pointer +** what -- type of information requested +** valp -- location to return information in +** +** Results: +** Failure: -1 and sets errno +** Success: sm_strgetmode() return [0 (zero)] +*/ + +int +sm_strgetinfo(fp, what, valp) + SM_FILE_T *fp; + int what; + void *valp; +{ + switch(what) + { + case SM_IO_WHAT_MODE: + return sm_strgetmode(fp, (int *) valp); + default: + errno = EINVAL; + return -1; + } +} + +/* +** SM_STRIO_INIT -- initializes a write-only string type +** +** Original comments below. This function does not appear to be used anywhere. +** The same functionality can be done by changing the mode of the file. +** ------------ +** sm_strio_init initializes an SM_FILE_T structure as a write-only file +** that writes into the specified buffer: +** - Use sm_io_putc, sm_io_fprintf, etc, to write into the buffer. +** Attempts to write more than size-1 characters into the buffer will fail +** silently (no error is reported). +** - Use sm_io_fflush to nul terminate the string in the buffer +** (the write pointer is not advanced). +** No memory is allocated either by sm_strio_init or by sm_io_{putc,write} etc. +** +** Parameters: +** fp -- file pointer +** buf -- memory location for stored data +** size -- size of 'buf' +** +** Results: +** none. +*/ + +void +sm_strio_init(fp, buf, size) + SM_FILE_T *fp; + char *buf; + size_t size; +{ + fp->sm_magic = SmFileMagic; + fp->f_flags = SMWR | SMSTR; + fp->f_file = -1; + fp->f_bf.smb_base = fp->f_p = (unsigned char *) buf; + fp->f_bf.smb_size = fp->f_w = (size ? size - 1 : 0); + fp->f_lbfsize = 0; + fp->f_r = 0; + fp->f_read = NULL; + fp->f_seek = NULL; + fp->f_getinfo = NULL; + fp->f_setinfo = NULL; +} diff --git a/usr/src/cmd/sendmail/libsm/strl.c b/usr/src/cmd/sendmail/libsm/strl.c new file mode 100644 index 0000000000..702c2c9291 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/strl.c @@ -0,0 +1,326 @@ +/* + * Copyright (c) 1999-2002 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: strl.c,v 1.31 2002/01/20 01:41:25 gshapiro Exp $") +#include <sm/config.h> +#include <sm/string.h> + +/* +** Notice: this file is used by libmilter. Please try to avoid +** using libsm specific functions. +*/ + +/* +** XXX the type of the length parameter has been changed +** from size_t to ssize_t to avoid theoretical problems with negative +** numbers passed into these functions. +** The real solution to this problem is to make sure that this doesn't +** happen, but for now we'll use this workaround. +*/ + +/* +** SM_STRLCPY -- size bounded string copy +** +** This is a bounds-checking variant of strcpy. +** If size > 0, copy up to size-1 characters from the nul terminated +** string src to dst, nul terminating the result. If size == 0, +** the dst buffer is not modified. +** Additional note: this function has been "tuned" to run fast and tested +** as such (versus versions in some OS's libc). +** +** The result is strlen(src). You can detect truncation (not all +** of the characters in the source string were copied) using the +** following idiom: +** +** char *s, buf[BUFSIZ]; +** ... +** if (sm_strlcpy(buf, s, sizeof(buf)) >= sizeof(buf)) +** goto overflow; +** +** Parameters: +** dst -- destination buffer +** src -- source string +** size -- size of destination buffer +** +** Returns: +** strlen(src) +*/ + +size_t +sm_strlcpy(dst, src, size) + register char *dst; + register const char *src; + ssize_t size; +{ + register ssize_t i; + + if (size-- <= 0) + return strlen(src); + for (i = 0; i < size && (dst[i] = src[i]) != 0; i++) + continue; + dst[i] = '\0'; + if (src[i] == '\0') + return i; + else + return i + strlen(src + i); +} + +/* +** SM_STRLCAT -- size bounded string concatenation +** +** This is a bounds-checking variant of strcat. +** If strlen(dst) < size, then append at most size - strlen(dst) - 1 +** characters from the source string to the destination string, +** nul terminating the result. Otherwise, dst is not modified. +** +** The result is the initial length of dst + the length of src. +** You can detect overflow (not all of the characters in the +** source string were copied) using the following idiom: +** +** char *s, buf[BUFSIZ]; +** ... +** if (sm_strlcat(buf, s, sizeof(buf)) >= sizeof(buf)) +** goto overflow; +** +** Parameters: +** dst -- nul-terminated destination string buffer +** src -- nul-terminated source string +** size -- size of destination buffer +** +** Returns: +** total length of the string tried to create +** (= initial length of dst + length of src) +*/ + +size_t +sm_strlcat(dst, src, size) + register char *dst; + register const char *src; + ssize_t size; +{ + register ssize_t i, j, o; + + o = strlen(dst); + if (size < o + 1) + return o + strlen(src); + size -= o + 1; + for (i = 0, j = o; i < size && (dst[j] = src[i]) != 0; i++, j++) + continue; + dst[j] = '\0'; + if (src[i] == '\0') + return j; + else + return j + strlen(src + i); +} +/* +** SM_STRLCAT2 -- append two strings to dst obeying length and +** '\0' terminate it +** +** strlcat2 will append at most len - strlen(dst) - 1 chars. +** terminates with '\0' if len > 0 +** dst = dst "+" src1 "+" src2 +** use this instead of sm_strlcat(dst,src1); sm_strlcat(dst,src2); +** for better speed. +** +** Parameters: +** dst -- "destination" string. +** src1 -- "from" string 1. +** src2 -- "from" string 2. +** len -- max. length of "destination" string. +** +** Returns: +** total length of the string tried to create +** (= initial length of dst + length of src) +** if this is greater than len then an overflow would have +** occurred. +** +*/ + +size_t +sm_strlcat2(dst, src1, src2, len) + register char *dst; + register const char *src1; + register const char *src2; + ssize_t len; +{ + register ssize_t i, j, o; + + /* current size of dst */ + o = strlen(dst); + + /* max. size is less than current? */ + if (len < o + 1) + return o + strlen(src1) + strlen(src2); + + len -= o + 1; /* space left in dst */ + + /* copy the first string; i: index in src1; j: index in dst */ + for (i = 0, j = o; i < len && (dst[j] = src1[i]) != 0; i++, j++) + continue; + + /* src1: end reached? */ + if (src1[i] != '\0') + { + /* no: terminate dst; there is space since i < len */ + dst[j] = '\0'; + return j + strlen(src1 + i) + strlen(src2); + } + + len -= i; /* space left in dst */ + + /* copy the second string; i: index in src2; j: index in dst */ + for (i = 0; i < len && (dst[j] = src2[i]) != 0; i++, j++) + continue; + dst[j] = '\0'; /* terminate dst; there is space since i < len */ + if (src2[i] == '\0') + return j; + else + return j + strlen(src2 + i); +} + +/* +** SM_STRLCPYN -- concatenate n strings and assign the result to dst +** while obeying length and '\0' terminate it +** +** dst = src1 "+" src2 "+" ... +** use this instead of sm_snprintf() for string values +** and repeated sm_strlc*() calls for better speed. +** +** Parameters: +** dst -- "destination" string. +** len -- max. length of "destination" string. +** n -- number of strings +** strings... +** +** Returns: +** total length of the string tried to create +** (= initial length of dst + length of src) +** if this is greater than len then an overflow would have +** occurred. +*/ + +size_t +#ifdef __STDC__ +sm_strlcpyn(char *dst, ssize_t len, int n, ...) +#else /* __STDC__ */ +sm_strlcpyn(dst, len, n, va_alist) + register char *dst; + ssize_t len; + int n; + va_dcl +#endif /* __STDC__ */ +{ + register ssize_t i, j; + char *str; + SM_VA_LOCAL_DECL + + SM_VA_START(ap, n); + + if (len-- <= 0) /* This allows space for the terminating '\0' */ + { + i = 0; + while (n-- > 0) + i += strlen(SM_VA_ARG(ap, char *)); + SM_VA_END(ap); + return i; + } + + j = 0; /* index in dst */ + + /* loop through all source strings */ + while (n-- > 0) + { + str = SM_VA_ARG(ap, char *); + + /* copy string; i: index in str; j: index in dst */ + for (i = 0; j < len && (dst[j] = str[i]) != 0; i++, j++) + continue; + + /* str: end reached? */ + if (str[i] != '\0') + { + /* no: terminate dst; there is space since j < len */ + dst[j] = '\0'; + j += strlen(str + i); + while (n-- > 0) + j += strlen(SM_VA_ARG(ap, char *)); + SM_VA_END(ap); + return j; + } + } + SM_VA_END(ap); + + dst[j] = '\0'; /* terminate dst; there is space since j < len */ + return j; +} + +#if 0 +/* +** SM_STRLAPP -- append string if it fits into buffer. +** +** If size > 0, copy up to size-1 characters from the nul terminated +** string src to dst, nul terminating the result. If size == 0, +** the dst buffer is not modified. +** +** This routine is useful for appending strings in a loop, e.g, instead of +** s = buf; +** for (ptr, ptr != NULL, ptr = next->ptr) +** { +** (void) sm_strlcpy(s, ptr->string, sizeof buf - (s - buf)); +** s += strlen(s); +** } +** replace the loop body with: +** if (!sm_strlapp(*s, ptr->string, sizeof buf - (s - buf))) +** break; +** it's faster... +** +** XXX interface isn't completely clear (yet), hence this code is +** not available. +** +** +** Parameters: +** dst -- (pointer to) destination buffer +** src -- source string +** size -- size of destination buffer +** +** Returns: +** true if strlen(src) < size +** +** Side Effects: +** modifies dst if append succeeds (enough space). +*/ + +bool +sm_strlapp(dst, src, size) + register char **dst; + register const char *src; + ssize_t size; +{ + register size_t i; + + if (size-- <= 0) + return false; + for (i = 0; i < size && ((*dst)[i] = src[i]) != '\0'; i++) + continue; + (*dst)[i] = '\0'; + if (src[i] == '\0') + { + *dst += i; + return true; + } + + /* undo */ + (*dst)[0] = '\0'; + return false; +} +#endif /* 0 */ diff --git a/usr/src/cmd/sendmail/libsm/strrevcmp.c b/usr/src/cmd/sendmail/libsm/strrevcmp.c new file mode 100644 index 0000000000..f2714aeea8 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/strrevcmp.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: strrevcmp.c,v 1.2 2001/08/27 22:21:51 gshapiro Exp $") + +#include <sm/config.h> +#include <sm/string.h> +#include <string.h> + +/* strcasecmp.c */ +extern const unsigned char charmap[]; + +/* +** SM_STRREVCASECMP -- compare two strings starting at the end (ignore case) +** +** Parameters: +** s1 -- first string. +** s2 -- second string. +** +** Returns: +** strcasecmp(reverse(s1), reverse(s2)) +*/ + +int +sm_strrevcasecmp(s1, s2) + const char *s1, *s2; +{ + register int i1, i2; + + i1 = strlen(s1) - 1; + i2 = strlen(s2) - 1; + while (i1 >= 0 && i2 >= 0 && + charmap[(unsigned char) s1[i1]] == + charmap[(unsigned char) s2[i2]]) + { + --i1; + --i2; + } + if (i1 < 0) + { + if (i2 < 0) + return 0; + else + return -1; + } + else + { + if (i2 < 0) + return 1; + else + return (charmap[(unsigned char) s1[i1]] - + charmap[(unsigned char) s2[i2]]); + } +} +/* +** SM_STRREVCMP -- compare two strings starting at the end +** +** Parameters: +** s1 -- first string. +** s2 -- second string. +** +** Returns: +** strcmp(reverse(s1), reverse(s2)) +*/ + +int +sm_strrevcmp(s1, s2) + const char *s1, *s2; +{ + register int i1, i2; + + i1 = strlen(s1) - 1; + i2 = strlen(s2) - 1; + while (i1 >= 0 && i2 >= 0 && s1[i1] == s2[i2]) + { + --i1; + --i2; + } + if (i1 < 0) + { + if (i2 < 0) + return 0; + else + return -1; + } + else + { + if (i2 < 0) + return 1; + else + return s1[i1] - s2[i2]; + } +} diff --git a/usr/src/cmd/sendmail/libsm/strto.c b/usr/src/cmd/sendmail/libsm/strto.c new file mode 100644 index 0000000000..78a9dffcab --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/strto.c @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1992 + * The Regents of the University of California. All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_IDSTR(id, "@(#)$Id: strto.c,v 1.18 2001/12/30 04:59:37 gshapiro Exp $") + +#include <sys/param.h> +#include <sys/types.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> +#include <sm/limits.h> +#include <sm/conf.h> +#include <sm/string.h> + +/* +** SM_STRTOLL -- Convert a string to a (signed) long long integer. +** +** Ignores `locale' stuff. Assumes that the upper and lower case +** alphabets and digits are each contiguous. +** +** Parameters: +** nptr -- string containing number +** endptr -- location of first invalid character +** base -- numeric base that 'nptr' number is based in +** +** Returns: +** Failure: on underflow LLONG_MIN is returned; on overflow +** LLONG_MAX is returned and errno is set. +** When 'endptr' == '\0' then the entire string 'nptr' +** was valid. +** Success: returns the converted number +*/ + +LONGLONG_T +sm_strtoll(nptr, endptr, base) + const char *nptr; + char **endptr; + register int base; +{ + register bool neg; + register const char *s; + register LONGLONG_T acc, cutoff; + register int c; + register int any, cutlim; + + /* + ** Skip white space and pick up leading +/- sign if any. + ** If base is 0, allow 0x for hex and 0 for octal, else + ** assume decimal; if base is already 16, allow 0x. + */ + + s = nptr; + do + { + c = (unsigned char) *s++; + } while (isascii(c) && isspace(c)); + if (c == '-') + { + neg = true; + c = *s++; + } + else + { + neg = false; + if (c == '+') + c = *s++; + } + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) + { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + + /* + ** Compute the cutoff value between legal numbers and illegal + ** numbers. That is the largest legal value, divided by the + ** base. An input number that is greater than this value, if + ** followed by a legal input character, is too big. One that + ** is equal to this value may be valid or not; the limit + ** between valid and invalid numbers is then based on the last + ** digit. For instance, if the range for long-long's is + ** [-9223372036854775808..9223372036854775807] and the input base + ** is 10, cutoff will be set to 922337203685477580 and cutlim to + ** either 7 (!neg) or 8 (neg), meaning that if we have + ** accumulated a value > 922337203685477580, or equal but the + ** next digit is > 7 (or 8), the number is too big, and we will + ** return a range error. + ** + ** Set any if any `digits' consumed; make it negative to indicate + ** overflow. + */ + + cutoff = neg ? LLONG_MIN : LLONG_MAX; + cutlim = cutoff % base; + cutoff /= base; + if (neg) + { + if (cutlim > 0) + { + cutlim -= base; + cutoff += 1; + } + cutlim = -cutlim; + } + for (acc = 0, any = 0;; c = (unsigned char) *s++) + { + if (isascii(c) && isdigit(c)) + c -= '0'; + else if (isascii(c) && isalpha(c)) + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0) + continue; + if (neg) + { + if (acc < cutoff || (acc == cutoff && c > cutlim)) + { + any = -1; + acc = LLONG_MIN; + errno = ERANGE; + } + else + { + any = 1; + acc *= base; + acc -= c; + } + } + else + { + if (acc > cutoff || (acc == cutoff && c > cutlim)) + { + any = -1; + acc = LLONG_MAX; + errno = ERANGE; + } + else + { + any = 1; + acc *= base; + acc += c; + } + } + } + if (endptr != 0) + *endptr = (char *) (any ? s - 1 : nptr); + return acc; +} + +/* +** SM_STRTOULL -- Convert a string to an unsigned long long integer. +** +** Ignores `locale' stuff. Assumes that the upper and lower case +** alphabets and digits are each contiguous. +** +** Parameters: +** nptr -- string containing (unsigned) number +** endptr -- location of first invalid character +** base -- numeric base that 'nptr' number is based in +** +** Returns: +** Failure: on overflow ULLONG_MAX is returned and errno is set. +** When 'endptr' == '\0' then the entire string 'nptr' +** was valid. +** Success: returns the converted number +*/ + +ULONGLONG_T +sm_strtoull(nptr, endptr, base) + const char *nptr; + char **endptr; + register int base; +{ + register const char *s; + register ULONGLONG_T acc, cutoff; + register int c; + register bool neg; + register int any, cutlim; + + /* See sm_strtoll for comments as to the logic used. */ + s = nptr; + do + { + c = (unsigned char) *s++; + } while (isascii(c) && isspace(c)); + neg = (c == '-'); + if (neg) + { + c = *s++; + } + else + { + if (c == '+') + c = *s++; + } + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) + { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + + cutoff = ULLONG_MAX / (ULONGLONG_T)base; + cutlim = ULLONG_MAX % (ULONGLONG_T)base; + for (acc = 0, any = 0;; c = (unsigned char) *s++) + { + if (isascii(c) && isdigit(c)) + c -= '0'; + else if (isascii(c) && isalpha(c)) + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0) + continue; + if (acc > cutoff || (acc == cutoff && c > cutlim)) + { + any = -1; + acc = ULLONG_MAX; + errno = ERANGE; + } + else + { + any = 1; + acc *= (ULONGLONG_T)base; + acc += c; + } + } + if (neg && any > 0) + acc = -((LONGLONG_T) acc); + if (endptr != 0) + *endptr = (char *) (any ? s - 1 : nptr); + return acc; +} diff --git a/usr/src/cmd/sendmail/libsm/t-event.c b/usr/src/cmd/sendmail/libsm/t-event.c new file mode 100644 index 0000000000..0b56b07900 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/t-event.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2001-2002, 2004 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: t-event.c,v 1.12 2004/08/03 20:50:32 ca Exp $") + +#include <stdio.h> + +#include <stdlib.h> +#include <unistd.h> +# include <sys/wait.h> +#if SM_CONF_SETITIMER +# include <sys/time.h> +#endif /* SM_CONF_SETITIMER */ + +#include <sm/clock.h> +#include <sm/test.h> + +static void evcheck __P((int)); +static void ev1 __P((int)); + +static int check; + +static void +evcheck(arg) + int arg; +{ + SM_TEST(arg == 3); + SM_TEST(check == 0); + check++; +} + +static void +ev1(arg) + int arg; +{ + SM_TEST(arg == 1); +} + +/* define as x if you want debug output */ +#define DBG_OUT(x) + +int +main(argc, argv) + int argc; + char *argv[]; +{ + SM_EVENT *ev; + + sm_test_begin(argc, argv, "test event handling"); + fprintf(stdout, "This test may hang. If there is no output within twelve seconds, abort it\nand recompile with -DSM_CONF_SETITIMER=%d\n", + SM_CONF_SETITIMER == 0 ? 1 : 0); + sleep(1); + SM_TEST(1 == 1); + DBG_OUT(fprintf(stdout, "We're back, test 1 seems to work.\n")); + ev = sm_seteventm(1000, ev1, 1); + sleep(1); + SM_TEST(2 == 2); + DBG_OUT(fprintf(stdout, "We're back, test 2 seems to work.\n")); + + /* schedule an event in 9s */ + ev = sm_seteventm(9000, ev1, 2); + sleep(1); + + /* clear the event before it can fire */ + sm_clrevent(ev); + SM_TEST(3 == 3); + DBG_OUT(fprintf(stdout, "We're back, test 3 seems to work.\n")); + + /* schedule an event in 1s */ + check = 0; + ev = sm_seteventm(1000, evcheck, 3); + sleep(2); + + /* clear the event */ + sm_clrevent(ev); + SM_TEST(4 == 4); + SM_TEST(check == 1); + DBG_OUT(fprintf(stdout, "We're back, test 4 seems to work.\n")); + + return sm_test_end(); +} diff --git a/usr/src/cmd/sendmail/libsm/t-exc.c b/usr/src/cmd/sendmail/libsm/t-exc.c new file mode 100644 index 0000000000..1af39e0b7f --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/t-exc.c @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_IDSTR(id, "@(#)$Id: t-exc.c,v 1.18 2001/07/05 22:46:35 gshapiro Exp $") + +#include <string.h> +#include <sm/heap.h> +#include <sm/io.h> +#include <sm/test.h> + +const SM_EXC_TYPE_T EtypeTest1 = +{ + SmExcTypeMagic, + "E:test1", + "i", + sm_etype_printf, + "test1 exception argv[0]=%0", +}; + +const SM_EXC_TYPE_T EtypeTest2 = +{ + SmExcTypeMagic, + "E:test2", + "i", + sm_etype_printf, + "test2 exception argv[0]=%0", +}; + +int +main(argc, argv) + int argc; + char **argv; +{ + void *p; + int volatile x; + char *unknown, *cant; + + sm_test_begin(argc, argv, "test exception handling"); + + /* + ** SM_TRY + */ + + cant = "can't happen"; + x = 0; + SM_TRY + x = 1; + SM_END_TRY + SM_TEST(x == 1); + + /* + ** SM_FINALLY-0 + */ + + x = 0; + SM_TRY + x = 1; + SM_FINALLY + x = 2; + SM_END_TRY + SM_TEST(x == 2); + + /* + ** SM_FINALLY-1 + */ + + x = 0; + SM_TRY + SM_TRY + x = 1; + sm_exc_raisenew_x(&EtypeTest1, 17); + SM_FINALLY + x = 2; + sm_exc_raisenew_x(&EtypeTest2, 42); + SM_END_TRY + SM_EXCEPT(exc, "E:test2") + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "got exception test2: can't happen\n"); + SM_EXCEPT(exc, "E:test1") + SM_TEST(x == 2 && exc->exc_argv[0].v_int == 17); + if (!(x == 2 && exc->exc_argv[0].v_int == 17)) + { + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "can't happen: x=%d argv[0]=%d\n", + x, exc->exc_argv[0].v_int); + } + SM_EXCEPT(exc, "*") + { + unknown = "unknown exception: "; + SM_TEST(strcmp(unknown, cant) == 0); + } + SM_END_TRY + + x = 3; + SM_TRY + x = 4; + sm_exc_raisenew_x(&EtypeTest1, 94); + SM_FINALLY + x = 5; + sm_exc_raisenew_x(&EtypeTest2, 95); + SM_EXCEPT(exc, "E:test2") + { + unknown = "got exception test2: "; + SM_TEST(strcmp(unknown, cant) == 0); + } + SM_EXCEPT(exc, "E:test1") + SM_TEST(x == 5 && exc->exc_argv[0].v_int == 94); + if (!(x == 5 && exc->exc_argv[0].v_int == 94)) + { + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "can't happen: x=%d argv[0]=%d\n", + x, exc->exc_argv[0].v_int); + } + SM_EXCEPT(exc, "*") + { + unknown = "unknown exception: "; + SM_TEST(strcmp(unknown, cant) == 0); + } + SM_END_TRY + + SM_TRY + sm_exc_raisenew_x(&SmEtypeErr, "test %d", 0); + SM_EXCEPT(exc, "*") +#if DEBUG + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "test 0 got an exception, as expected:\n"); + sm_exc_print(exc, smioout); +#endif /* DEBUG */ + return sm_test_end(); + SM_END_TRY + + p = sm_malloc_x((size_t)(-1)); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "sm_malloc_x unexpectedly succeeded, returning %p\n", p); + unknown = "sm_malloc_x unexpectedly succeeded"; + SM_TEST(strcmp(unknown, cant) == 0); + return sm_test_end(); +} diff --git a/usr/src/cmd/sendmail/libsm/t-float.c b/usr/src/cmd/sendmail/libsm/t-float.c new file mode 100644 index 0000000000..b4d416ab97 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/t-float.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_IDSTR(id, "@(#)$Id: t-float.c,v 1.16 2001/02/02 23:11:46 ca Exp $") + +#include <sm/limits.h> +#include <sm/io.h> +#include <sm/string.h> +#include <sm/test.h> +#include <sm/types.h> + +int +main(argc, argv) + int argc; + char **argv; +{ + double d, d2; + double ld; + char buf[128]; + char *r; + + /* + ** Sendmail uses printf and scanf with doubles, + ** so make sure that this works. + */ + + sm_test_begin(argc, argv, "test floating point stuff"); + + d = 1.125; + sm_snprintf(buf, sizeof(buf), "%d %.3f %d", 0, d, 1); + r = "0 1.125 1"; + if (!SM_TEST(strcmp(buf, r) == 0)) + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "got %s instead\n", buf); + + d = 1.125; + sm_snprintf(buf, sizeof(buf), "%.3f", d); + r = "1.125"; + if (!SM_TEST(strcmp(buf, r) == 0)) + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "got %s instead\n", buf); + d2 = 0.0; + sm_io_sscanf(buf, "%lf", &d2); +#if SM_CONF_BROKEN_STRTOD + if (d != d2) + { + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "wanted %f, got %f\n", d, d2); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "error ignored since SM_CONF_BROKEN_STRTOD is set for this OS\n"); + } +#else /* SM_CONF_BROKEN_STRTOD */ + if (!SM_TEST(d == d2)) + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "wanted %f, got %f\n", d, d2); +#endif /* SM_CONF_BROKEN_STRTOD */ + + ld = 2.5; + sm_snprintf(buf, sizeof(buf), "%.3f %.1f", d, ld); + r = "1.125 2.5"; + if (!SM_TEST(strcmp(buf, r) == 0)) + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "got %s instead\n", buf); + return sm_test_end(); +} diff --git a/usr/src/cmd/sendmail/libsm/t-fopen.c b/usr/src/cmd/sendmail/libsm/t-fopen.c new file mode 100644 index 0000000000..2fcb2839e6 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/t-fopen.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2000-2002 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_IDSTR(id, "@(#)$Id: t-fopen.c,v 1.9 2002/02/06 23:57:45 ca Exp $") + +#include <fcntl.h> +#include <sm/io.h> +#include <sm/test.h> + +/* ARGSUSED0 */ +int +main(argc, argv) + int argc; + char *argv[]; +{ + int m, r; + SM_FILE_T *out; + + sm_test_begin(argc, argv, "test sm_io_fopen"); + out = sm_io_fopen("foo", O_WRONLY|O_APPEND|O_CREAT, 0666); + SM_TEST(out != NULL); + if (out != NULL) + { + (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "foo\n"); + r = sm_io_getinfo(out, SM_IO_WHAT_MODE, &m); + SM_TEST(r == 0); + SM_TEST(m == SM_IO_WRONLY); + sm_io_close(out, SM_TIME_DEFAULT); + } + return sm_test_end(); +} diff --git a/usr/src/cmd/sendmail/libsm/t-heap.c b/usr/src/cmd/sendmail/libsm/t-heap.c new file mode 100644 index 0000000000..290b07e17f --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/t-heap.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_IDSTR(id, "@(#)$Id: t-heap.c,v 1.8 2001/03/06 17:27:36 ca Exp $") + +#include <sm/debug.h> +#include <sm/heap.h> +#include <sm/io.h> +#include <sm/test.h> +#include <sm/xtrap.h> + +#if SM_HEAP_CHECK +extern SM_DEBUG_T SmHeapCheck; +# define HEAP_CHECK sm_debug_active(&SmHeapCheck, 1) +#else /* SM_HEAP_CHECK */ +# define HEAP_CHECK 0 +#endif /* SM_HEAP_CHECK */ + +int +main(argc, argv) + int argc; + char **argv; +{ + void *p; + + sm_test_begin(argc, argv, "test heap handling"); + if (argc > 1) + sm_debug_addsettings_x(argv[1]); + + p = sm_malloc(10); + SM_TEST(p != NULL); + p = sm_realloc_x(p, 20); + SM_TEST(p != NULL); + p = sm_realloc(p, 30); + SM_TEST(p != NULL); + if (HEAP_CHECK) + { + sm_dprintf("heap with 1 30-byte block allocated:\n"); + sm_heap_report(smioout, 3); + } + + if (HEAP_CHECK) + { + sm_free(p); + sm_dprintf("heap with 0 blocks allocated:\n"); + sm_heap_report(smioout, 3); + sm_dprintf("xtrap count = %d\n", SmXtrapCount); + } + +#if DEBUG + /* this will cause a core dump */ + sm_dprintf("about to free %p for the second time\n", p); + sm_free(p); +#endif /* DEBUG */ + + return sm_test_end(); +} diff --git a/usr/src/cmd/sendmail/libsm/t-match.c b/usr/src/cmd/sendmail/libsm/t-match.c new file mode 100644 index 0000000000..01012fa188 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/t-match.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2000 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_IDSTR(id, "@(#)$Id: t-match.c,v 1.7 2000/12/18 18:12:12 ca Exp $") + +#include <sm/string.h> +#include <sm/io.h> +#include <sm/test.h> + +#define try(str, pat, want) \ + got = sm_match(str, pat); \ + if (!SM_TEST(got == want)) \ + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, \ + "sm_match(\"%s\", \"%s\") returns %s\n", \ + str, pat, got ? "true" : "false"); + +int +main(argc, argv) + int argc; + char **argv; +{ + bool got; + + sm_test_begin(argc, argv, "test sm_match"); + + try("foo", "foo", true); + try("foo", "bar", false); + try("foo[bar", "foo[bar", true); + try("foo[bar]", "foo[bar]", false); + try("foob", "foo[bar]", true); + try("a-b", "a[]-]b", true); + try("abcde", "a*e", true); + try("[", "[[]", true); + try("c", "[a-z]", true); + try("C", "[a-z]", false); + try("F:sm.heap", "[!F]*", false); + try("E:sm.err", "[!F]*", true); + + return sm_test_end(); +} diff --git a/usr/src/cmd/sendmail/libsm/t-path.c b/usr/src/cmd/sendmail/libsm/t-path.c new file mode 100644 index 0000000000..066d2bbaa4 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/t-path.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_IDSTR(id, "@(#)$Id: t-path.c,v 1.6 2001/07/05 22:47:29 gshapiro Exp $") + +#include <string.h> +#include <sm/path.h> +#include <sm/test.h> + +int +main(argc, argv) + int argc; + char **argv; +{ + char *r; + + sm_test_begin(argc, argv, "test path handling"); + + SM_TEST(sm_path_isdevnull(SM_PATH_DEVNULL)); + r = "/dev/null"; + SM_TEST(sm_path_isdevnull(r)); + r = "/nev/dull"; + SM_TEST(!sm_path_isdevnull(r)); + r = "nul"; + SM_TEST(!sm_path_isdevnull(r)); + + return sm_test_end(); +} diff --git a/usr/src/cmd/sendmail/libsm/t-rpool.c b/usr/src/cmd/sendmail/libsm/t-rpool.c new file mode 100644 index 0000000000..98de40662e --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/t-rpool.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_IDSTR(id, "@(#)$Id: t-rpool.c,v 1.16 2001/03/04 18:38:47 ca Exp $") + +#include <sm/debug.h> +#include <sm/heap.h> +#include <sm/rpool.h> +#include <sm/io.h> +#include <sm/test.h> + +static void +rfree __P(( + void *cx)); + +static void +rfree(cx) + void *cx; +{ + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "rfree freeing `%s'\n", + (char *) cx); +} + +int +main(argc, argv) + int argc; + char *argv[]; +{ + SM_RPOOL_T *rpool; + char *a[26]; + int i, j; + SM_RPOOL_ATTACH_T att; + + sm_test_begin(argc, argv, "test rpool"); + sm_debug_addsetting_x("sm_check_heap", 1); + rpool = sm_rpool_new_x(NULL); + SM_TEST(rpool != NULL); + att = sm_rpool_attach_x(rpool, rfree, "attachment #1"); + SM_TEST(att != NULL); + for (i = 0; i < 26; ++i) + { + size_t sz = i * i * i; + + a[i] = sm_rpool_malloc_x(rpool, sz); + for (j = 0; j < sz; ++j) + a[i][j] = 'a' + i; + } + att = sm_rpool_attach_x(rpool, rfree, "attachment #2"); + (void) sm_rpool_attach_x(rpool, rfree, "attachment #3"); + sm_rpool_detach(att); + + /* XXX more tests? */ +#if DEBUG + sm_dprintf("heap after filling up rpool:\n"); + sm_heap_report(smioout, 3); + sm_dprintf("freeing rpool:\n"); + sm_rpool_free(rpool); + sm_dprintf("heap after freeing rpool:\n"); + sm_heap_report(smioout, 3); +#endif /* DEBUG */ + return sm_test_end(); +} diff --git a/usr/src/cmd/sendmail/libsm/t-scanf.c b/usr/src/cmd/sendmail/libsm/t-scanf.c new file mode 100644 index 0000000000..3dac7d4a8e --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/t-scanf.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_IDSTR(id, "@(#)$Id: t-scanf.c,v 1.5 2001/11/13 00:51:28 ca Exp $") + +#include <sm/limits.h> +#include <sm/io.h> +#include <sm/string.h> +#include <sm/test.h> +#include <sm/types.h> + +int +main(argc, argv) + int argc; + char **argv; +{ + int i, d, h; + char buf[128]; + char *r; + + sm_test_begin(argc, argv, "test scanf point stuff"); +#if !SM_CONF_BROKEN_SIZE_T + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, +"If tests for \"h == 2\" fail, check whether size_t is signed on your OS.\n\ +If that is the case, add -DSM_CONF_BROKEN_SIZE_T to confENVDEF\n\ +and start over. Otherwise contact sendmail.org.\n"); +#endif /* !SM_CONF_BROKEN_SIZE_T */ + + d = 2; + sm_snprintf(buf, sizeof(buf), "%d", d); + r = "2"; + if (!SM_TEST(strcmp(buf, r) == 0)) + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "got %s instead\n", buf); + + i = sm_io_sscanf(buf, "%d", &h); + SM_TEST(i == 1); + SM_TEST(h == 2); + + d = 2; + sm_snprintf(buf, sizeof(buf), "%d\n", d); + r = "2\n"; + if (!SM_TEST(strcmp(buf, r) == 0)) + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "got %s instead\n", buf); + + i = sm_io_sscanf(buf, "%d", &h); + SM_TEST(i == 1); + SM_TEST(h == 2); + + return sm_test_end(); +} diff --git a/usr/src/cmd/sendmail/libsm/t-sem.c b/usr/src/cmd/sendmail/libsm/t-sem.c new file mode 100644 index 0000000000..c8ee8a48a1 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/t-sem.c @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: t-sem.c,v 1.14 2005/03/25 21:27:41 ca Exp $") + +#include <stdio.h> + +#if SM_CONF_SEM +# include <stdlib.h> +# include <unistd.h> +# include <sysexits.h> +# include <sm/heap.h> +# include <sm/string.h> +# include <sm/signal.h> +# include <sm/test.h> +# include <sm/sem.h> + +static void +delay(t, s) + int t; + char *s; +{ + if (t > 0) + { +#if DEBUG + fprintf(stderr, "sleep(%d) before %s\n", t, s); +#endif /* DEBUG */ + sleep(t); + } +#if DEBUG + fprintf(stderr, "%s\n", s); +#endif /* DEBUG */ +} + + +/* +** SEMINTER -- interactive testing of semaphores. +** +** Parameters: +** owner -- create semaphores. +** +** Returns: +** 0 on success +** < 0 on failure. +*/ + +static int +seminter(owner) + bool owner; +{ + int semid; + int t; + + semid = sm_sem_start(SM_SEM_KEY, SM_NSEM, 0, owner); + if (semid < 0) + { + perror("sm_sem_start failed"); + return 1; + } + + while ((t = getchar()) != EOF) + { + switch (t) + { + case 'a': + delay(0, "try to acq"); + if (sm_sem_acq(semid, 0, 2) < 0) + { + perror("sm_sem_acq failed"); + return 1; + } + delay(0, "acquired"); + break; + + case 'r': + delay(0, "try to rel"); + if (sm_sem_rel(semid, 0, 2) < 0) + { + perror("sm_sem_rel failed"); + return 1; + } + delay(0, "released"); + break; + + case 'v': + if ((t = sm_sem_get(semid, 0)) < 0) + { + perror("get_sem failed"); + return 1; + } + printf("semval: %d\n", t); + break; + + } + } + if (owner) + return sm_sem_stop(semid); + return 0; +} + +/* +** SEM_CLEANUP -- cleanup if something breaks +** +** Parameters: +** sig -- signal. +** +** Returns: +** none. +*/ + +static int semid_c = -1; +void +sem_cleanup(sig) + int sig; +{ + if (semid_c >= 0) + (void) sm_sem_stop(semid_c); + exit(EX_UNAVAILABLE); +} + +/* +** SEMTEST -- test of semaphores +** +** Parameters: +** owner -- create semaphores. +** +** Returns: +** 0 on success +** < 0 on failure. +*/ + +# define MAX_CNT 10 + +static int +semtest(owner) + int owner; +{ + int semid, r; + int cnt = 0; + + semid = sm_sem_start(SM_SEM_KEY, 1, 0, owner); + if (semid < 0) + { + perror("sm_sem_start failed"); + return -1; + } + + if (owner) + { + /* just in case someone kills the program... */ + semid_c = semid; + (void) sm_signal(SIGHUP, sem_cleanup); + (void) sm_signal(SIGINT, sem_cleanup); + (void) sm_signal(SIGTERM, sem_cleanup); + + delay(1, "parent: acquire 1"); + cnt = 0; + do + { + r = sm_sem_acq(semid, 0, 0); + if (r < 0) + { + sleep(1); + ++cnt; + } + } while (r < 0 && cnt <= MAX_CNT); + SM_TEST(r >= 0); + if (r < 0) + return r; + + delay(3, "parent: release 1"); + cnt = 0; + do + { + r = sm_sem_rel(semid, 0, 0); + if (r < 0) + { + sleep(1); + ++cnt; + } + } while (r < 0 && cnt <= MAX_CNT); + SM_TEST(r >= 0); + if (r < 0) + return r; + + delay(1, "parent: getval"); + cnt = 0; + do + { + r = sm_sem_get(semid, 0); + if (r <= 0) + { + sleep(1); + ++cnt; + } + } while (r <= 0 && cnt <= MAX_CNT); + SM_TEST(r > 0); + if (r <= 0) + return r; + + delay(1, "parent: acquire 2"); + cnt = 0; + do + { + r = sm_sem_acq(semid, 0, 0); + if (r < 0) + { + sleep(1); + ++cnt; + } + } while (r < 0 && cnt <= MAX_CNT); + SM_TEST(r >= 0); + if (r < 0) + return r; + + cnt = 0; + do + { + r = sm_sem_rel(semid, 0, 0); + if (r < 0) + { + sleep(1); + ++cnt; + } + } while (r < 0 && cnt <= MAX_CNT); + SM_TEST(r >= 0); + if (r < 0) + return r; + } + else + { + delay(1, "child: acquire 1"); + cnt = 0; + do + { + r = sm_sem_acq(semid, 0, 0); + if (r < 0) + { + sleep(1); + ++cnt; + } + } while (r < 0 && cnt <= MAX_CNT); + SM_TEST(r >= 0); + if (r < 0) + return r; + + delay(1, "child: release 1"); + cnt = 0; + do + { + r = sm_sem_rel(semid, 0, 0); + if (r < 0) + { + sleep(1); + ++cnt; + } + } while (r < 0 && cnt <= MAX_CNT); + SM_TEST(r >= 0); + if (r < 0) + return r; + + } + if (owner) + return sm_sem_stop(semid); + return 0; +} + +int +main(argc, argv) + int argc; + char *argv[]; +{ + bool interactive = false; + bool owner = false; + int ch; + int r = 0; + +# define OPTIONS "io" + while ((ch = getopt(argc, argv, OPTIONS)) != -1) + { + switch ((char) ch) + { + case 'i': + interactive = true; + break; + + case 'o': + owner = true; + break; + + default: + break; + } + } + + if (interactive) + r = seminter(owner); + else + { + pid_t pid; + + printf("This test takes about 8 seconds.\n"); + printf("If it takes longer than 30 second, please interrupt it\n"); + printf("and compile again without semaphore support, i.e.,"); + printf("-DSM_CONF_SEM=0\n"); + if ((pid = fork()) < 0) + { + perror("fork failed\n"); + return -1; + } + + sm_test_begin(argc, argv, "test semaphores"); + if (pid == 0) + { + /* give the parent the chance to setup data */ + sleep(1); + r = semtest(false); + } + else + { + r = semtest(true); + } + SM_TEST(r == 0); + return sm_test_end(); + } + return r; +} +#else /* SM_CONF_SEM */ +int +main(argc, argv) + int argc; + char *argv[]; +{ + printf("No support for semaphores configured on this machine\n"); + return 0; +} +#endif /* SM_CONF_SEM */ diff --git a/usr/src/cmd/sendmail/libsm/t-shm.c b/usr/src/cmd/sendmail/libsm/t-shm.c new file mode 100644 index 0000000000..e44addd596 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/t-shm.c @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2000-2002, 2004, 2005 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: t-shm.c,v 1.22 2005/01/14 02:14:10 ca Exp $") + +#include <stdio.h> + +#if SM_CONF_SHM +# include <stdlib.h> +# include <unistd.h> +# include <sys/wait.h> + +# include <sm/heap.h> +# include <sm/string.h> +# include <sm/test.h> +# include <sm/shm.h> + +# define SHMSIZE 1024 +# define SHM_MAX 6400000 +# define T_SHMKEY 21 + + +/* +** SHMINTER -- interactive testing of shared memory +** +** Parameters: +** owner -- create segment. +** +** Returns: +** 0 on success +** < 0 on failure. +*/ + +int shminter __P((bool)); + +int +shminter(owner) + bool owner; +{ + int *shm, shmid; + int i, t; + + shm = (int *) sm_shmstart(T_SHMKEY, SHMSIZE, 0, &shmid, owner); + if (shm == (int *) 0) + { + perror("shminit failed"); + return -1; + } + + while ((t = getchar()) != EOF) + { + switch (t) + { + case 'c': + *shm = 0; + break; + case 'i': + ++*shm; + break; + case 'd': + --*shm; + break; + case 's': + sleep(1); + break; + case 'l': + t = *shm; + for (i = 0; i < SHM_MAX; i++) + { + ++*shm; + } + if (*shm != SHM_MAX + t) + fprintf(stderr, "error: %d != %d\n", + *shm, SHM_MAX + t); + break; + case 'v': + printf("shmval: %d\n", *shm); + break; + case 'S': + i = sm_shmsetowner(shmid, getuid(), getgid(), 0644); + printf("sm_shmsetowner=%d\n", i); + break; + } + } + return sm_shmstop((void *) shm, shmid, owner); +} + + +/* +** SHMBIG -- testing of shared memory +** +** Parameters: +** owner -- create segment. +** size -- size of segment. +** +** Returns: +** 0 on success +** < 0 on failure. +*/ + +int shmbig __P((bool, int)); + +int +shmbig(owner, size) + bool owner; + int size; +{ + int *shm, shmid; + int i; + + shm = (int *) sm_shmstart(T_SHMKEY, size, 0, &shmid, owner); + if (shm == (int *) 0) + { + perror("shminit failed"); + return -1; + } + + for (i = 0; i < size / sizeof(int); i++) + shm[i] = i; + for (i = 0; i < size / sizeof(int); i++) + { + if (shm[i] != i) + { + fprintf(stderr, "failed at %d: %d", i, shm[i]); + } + } + + return sm_shmstop((void *) shm, shmid, owner); +} + + +/* +** SHMTEST -- test of shared memory +** +** Parameters: +** owner -- create segment. +** +** Returns: +** 0 on success +** < 0 on failure. +*/ + +# define MAX_CNT 10 + +int shmtest __P((int)); + +int +shmtest(owner) + int owner; +{ + int *shm, shmid; + int cnt = 0; + + shm = (int *) sm_shmstart(T_SHMKEY, SHMSIZE, 0, &shmid, owner); + if (shm == (int *) 0) + { + perror("shminit failed"); + return -1; + } + + if (owner) + { + int r; + + r = sm_shmsetowner(shmid, getuid(), getgid(), 0660); + SM_TEST(r == 0); + *shm = 1; + while (*shm == 1 && cnt++ < MAX_CNT) + sleep(1); + SM_TEST(cnt <= MAX_CNT); + + /* release and re-acquire the segment */ + r = sm_shmstop((void *) shm, shmid, owner); + SM_TEST(r == 0); + shm = (int *) sm_shmstart(T_SHMKEY, SHMSIZE, 0, &shmid, owner); + SM_TEST(shm != (int *) 0); + } + else + { + while (*shm != 1 && cnt++ < MAX_CNT) + sleep(1); + SM_TEST(cnt <= MAX_CNT); + *shm = 2; + + /* wait a momemt so the segment is still in use */ + sleep(2); + } + return sm_shmstop((void *) shm, shmid, owner); +} + +int +main(argc, argv) + int argc; + char *argv[]; +{ + bool interactive = false; + bool owner = false; + int big = -1; + int ch; + int r = 0; + int status; + extern char *optarg; + +# define OPTIONS "b:io" + while ((ch = getopt(argc, argv, OPTIONS)) != -1) + { + switch ((char) ch) + { + case 'b': + big = atoi(optarg); + break; + + case 'i': + interactive = true; + break; + + case 'o': + owner = true; + break; + + default: + break; + } + } + + if (interactive) + r = shminter(owner); + else if (big > 0) + r = shmbig(true, big); + else + { + pid_t pid; + extern int SmTestNumErrors; + + if ((pid = fork()) < 0) + { + perror("fork failed\n"); + return -1; + } + + sm_test_begin(argc, argv, "test shared memory"); + if (pid == 0) + { + /* give the parent the chance to setup data */ + sleep(1); + r = shmtest(false); + } + else + { + r = shmtest(true); + (void) wait(&status); + } + SM_TEST(r == 0); + if (SmTestNumErrors > 0) + printf("add -DSM_CONF_SHM=0 to confENVDEF in devtools/Site/site.config.m4\nand start over.\n"); + return sm_test_end(); + } + return r; +} +#else /* SM_CONF_SHM */ +int +main(argc, argv) + int argc; + char *argv[]; +{ + printf("No support for shared memory configured on this machine\n"); + return 0; +} +#endif /* SM_CONF_SHM */ diff --git a/usr/src/cmd/sendmail/libsm/t-smstdio.c b/usr/src/cmd/sendmail/libsm/t-smstdio.c new file mode 100644 index 0000000000..6a555cdf57 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/t-smstdio.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_IDSTR(id, "@(#)$Id: t-smstdio.c,v 1.9 2001/03/21 18:30:41 ca Exp $") + +#include <sm/io.h> +#include <sm/string.h> +#include <sm/test.h> + +int +main(argc, argv) + int argc; + char **argv; +{ + FILE *stream; + SM_FILE_T *fp; + char buf[128]; + size_t n; + static char testmsg[] = "hello, world\n"; + + sm_test_begin(argc, argv, + "test sm_io_stdioopen, smiostdin, smiostdout"); + + stream = fopen("t-smstdio.1", "w"); + SM_TEST(stream != NULL); + + fp = sm_io_stdioopen(stream, "w"); + SM_TEST(fp != NULL); + + (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s", testmsg); + sm_io_close(fp, SM_TIME_DEFAULT); + +#if 0 + /* + ** stream should now be closed. This is a tricky way to test + ** if it is still open. Alas, it core dumps on Linux. + */ + + fprintf(stream, "oops! stream is still open!\n"); + fclose(stream); +#endif + + stream = fopen("t-smstdio.1", "r"); + SM_TEST(stream != NULL); + + fp = sm_io_stdioopen(stream, "r"); + SM_TEST(fp != NULL); + + n = sm_io_read(fp, SM_TIME_DEFAULT, buf, sizeof(buf)); + if (SM_TEST(n == strlen(testmsg))) + { + buf[n] = '\0'; + SM_TEST(strcmp(buf, testmsg) == 0); + } + +#if 0 + + /* + ** Copy smiostdin to smiostdout + ** gotta think some more about how to test smiostdin and smiostdout + */ + + while ((c = sm_io_getc(smiostdin)) != SM_IO_EOF) + sm_io_putc(smiostdout, c); +#endif + + return sm_test_end(); +} diff --git a/usr/src/cmd/sendmail/libsm/t-string.c b/usr/src/cmd/sendmail/libsm/t-string.c new file mode 100644 index 0000000000..30cef8d236 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/t-string.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_IDSTR(id, "@(#)$Id: t-string.c,v 1.9 2001/01/26 03:28:43 ca Exp $") + +#include <sm/exc.h> +#include <sm/io.h> +#include <sm/string.h> +#include <sm/test.h> + +int +main(argc, argv) + int argc; + char **argv; +{ + char *s; + char buf[4096]; + char foo[4]; + char *r; + int n; + + sm_test_begin(argc, argv, "test string utilities"); + + s = sm_stringf_x("%.3s%03d", "foobar", 42); + r = "foo042"; + SM_TEST(strcmp(s, r) == 0); + + s = sm_stringf_x("+%*x+", 2000, 0xCAFE); + sm_snprintf(buf, 4096, "+%*x+", 2000, 0xCAFE); + SM_TEST(strcmp(s, buf) == 0); + + foo[3] = 1; + n = sm_snprintf(foo, sizeof(foo), "foobar%dbaz", 42); + SM_TEST(n == 11); + r = "foo"; + SM_TEST(strcmp(foo, r) == 0); + + return sm_test_end(); +} diff --git a/usr/src/cmd/sendmail/libsm/t-strio.c b/usr/src/cmd/sendmail/libsm/t-strio.c new file mode 100644 index 0000000000..841de427c5 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/t-strio.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_IDSTR(id, "@(#)$Id: t-strio.c,v 1.9 2001/03/03 04:00:53 ca Exp $") +#include <sm/string.h> +#include <sm/io.h> +#include <sm/test.h> + +int +main(argc, argv) + int argc; + char *argv[]; +{ + char buf[20]; + char *r; + SM_FILE_T f; + + sm_test_begin(argc, argv, "test strio"); + (void) memset(buf, '.', 20); + sm_strio_init(&f, buf, 10); + (void) sm_io_fprintf(&f, SM_TIME_DEFAULT, "foobarbazoom"); + sm_io_flush(&f, SM_TIME_DEFAULT); + r = "foobarbaz"; + SM_TEST(strcmp(buf, r) == 0); + return sm_test_end(); +} diff --git a/usr/src/cmd/sendmail/libsm/t-strl.c b/usr/src/cmd/sendmail/libsm/t-strl.c new file mode 100644 index 0000000000..dbafbb9bcd --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/t-strl.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_IDSTR(id, "@(#)$Id: t-strl.c,v 1.13 2001/08/27 23:00:05 gshapiro Exp $") + +#include <stdlib.h> +#include <stdio.h> +#include <sm/heap.h> +#include <sm/string.h> +#include <sm/test.h> + +#define MAXL 16 +#define N 5 +#define SIZE 128 + +int +main(argc, argv) + int argc; + char *argv[]; +{ + char *s1, *s2, *s3; + int one, two, k; + char src1[N][SIZE], dst1[SIZE], dst2[SIZE]; + char *r; + + sm_test_begin(argc, argv, "test strl* string functions"); + s1 = "abc"; + s2 = "123"; + s3 = sm_malloc_x(MAXL); + + SM_TEST(sm_strlcpy(s3, s1, 4) == 3); + SM_TEST(strcmp(s1, s3) == 0); + + SM_TEST(sm_strlcat(s3, s2, 8) == 6); + r ="abc123"; + SM_TEST(strcmp(s3, r) == 0); + + SM_TEST(sm_strlcpy(s3, s1, 2) == 3); + r = "a"; + SM_TEST(strcmp(s3, r) == 0); + + SM_TEST(sm_strlcat(s3, s2, 3) == 4); + r = "a1"; + SM_TEST(strcmp(s3, r) == 0); + + SM_TEST(sm_strlcpy(s3, s1, 4) == 3); + r = ":"; + SM_TEST(sm_strlcat2(s3, r, s2, MAXL) == 7); + r = "abc:123"; + SM_TEST(strcmp(s3, r) == 0); + + SM_TEST(sm_strlcpy(s3, s1, 4) == 3); + r = ":"; + SM_TEST(sm_strlcat2(s3, r, s2, 6) == 7); + r = "abc:1"; + SM_TEST(strcmp(s3, r) == 0); + + SM_TEST(sm_strlcpy(s3, s1, 4) == 3); + r = ":"; + SM_TEST(sm_strlcat2(s3, r, s2, 2) == 7); + r = "abc"; + SM_TEST(strcmp(s3, r) == 0); + + SM_TEST(sm_strlcpy(s3, s1, 4) == 3); + r = ":"; + SM_TEST(sm_strlcat2(s3, r, s2, 4) == 7); + r = "abc"; + SM_TEST(strcmp(s3, r) == 0); + + SM_TEST(sm_strlcpy(s3, s1, 4) == 3); + r = ":"; + SM_TEST(sm_strlcat2(s3, r, s2, 5) == 7); + r = "abc:"; + SM_TEST(strcmp(s3, r) == 0); + + SM_TEST(sm_strlcpy(s3, s1, 4) == 3); + r = ":"; + SM_TEST(sm_strlcat2(s3, r, s2, 6) == 7); + r = "abc:1"; + SM_TEST(strcmp(s3, r) == 0); + + for (k = 0; k < N; k++) + { + (void) sm_strlcpy(src1[k], "abcdef", sizeof src1); + } + + one = sm_strlcpyn(dst1, sizeof dst1, 3, src1[0], "/", src1[1]); + two = sm_snprintf(dst2, sizeof dst2, "%s/%s", src1[0], src1[1]); + SM_TEST(one == two); + SM_TEST(strcmp(dst1, dst2) == 0); + one = sm_strlcpyn(dst1, 10, 3, src1[0], "/", src1[1]); + two = sm_snprintf(dst2, 10, "%s/%s", src1[0], src1[1]); + SM_TEST(one == two); + SM_TEST(strcmp(dst1, dst2) == 0); + one = sm_strlcpyn(dst1, 5, 3, src1[0], "/", src1[1]); + two = sm_snprintf(dst2, 5, "%s/%s", src1[0], src1[1]); + SM_TEST(one == two); + SM_TEST(strcmp(dst1, dst2) == 0); + one = sm_strlcpyn(dst1, 0, 3, src1[0], "/", src1[1]); + two = sm_snprintf(dst2, 0, "%s/%s", src1[0], src1[1]); + SM_TEST(one == two); + SM_TEST(strcmp(dst1, dst2) == 0); + one = sm_strlcpyn(dst1, sizeof dst1, 5, src1[0], "/", src1[1], "/", src1[2]); + two = sm_snprintf(dst2, sizeof dst2, "%s/%s/%s", src1[0], src1[1], src1[2]); + SM_TEST(one == two); + SM_TEST(strcmp(dst1, dst2) == 0); + one = sm_strlcpyn(dst1, 15, 5, src1[0], "/", src1[1], "/", src1[2]); + two = sm_snprintf(dst2, 15, "%s/%s/%s", src1[0], src1[1], src1[2]); + SM_TEST(one == two); + SM_TEST(strcmp(dst1, dst2) == 0); + one = sm_strlcpyn(dst1, 20, 5, src1[0], "/", src1[1], "/", src1[2]); + two = sm_snprintf(dst2, 20, "%s/%s/%s", src1[0], src1[1], src1[2]); + SM_TEST(one == two); + SM_TEST(strcmp(dst1, dst2) == 0); + + one = sm_strlcpyn(dst1, sizeof dst1, 0); + SM_TEST(one == 0); + r = ""; + SM_TEST(strcmp(dst1, r) == 0); + one = sm_strlcpyn(dst1, 20, 1, src1[0]); + two = sm_snprintf(dst2, 20, "%s", src1[0]); + SM_TEST(one == two); + one = sm_strlcpyn(dst1, 2, 1, src1[0]); + two = sm_snprintf(dst2, 2, "%s", src1[0]); + SM_TEST(one == two); + + return sm_test_end(); +} diff --git a/usr/src/cmd/sendmail/libsm/t-strrevcmp.c b/usr/src/cmd/sendmail/libsm/t-strrevcmp.c new file mode 100644 index 0000000000..7ce06d5e0f --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/t-strrevcmp.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_IDSTR(id, "@(#)$Id: t-strrevcmp.c,v 1.1 2001/07/16 21:35:28 ca Exp $") + +#include <sm/exc.h> +#include <sm/io.h> +#include <sm/string.h> +#include <sm/test.h> + +int +main(argc, argv) + int argc; + char **argv; +{ + char *s1; + char *s2; + + sm_test_begin(argc, argv, "test string compare"); + + s1 = "equal"; + s2 = "equal"; + SM_TEST(sm_strrevcmp(s1, s2) == 0); + + s1 = "equal"; + s2 = "qual"; + SM_TEST(sm_strrevcmp(s1, s2) > 0); + + s1 = "qual"; + s2 = "equal"; + SM_TEST(sm_strrevcmp(s1, s2) < 0); + + s1 = "Equal"; + s2 = "equal"; + SM_TEST(sm_strrevcmp(s1, s2) < 0); + + s1 = "Equal"; + s2 = "equal"; + SM_TEST(sm_strrevcasecmp(s1, s2) == 0); + + s1 = "Equal"; + s2 = "eQuaL"; + SM_TEST(sm_strrevcasecmp(s1, s2) == 0); + + return sm_test_end(); +} diff --git a/usr/src/cmd/sendmail/libsm/test.c b/usr/src/cmd/sendmail/libsm/test.c new file mode 100644 index 0000000000..46b3b2bdd1 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/test.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2000-2002 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_IDSTR(Id, "@(#)$Id: test.c,v 1.16 2002/01/08 17:54:40 ca Exp $") + +/* +** Abstractions for writing libsm test programs. +*/ + +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <sm/debug.h> +#include <sm/test.h> + +extern char *optarg; +extern int optind; +extern int optopt; +extern int opterr; + +int SmTestIndex; +int SmTestNumErrors; +bool SmTestVerbose; + +static char Help[] = "\ +%s [-h] [-d debugging] [-v]\n\ +\n\ +%s\n\ +\n\ +-h Display this help information.\n\ +-d debugging Set debug activation levels.\n\ +-v Verbose output.\n\ +"; + +static char Usage[] = "\ +Usage: %s [-h] [-v]\n\ +Use %s -h for help.\n\ +"; + +/* +** SM_TEST_BEGIN -- initialize test system. +** +** Parameters: +** argc -- argument counter. +** argv -- argument vector. +** testname -- description of tests. +** +** Results: +** none. +*/ + +void +sm_test_begin(argc, argv, testname) + int argc; + char **argv; + char *testname; +{ + int c; + + SmTestIndex = 0; + SmTestNumErrors = 0; + SmTestVerbose = false; + opterr = 0; + + while ((c = getopt(argc, argv, "vhd:")) != -1) + { + switch (c) + { + case 'v': + SmTestVerbose = true; + break; + case 'd': + sm_debug_addsettings_x(optarg); + break; + case 'h': + (void) fprintf(stdout, Help, argv[0], testname); + exit(0); + default: + (void) fprintf(stderr, + "Unknown command line option -%c\n", + optopt); + (void) fprintf(stderr, Usage, argv[0], argv[0]); + exit(1); + } + } +} + +/* +** SM_TEST -- single test. +** +** Parameters: +** success -- did test succeeed? +** expr -- expression that has been evaluated. +** filename -- guess... +** lineno -- line number. +** +** Results: +** value of success. +*/ + +bool +sm_test(success, expr, filename, lineno) + bool success; + char *expr; + char *filename; + int lineno; +{ + ++SmTestIndex; + if (SmTestVerbose) + (void) fprintf(stderr, "%d..", SmTestIndex); + if (!success) + { + ++SmTestNumErrors; + if (!SmTestVerbose) + (void) fprintf(stderr, "%d..", SmTestIndex); + (void) fprintf(stderr, "bad! %s:%d %s\n", filename, lineno, + expr); + } + else + { + if (SmTestVerbose) + (void) fprintf(stderr, "ok\n"); + } + return success; +} + +/* +** SM_TEST_END -- end of test system. +** +** Parameters: +** none. +** +** Results: +** number of errors. +*/ + +int +sm_test_end() +{ + (void) fprintf(stderr, "%d of %d tests completed successfully\n", + SmTestIndex - SmTestNumErrors, SmTestIndex); + if (SmTestNumErrors != 0) + (void) fprintf(stderr, "*** %d error%s in test! ***\n", + SmTestNumErrors, + SmTestNumErrors > 1 ? "s" : ""); + + return SmTestNumErrors; +} diff --git a/usr/src/cmd/sendmail/libsm/ungetc.c b/usr/src/cmd/sendmail/libsm/ungetc.c new file mode 100644 index 0000000000..0b4b8c5dd9 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/ungetc.c @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2000-2001, 2004 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_IDSTR(id, "@(#)$Id: ungetc.c,v 1.29 2004/08/03 20:54:49 ca Exp $") + +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include <sys/time.h> +#include <errno.h> +#include <sm/io.h> +#include <sm/heap.h> +#include <sm/assert.h> +#include <sm/conf.h> +#include "local.h" + +static void sm_submore_x __P((SM_FILE_T *)); + +/* +** SM_SUBMORE_X -- expand ungetc buffer +** +** Expand the ungetc buffer `in place'. That is, adjust fp->f_p when +** the buffer moves, so that it points the same distance from the end, +** and move the bytes in the buffer around as necessary so that they +** are all at the end (stack-style). +** +** Parameters: +** fp -- the file pointer +** +** Results: +** none. +** +** Exceptions: +** F:sm_heap -- out of memory +*/ + +static void +sm_submore_x(fp) + SM_FILE_T *fp; +{ + register int i; + register unsigned char *p; + + if (fp->f_ub.smb_base == fp->f_ubuf) + { + /* Get a buffer; f_ubuf is fixed size. */ + p = sm_malloc_x((size_t) SM_IO_BUFSIZ); + fp->f_ub.smb_base = p; + fp->f_ub.smb_size = SM_IO_BUFSIZ; + p += SM_IO_BUFSIZ - sizeof(fp->f_ubuf); + for (i = sizeof(fp->f_ubuf); --i >= 0;) + p[i] = fp->f_ubuf[i]; + fp->f_p = p; + return; + } + i = fp->f_ub.smb_size; + p = sm_realloc_x(fp->f_ub.smb_base, i << 1); + + /* no overlap (hence can use memcpy) because we doubled the size */ + (void) memcpy((void *) (p + i), (void *) p, (size_t) i); + fp->f_p = p + i; + fp->f_ub.smb_base = p; + fp->f_ub.smb_size = i << 1; +} + +/* +** SM_IO_UNGETC -- place a character back into the buffer just read +** +** Parameters: +** fp -- the file pointer affected +** timeout -- time to complete ungetc +** c -- the character to place back +** +** Results: +** On success, returns value of character placed back, 0-255. +** Returns SM_IO_EOF if c == SM_IO_EOF or if last operation +** was a write and flush failed. +** +** Exceptions: +** F:sm_heap -- out of memory +*/ + +int +sm_io_ungetc(fp, timeout, c) + register SM_FILE_T *fp; + int timeout; + int c; +{ + SM_REQUIRE_ISA(fp, SmFileMagic); + if (c == SM_IO_EOF) + return SM_IO_EOF; + if (timeout == SM_TIME_IMMEDIATE) + { + /* + ** Ungetting the buffer will take time and we are wanted to + ** return immediately. So... + */ + + errno = EAGAIN; + return SM_IO_EOF; + } + + if (!Sm_IO_DidInit) + sm_init(); + if ((fp->f_flags & SMRD) == 0) + { + /* + ** Not already reading: no good unless reading-and-writing. + ** Otherwise, flush any current write stuff. + */ + + if ((fp->f_flags & SMRW) == 0) + return SM_IO_EOF; + if (fp->f_flags & SMWR) + { + if (sm_flush(fp, &timeout)) + return SM_IO_EOF; + fp->f_flags &= ~SMWR; + fp->f_w = 0; + fp->f_lbfsize = 0; + } + fp->f_flags |= SMRD; + } + c = (unsigned char) c; + + /* + ** If we are in the middle of ungetc'ing, just continue. + ** This may require expanding the current ungetc buffer. + */ + + if (HASUB(fp)) + { + if (fp->f_r >= fp->f_ub.smb_size) + sm_submore_x(fp); + *--fp->f_p = c; + fp->f_r++; + return c; + } + fp->f_flags &= ~SMFEOF; + + /* + ** If we can handle this by simply backing up, do so, + ** but never replace the original character. + ** (This makes sscanf() work when scanning `const' data.) + */ + + if (fp->f_bf.smb_base != NULL && fp->f_p > fp->f_bf.smb_base && + fp->f_p[-1] == c) + { + fp->f_p--; + fp->f_r++; + return c; + } + + /* + ** Create an ungetc buffer. + ** Initially, we will use the `reserve' buffer. + */ + + fp->f_ur = fp->f_r; + fp->f_up = fp->f_p; + fp->f_ub.smb_base = fp->f_ubuf; + fp->f_ub.smb_size = sizeof(fp->f_ubuf); + fp->f_ubuf[sizeof(fp->f_ubuf) - 1] = c; + fp->f_p = &fp->f_ubuf[sizeof(fp->f_ubuf) - 1]; + fp->f_r = 1; + + return c; +} diff --git a/usr/src/cmd/sendmail/libsm/vasprintf.c b/usr/src/cmd/sendmail/libsm/vasprintf.c new file mode 100644 index 0000000000..d8a173ce13 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/vasprintf.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +/* + * Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.com> + * + * 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 TODD C. MILLER DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: vasprintf.c,v 1.26.2.1 2003/06/03 02:14:09 ca Exp $") +#include <stdlib.h> +#include <errno.h> +#include <sm/io.h> +#include <sm/heap.h> +#include "local.h" + +/* +** SM_VASPRINTF -- printf to a dynamically allocated string +** +** Write 'printf' output to a dynamically allocated string +** buffer which is returned to the caller. +** +** Parameters: +** str -- *str receives a pointer to the allocated string +** fmt -- format directives for printing +** ap -- variable argument list +** +** Results: +** On failure, set *str to NULL, set errno, and return -1. +** +** On success, set *str to a pointer to a nul-terminated +** string buffer containing printf output, and return the +** length of the string (not counting the nul). +*/ + +#define SM_VA_BUFSIZE 128 + +int +sm_vasprintf(str, fmt, ap) + char **str; + const char *fmt; + SM_VA_LOCAL_DECL +{ + int ret; + SM_FILE_T fake; + unsigned char *base; + + fake.sm_magic = SmFileMagic; + fake.f_timeout = SM_TIME_FOREVER; + fake.f_timeoutstate = SM_TIME_BLOCK; + fake.f_file = -1; + fake.f_flags = SMWR | SMSTR | SMALC; + fake.f_bf.smb_base = fake.f_p = (unsigned char *)sm_malloc(SM_VA_BUFSIZE); + if (fake.f_bf.smb_base == NULL) + goto err2; + fake.f_close = NULL; + fake.f_open = NULL; + fake.f_read = NULL; + fake.f_write = NULL; + fake.f_seek = NULL; + fake.f_setinfo = fake.f_getinfo = NULL; + fake.f_type = "sm_vasprintf:fake"; + fake.f_bf.smb_size = fake.f_w = SM_VA_BUFSIZE - 1; + fake.f_timeout = SM_TIME_FOREVER; + ret = sm_io_vfprintf(&fake, SM_TIME_FOREVER, fmt, ap); + if (ret == -1) + goto err; + *fake.f_p = '\0'; + + /* use no more space than necessary */ + base = (unsigned char *) sm_realloc(fake.f_bf.smb_base, ret + 1); + if (base == NULL) + goto err; + *str = (char *)base; + return ret; + +err: + if (fake.f_bf.smb_base != NULL) + { + sm_free(fake.f_bf.smb_base); + fake.f_bf.smb_base = NULL; + } +err2: + *str = NULL; + errno = ENOMEM; + return -1; +} diff --git a/usr/src/cmd/sendmail/libsm/vfprintf.c b/usr/src/cmd/sendmail/libsm/vfprintf.c new file mode 100644 index 0000000000..2ca7e9e58c --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/vfprintf.c @@ -0,0 +1,1110 @@ +/* + * Copyright (c) 2000-2001, 2004 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_IDSTR(id, "@(#)$Id: vfprintf.c,v 1.53 2004/08/03 20:54:49 ca Exp $") + +/* +** Overall: +** Actual printing innards. +** This code is large and complicated... +*/ + +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <sm/config.h> +#include <sm/varargs.h> +#include <sm/io.h> +#include <sm/heap.h> +#include <sm/conf.h> +#include "local.h" +#include "fvwrite.h" + +static int sm_bprintf __P((SM_FILE_T *, const char *, va_list)); +static void sm_find_arguments __P((const char *, va_list , va_list **)); +static void sm_grow_type_table_x __P((unsigned char **, int *)); +static int sm_print __P((SM_FILE_T *, int, struct sm_uio *)); + +/* +** SM_PRINT -- print/flush to the file +** +** Flush out all the vectors defined by the given uio, +** then reset it so that it can be reused. +** +** Parameters: +** fp -- file pointer +** timeout -- time to complete operation (milliseconds) +** uio -- vector list of memory locations of data for printing +** +** Results: +** Success: 0 (zero) +** Failure: +*/ + +static int +sm_print(fp, timeout, uio) + SM_FILE_T *fp; + int timeout; + register struct sm_uio *uio; +{ + register int err; + + if (uio->uio_resid == 0) + { + uio->uio_iovcnt = 0; + return 0; + } + err = sm_fvwrite(fp, timeout, uio); + uio->uio_resid = 0; + uio->uio_iovcnt = 0; + return err; +} + +/* +** SM_BPRINTF -- allow formating to an unbuffered file. +** +** Helper function for `fprintf to unbuffered unix file': creates a +** temporary buffer (via a "fake" file pointer). +** We only work on write-only files; this avoids +** worries about ungetc buffers and so forth. +** +** Parameters: +** fp -- the file to send the o/p to +** fmt -- format instructions for the o/p +** ap -- vectors of data units used for formating +** +** Results: +** Failure: SM_IO_EOF and errno set +** Success: number of data units used in the formating +** +** Side effects: +** formatted o/p can be SM_IO_BUFSIZ length maximum +*/ + +static int +sm_bprintf(fp, fmt, ap) + SM_FILE_T *fp; + const char *fmt; + SM_VA_LOCAL_DECL +{ + int ret; + SM_FILE_T fake; + unsigned char buf[SM_IO_BUFSIZ]; + extern const char SmFileMagic[]; + + /* copy the important variables */ + fake.sm_magic = SmFileMagic; + fake.f_timeout = SM_TIME_FOREVER; + fake.f_timeoutstate = SM_TIME_BLOCK; + fake.f_flags = fp->f_flags & ~SMNBF; + fake.f_file = fp->f_file; + fake.f_cookie = fp->f_cookie; + fake.f_write = fp->f_write; + fake.f_close = NULL; + fake.f_open = NULL; + fake.f_read = NULL; + fake.f_seek = NULL; + fake.f_setinfo = fake.f_getinfo = NULL; + fake.f_type = "sm_bprintf:fake"; + + /* set up the buffer */ + fake.f_bf.smb_base = fake.f_p = buf; + fake.f_bf.smb_size = fake.f_w = sizeof(buf); + fake.f_lbfsize = 0; /* not actually used, but Just In Case */ + + /* do the work, then copy any error status */ + ret = sm_io_vfprintf(&fake, SM_TIME_FOREVER, fmt, ap); + if (ret >= 0 && sm_io_flush(&fake, SM_TIME_FOREVER)) + ret = SM_IO_EOF; /* errno set by sm_io_flush */ + if (fake.f_flags & SMERR) + fp->f_flags |= SMERR; + return ret; +} + + +#define BUF 40 + +#define STATIC_ARG_TBL_SIZE 8 /* Size of static argument table. */ + + +/* Macros for converting digits to letters and vice versa */ +#define to_digit(c) ((c) - '0') +#define is_digit(c) ((unsigned) to_digit(c) <= 9) +#define to_char(n) ((char) (n) + '0') + +/* Flags used during conversion. */ +#define ALT 0x001 /* alternate form */ +#define HEXPREFIX 0x002 /* add 0x or 0X prefix */ +#define LADJUST 0x004 /* left adjustment */ +#define LONGINT 0x010 /* long integer */ +#define QUADINT 0x020 /* quad integer */ +#define SHORTINT 0x040 /* short integer */ +#define ZEROPAD 0x080 /* zero (as opposed to blank) pad */ +#define FPT 0x100 /* Floating point number */ + +/* +** SM_IO_VPRINTF -- performs actual formating for o/p +** +** Parameters: +** fp -- file pointer for o/p +** timeout -- time to complete the print +** fmt0 -- formating directives +** ap -- vectors with data units for formating +** +** Results: +** Success: number of data units used for formatting +** Failure: SM_IO_EOF and sets errno +*/ + +int +sm_io_vfprintf(fp, timeout, fmt0, ap) + SM_FILE_T *fp; + int timeout; + const char *fmt0; + SM_VA_LOCAL_DECL +{ + register char *fmt; /* format string */ + register int ch; /* character from fmt */ + register int n, m, n2; /* handy integers (short term usage) */ + register char *cp; /* handy char pointer (short term usage) */ + register struct sm_iov *iovp;/* for PRINT macro */ + register int flags; /* flags as above */ + int ret; /* return value accumulator */ + int width; /* width from format (%8d), or 0 */ + int prec; /* precision from format (%.3d), or -1 */ + char sign; /* sign prefix (' ', '+', '-', or \0) */ + wchar_t wc; + ULONGLONG_T _uquad; /* integer arguments %[diouxX] */ + enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */ + int dprec; /* a copy of prec if [diouxX], 0 otherwise */ + int realsz; /* field size expanded by dprec */ + int size; /* size of converted field or string */ + char *xdigs="0123456789abcdef"; /* digits for [xX] conversion */ +#define NIOV 8 + struct sm_uio uio; /* output information: summary */ + struct sm_iov iov[NIOV];/* ... and individual io vectors */ + char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ + char ox[2]; /* space for 0x hex-prefix */ + va_list *argtable; /* args, built due to positional arg */ + va_list statargtable[STATIC_ARG_TBL_SIZE]; + int nextarg; /* 1-based argument index */ + va_list orgap; /* original argument pointer */ + + /* + ** Choose PADSIZE to trade efficiency vs. size. If larger printf + ** fields occur frequently, increase PADSIZE and make the initialisers + ** below longer. + */ +#define PADSIZE 16 /* pad chunk size */ + static char blanks[PADSIZE] = + {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; + static char zeroes[PADSIZE] = + {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; + + /* + ** BEWARE, these `goto error' on error, and PAD uses `n'. + */ +#define PRINT(ptr, len) do { \ + iovp->iov_base = (ptr); \ + iovp->iov_len = (len); \ + uio.uio_resid += (len); \ + iovp++; \ + if (++uio.uio_iovcnt >= NIOV) \ + { \ + if (sm_print(fp, timeout, &uio)) \ + goto error; \ + iovp = iov; \ + } \ +} while (0) +#define PAD(howmany, with) do \ +{ \ + if ((n = (howmany)) > 0) \ + { \ + while (n > PADSIZE) { \ + PRINT(with, PADSIZE); \ + n -= PADSIZE; \ + } \ + PRINT(with, n); \ + } \ +} while (0) +#define FLUSH() do \ +{ \ + if (uio.uio_resid && sm_print(fp, timeout, &uio)) \ + goto error; \ + uio.uio_iovcnt = 0; \ + iovp = iov; \ +} while (0) + + /* + ** To extend shorts properly, we need both signed and unsigned + ** argument extraction methods. + */ +#define SARG() \ + (flags&QUADINT ? SM_VA_ARG(ap, LONGLONG_T) : \ + flags&LONGINT ? GETARG(long) : \ + flags&SHORTINT ? (long) (short) GETARG(int) : \ + (long) GETARG(int)) +#define UARG() \ + (flags&QUADINT ? SM_VA_ARG(ap, ULONGLONG_T) : \ + flags&LONGINT ? GETARG(unsigned long) : \ + flags&SHORTINT ? (unsigned long) (unsigned short) GETARG(int) : \ + (unsigned long) GETARG(unsigned int)) + + /* + ** Get * arguments, including the form *nn$. Preserve the nextarg + ** that the argument can be gotten once the type is determined. + */ +#define GETASTER(val) \ + n2 = 0; \ + cp = fmt; \ + while (is_digit(*cp)) \ + { \ + n2 = 10 * n2 + to_digit(*cp); \ + cp++; \ + } \ + if (*cp == '$') \ + { \ + int hold = nextarg; \ + if (argtable == NULL) \ + { \ + argtable = statargtable; \ + sm_find_arguments(fmt0, orgap, &argtable); \ + } \ + nextarg = n2; \ + val = GETARG(int); \ + nextarg = hold; \ + fmt = ++cp; \ + } \ + else \ + { \ + val = GETARG(int); \ + } + +/* +** Get the argument indexed by nextarg. If the argument table is +** built, use it to get the argument. If its not, get the next +** argument (and arguments must be gotten sequentially). +*/ + +#if SM_VA_STD +# define GETARG(type) \ + (((argtable != NULL) ? (void) (ap = argtable[nextarg]) : (void) 0), \ + nextarg++, SM_VA_ARG(ap, type)) +#else /* SM_VA_STD */ +# define GETARG(type) \ + ((argtable != NULL) ? (*((type*)(argtable[nextarg++]))) : \ + (nextarg++, SM_VA_ARG(ap, type))) +#endif /* SM_VA_STD */ + + /* sorry, fprintf(read_only_file, "") returns SM_IO_EOF, not 0 */ + if (cantwrite(fp)) + { + errno = EBADF; + return SM_IO_EOF; + } + + /* optimise fprintf(stderr) (and other unbuffered Unix files) */ + if ((fp->f_flags & (SMNBF|SMWR|SMRW)) == (SMNBF|SMWR) && + fp->f_file >= 0) + return sm_bprintf(fp, fmt0, ap); + + fmt = (char *) fmt0; + argtable = NULL; + nextarg = 1; + SM_VA_COPY(orgap, ap); + uio.uio_iov = iovp = iov; + uio.uio_resid = 0; + uio.uio_iovcnt = 0; + ret = 0; + + /* Scan the format for conversions (`%' character). */ + for (;;) + { + cp = fmt; + n = 0; + while ((wc = *fmt) != '\0') + { + if (wc == '%') + { + n = 1; + break; + } + fmt++; + } + if ((m = fmt - cp) != 0) + { + PRINT(cp, m); + ret += m; + } + if (n <= 0) + goto done; + fmt++; /* skip over '%' */ + + flags = 0; + dprec = 0; + width = 0; + prec = -1; + sign = '\0'; + +rflag: ch = *fmt++; +reswitch: switch (ch) + { + case ' ': + + /* + ** ``If the space and + flags both appear, the space + ** flag will be ignored.'' + ** -- ANSI X3J11 + */ + + if (!sign) + sign = ' '; + goto rflag; + case '#': + flags |= ALT; + goto rflag; + case '*': + + /* + ** ``A negative field width argument is taken as a + ** - flag followed by a positive field width.'' + ** -- ANSI X3J11 + ** They don't exclude field widths read from args. + */ + + GETASTER(width); + if (width >= 0) + goto rflag; + width = -width; + /* FALLTHROUGH */ + case '-': + flags |= LADJUST; + goto rflag; + case '+': + sign = '+'; + goto rflag; + case '.': + if ((ch = *fmt++) == '*') + { + GETASTER(n); + prec = n < 0 ? -1 : n; + goto rflag; + } + n = 0; + while (is_digit(ch)) + { + n = 10 * n + to_digit(ch); + ch = *fmt++; + } + if (ch == '$') + { + nextarg = n; + if (argtable == NULL) + { + argtable = statargtable; + sm_find_arguments(fmt0, orgap, + &argtable); + } + goto rflag; + } + prec = n < 0 ? -1 : n; + goto reswitch; + case '0': + + /* + ** ``Note that 0 is taken as a flag, not as the + ** beginning of a field width.'' + ** -- ANSI X3J11 + */ + + flags |= ZEROPAD; + goto rflag; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + n = 0; + do + { + n = 10 * n + to_digit(ch); + ch = *fmt++; + } while (is_digit(ch)); + if (ch == '$') + { + nextarg = n; + if (argtable == NULL) + { + argtable = statargtable; + sm_find_arguments(fmt0, orgap, + &argtable); + } + goto rflag; + } + width = n; + goto reswitch; + case 'h': + flags |= SHORTINT; + goto rflag; + case 'l': + if (*fmt == 'l') + { + fmt++; + flags |= QUADINT; + } + else + { + flags |= LONGINT; + } + goto rflag; + case 'q': + flags |= QUADINT; + goto rflag; + case 'c': + *(cp = buf) = GETARG(int); + size = 1; + sign = '\0'; + break; + case 'D': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'd': + case 'i': + _uquad = SARG(); + if ((LONGLONG_T) _uquad < 0) + { + _uquad = -(LONGLONG_T) _uquad; + sign = '-'; + } + base = DEC; + goto number; + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + { + double val; + char *p; + char fmt[16]; + char out[150]; + size_t len; + + /* + ** This code implements floating point output + ** in the most portable manner possible, + ** relying only on 'sprintf' as defined by + ** the 1989 ANSI C standard. + ** We silently cap width and precision + ** at 120, to avoid buffer overflow. + */ + + val = GETARG(double); + + p = fmt; + *p++ = '%'; + if (sign) + *p++ = sign; + if (flags & ALT) + *p++ = '#'; + if (flags & LADJUST) + *p++ = '-'; + if (flags & ZEROPAD) + *p++ = '0'; + *p++ = '*'; + if (prec >= 0) + { + *p++ = '.'; + *p++ = '*'; + } + *p++ = ch; + *p = '\0'; + + if (width > 120) + width = 120; + if (prec > 120) + prec = 120; + if (prec >= 0) + sprintf(out, fmt, width, prec, val); + else + sprintf(out, fmt, width, val); + len = strlen(out); + PRINT(out, len); + FLUSH(); + continue; + } + case 'n': + if (flags & QUADINT) + *GETARG(LONGLONG_T *) = ret; + else if (flags & LONGINT) + *GETARG(long *) = ret; + else if (flags & SHORTINT) + *GETARG(short *) = ret; + else + *GETARG(int *) = ret; + continue; /* no output */ + case 'O': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'o': + _uquad = UARG(); + base = OCT; + goto nosign; + case 'p': + + /* + ** ``The argument shall be a pointer to void. The + ** value of the pointer is converted to a sequence + ** of printable characters, in an implementation- + ** defined manner.'' + ** -- ANSI X3J11 + */ + + /* NOSTRICT */ + { + union + { + void *p; + ULONGLONG_T ll; + unsigned long l; + unsigned i; + } u; + u.p = GETARG(void *); + if (sizeof(void *) == sizeof(ULONGLONG_T)) + _uquad = u.ll; + else if (sizeof(void *) == sizeof(long)) + _uquad = u.l; + else + _uquad = u.i; + } + base = HEX; + xdigs = "0123456789abcdef"; + flags |= HEXPREFIX; + ch = 'x'; + goto nosign; + case 's': + if ((cp = GETARG(char *)) == NULL) + cp = "(null)"; + if (prec >= 0) + { + /* + ** can't use strlen; can only look for the + ** NUL in the first `prec' characters, and + ** strlen() will go further. + */ + + char *p = memchr(cp, 0, prec); + + if (p != NULL) + { + size = p - cp; + if (size > prec) + size = prec; + } + else + size = prec; + } + else + size = strlen(cp); + sign = '\0'; + break; + case 'U': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'u': + _uquad = UARG(); + base = DEC; + goto nosign; + case 'X': + xdigs = "0123456789ABCDEF"; + goto hex; + case 'x': + xdigs = "0123456789abcdef"; +hex: _uquad = UARG(); + base = HEX; + /* leading 0x/X only if non-zero */ + if (flags & ALT && _uquad != 0) + flags |= HEXPREFIX; + + /* unsigned conversions */ +nosign: sign = '\0'; + + /* + ** ``... diouXx conversions ... if a precision is + ** specified, the 0 flag will be ignored.'' + ** -- ANSI X3J11 + */ + +number: if ((dprec = prec) >= 0) + flags &= ~ZEROPAD; + + /* + ** ``The result of converting a zero value with an + ** explicit precision of zero is no characters.'' + ** -- ANSI X3J11 + */ + + cp = buf + BUF; + if (_uquad != 0 || prec != 0) + { + /* + ** Unsigned mod is hard, and unsigned mod + ** by a constant is easier than that by + ** a variable; hence this switch. + */ + + switch (base) + { + case OCT: + do + { + *--cp = to_char(_uquad & 7); + _uquad >>= 3; + } while (_uquad); + /* handle octal leading 0 */ + if (flags & ALT && *cp != '0') + *--cp = '0'; + break; + + case DEC: + /* many numbers are 1 digit */ + while (_uquad >= 10) + { + *--cp = to_char(_uquad % 10); + _uquad /= 10; + } + *--cp = to_char(_uquad); + break; + + case HEX: + do + { + *--cp = xdigs[_uquad & 15]; + _uquad >>= 4; + } while (_uquad); + break; + + default: + cp = "bug in sm_io_vfprintf: bad base"; + size = strlen(cp); + goto skipsize; + } + } + size = buf + BUF - cp; + skipsize: + break; + default: /* "%?" prints ?, unless ? is NUL */ + if (ch == '\0') + goto done; + /* pretend it was %c with argument ch */ + cp = buf; + *cp = ch; + size = 1; + sign = '\0'; + break; + } + + /* + ** All reasonable formats wind up here. At this point, `cp' + ** points to a string which (if not flags&LADJUST) should be + ** padded out to `width' places. If flags&ZEROPAD, it should + ** first be prefixed by any sign or other prefix; otherwise, + ** it should be blank padded before the prefix is emitted. + ** After any left-hand padding and prefixing, emit zeroes + ** required by a decimal [diouxX] precision, then print the + ** string proper, then emit zeroes required by any leftover + ** floating precision; finally, if LADJUST, pad with blanks. + ** + ** Compute actual size, so we know how much to pad. + ** size excludes decimal prec; realsz includes it. + */ + + realsz = dprec > size ? dprec : size; + if (sign) + realsz++; + else if (flags & HEXPREFIX) + realsz+= 2; + + /* right-adjusting blank padding */ + if ((flags & (LADJUST|ZEROPAD)) == 0) + PAD(width - realsz, blanks); + + /* prefix */ + if (sign) + { + PRINT(&sign, 1); + } + else if (flags & HEXPREFIX) + { + ox[0] = '0'; + ox[1] = ch; + PRINT(ox, 2); + } + + /* right-adjusting zero padding */ + if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) + PAD(width - realsz, zeroes); + + /* leading zeroes from decimal precision */ + PAD(dprec - size, zeroes); + + /* the string or number proper */ + PRINT(cp, size); + /* left-adjusting padding (always blank) */ + if (flags & LADJUST) + PAD(width - realsz, blanks); + + /* finally, adjust ret */ + ret += width > realsz ? width : realsz; + + FLUSH(); /* copy out the I/O vectors */ + } +done: + FLUSH(); +error: + if ((argtable != NULL) && (argtable != statargtable)) + sm_free(argtable); + return sm_error(fp) ? SM_IO_EOF : ret; + /* NOTREACHED */ +} + +/* Type ids for argument type table. */ +#define T_UNUSED 0 +#define T_SHORT 1 +#define T_U_SHORT 2 +#define TP_SHORT 3 +#define T_INT 4 +#define T_U_INT 5 +#define TP_INT 6 +#define T_LONG 7 +#define T_U_LONG 8 +#define TP_LONG 9 +#define T_QUAD 10 +#define T_U_QUAD 11 +#define TP_QUAD 12 +#define T_DOUBLE 13 +#define TP_CHAR 15 +#define TP_VOID 16 + +/* +** SM_FIND_ARGUMENTS -- find all args when a positional parameter is found. +** +** Find all arguments when a positional parameter is encountered. Returns a +** table, indexed by argument number, of pointers to each arguments. The +** initial argument table should be an array of STATIC_ARG_TBL_SIZE entries. +** It will be replaced with a malloc-ed one if it overflows. +** +** Parameters: +** fmt0 -- formating directives +** ap -- vector list of data unit for formating consumption +** argtable -- an indexable table (returned) of 'ap' +** +** Results: +** none. +*/ + +static void +sm_find_arguments(fmt0, ap, argtable) + const char *fmt0; + SM_VA_LOCAL_DECL + va_list **argtable; +{ + register char *fmt; /* format string */ + register int ch; /* character from fmt */ + register int n, n2; /* handy integer (short term usage) */ + register char *cp; /* handy char pointer (short term usage) */ + register int flags; /* flags as above */ + unsigned char *typetable; /* table of types */ + unsigned char stattypetable[STATIC_ARG_TBL_SIZE]; + int tablesize; /* current size of type table */ + int tablemax; /* largest used index in table */ + int nextarg; /* 1-based argument index */ + + /* Add an argument type to the table, expanding if necessary. */ +#define ADDTYPE(type) \ + ((nextarg >= tablesize) ? \ + (sm_grow_type_table_x(&typetable, &tablesize), 0) : 0, \ + typetable[nextarg++] = type, \ + (nextarg > tablemax) ? tablemax = nextarg : 0) + +#define ADDSARG() \ + ((flags & LONGINT) ? ADDTYPE(T_LONG) : \ + ((flags & SHORTINT) ? ADDTYPE(T_SHORT) : ADDTYPE(T_INT))) + +#define ADDUARG() \ + ((flags & LONGINT) ? ADDTYPE(T_U_LONG) : \ + ((flags & SHORTINT) ? ADDTYPE(T_U_SHORT) : ADDTYPE(T_U_INT))) + + /* Add * arguments to the type array. */ +#define ADDASTER() \ + n2 = 0; \ + cp = fmt; \ + while (is_digit(*cp)) \ + { \ + n2 = 10 * n2 + to_digit(*cp); \ + cp++; \ + } \ + if (*cp == '$') \ + { \ + int hold = nextarg; \ + nextarg = n2; \ + ADDTYPE (T_INT); \ + nextarg = hold; \ + fmt = ++cp; \ + } \ + else \ + { \ + ADDTYPE (T_INT); \ + } + fmt = (char *) fmt0; + typetable = stattypetable; + tablesize = STATIC_ARG_TBL_SIZE; + tablemax = 0; + nextarg = 1; + (void) memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE); + + /* Scan the format for conversions (`%' character). */ + for (;;) + { + for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) + /* void */; + if (ch == '\0') + goto done; + fmt++; /* skip over '%' */ + + flags = 0; + +rflag: ch = *fmt++; +reswitch: switch (ch) + { + case ' ': + case '#': + goto rflag; + case '*': + ADDASTER(); + goto rflag; + case '-': + case '+': + goto rflag; + case '.': + if ((ch = *fmt++) == '*') + { + ADDASTER(); + goto rflag; + } + while (is_digit(ch)) + { + ch = *fmt++; + } + goto reswitch; + case '0': + goto rflag; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + n = 0; + do + { + n = 10 * n + to_digit(ch); + ch = *fmt++; + } while (is_digit(ch)); + if (ch == '$') + { + nextarg = n; + goto rflag; + } + goto reswitch; + case 'h': + flags |= SHORTINT; + goto rflag; + case 'l': + flags |= LONGINT; + goto rflag; + case 'q': + flags |= QUADINT; + goto rflag; + case 'c': + ADDTYPE(T_INT); + break; + case 'D': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'd': + case 'i': + if (flags & QUADINT) + { + ADDTYPE(T_QUAD); + } + else + { + ADDSARG(); + } + break; + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + ADDTYPE(T_DOUBLE); + break; + case 'n': + if (flags & QUADINT) + ADDTYPE(TP_QUAD); + else if (flags & LONGINT) + ADDTYPE(TP_LONG); + else if (flags & SHORTINT) + ADDTYPE(TP_SHORT); + else + ADDTYPE(TP_INT); + continue; /* no output */ + case 'O': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'o': + if (flags & QUADINT) + ADDTYPE(T_U_QUAD); + else + ADDUARG(); + break; + case 'p': + ADDTYPE(TP_VOID); + break; + case 's': + ADDTYPE(TP_CHAR); + break; + case 'U': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'u': + if (flags & QUADINT) + ADDTYPE(T_U_QUAD); + else + ADDUARG(); + break; + case 'X': + case 'x': + if (flags & QUADINT) + ADDTYPE(T_U_QUAD); + else + ADDUARG(); + break; + default: /* "%?" prints ?, unless ? is NUL */ + if (ch == '\0') + goto done; + break; + } + } +done: + /* Build the argument table. */ + if (tablemax >= STATIC_ARG_TBL_SIZE) + { + *argtable = (va_list *) + sm_malloc(sizeof(va_list) * (tablemax + 1)); + } + + for (n = 1; n <= tablemax; n++) + { + SM_VA_COPY((*argtable)[n], ap); + switch (typetable [n]) + { + case T_UNUSED: + (void) SM_VA_ARG(ap, int); + break; + case T_SHORT: + (void) SM_VA_ARG(ap, int); + break; + case T_U_SHORT: + (void) SM_VA_ARG(ap, int); + break; + case TP_SHORT: + (void) SM_VA_ARG(ap, short *); + break; + case T_INT: + (void) SM_VA_ARG(ap, int); + break; + case T_U_INT: + (void) SM_VA_ARG(ap, unsigned int); + break; + case TP_INT: + (void) SM_VA_ARG(ap, int *); + break; + case T_LONG: + (void) SM_VA_ARG(ap, long); + break; + case T_U_LONG: + (void) SM_VA_ARG(ap, unsigned long); + break; + case TP_LONG: + (void) SM_VA_ARG(ap, long *); + break; + case T_QUAD: + (void) SM_VA_ARG(ap, LONGLONG_T); + break; + case T_U_QUAD: + (void) SM_VA_ARG(ap, ULONGLONG_T); + break; + case TP_QUAD: + (void) SM_VA_ARG(ap, LONGLONG_T *); + break; + case T_DOUBLE: + (void) SM_VA_ARG(ap, double); + break; + case TP_CHAR: + (void) SM_VA_ARG(ap, char *); + break; + case TP_VOID: + (void) SM_VA_ARG(ap, void *); + break; + } + } + + if ((typetable != NULL) && (typetable != stattypetable)) + sm_free(typetable); +} + +/* +** SM_GROW_TYPE_TABLE -- Increase the size of the type table. +** +** Parameters: +** tabletype -- type of table to grow +** tablesize -- requested new table size +** +** Results: +** Raises an exception if can't allocate memory. +*/ + +static void +sm_grow_type_table_x(typetable, tablesize) + unsigned char **typetable; + int *tablesize; +{ + unsigned char *oldtable = *typetable; + int newsize = *tablesize * 2; + + if (*tablesize == STATIC_ARG_TBL_SIZE) + { + *typetable = (unsigned char *) sm_malloc_x(sizeof(unsigned char) + * newsize); + (void) memmove(*typetable, oldtable, *tablesize); + } + else + { + *typetable = (unsigned char *) sm_realloc_x(typetable, + sizeof(unsigned char) * newsize); + } + (void) memset(&typetable [*tablesize], T_UNUSED, + (newsize - *tablesize)); + + *tablesize = newsize; +} diff --git a/usr/src/cmd/sendmail/libsm/vfscanf.c b/usr/src/cmd/sendmail/libsm/vfscanf.c new file mode 100644 index 0000000000..bc0f1d004d --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/vfscanf.c @@ -0,0 +1,877 @@ +/* + * Copyright (c) 2000-2001, 2004 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_IDSTR(id, "@(#)$Id: vfscanf.c,v 1.52 2004/08/03 20:56:32 ca Exp $") + +#include <ctype.h> +#include <stdlib.h> +#include <errno.h> +#include <setjmp.h> +#include <sys/time.h> +#include <sm/varargs.h> +#include <sm/config.h> +#include <sm/io.h> +#include <sm/signal.h> +#include <sm/clock.h> +#include <sm/string.h> +#include "local.h" + +#define BUF 513 /* Maximum length of numeric string. */ + +/* Flags used during conversion. */ +#define LONG 0x01 /* l: long or double */ +#define SHORT 0x04 /* h: short */ +#define QUAD 0x08 /* q: quad (same as ll) */ +#define SUPPRESS 0x10 /* suppress assignment */ +#define POINTER 0x20 /* weird %p pointer (`fake hex') */ +#define NOSKIP 0x40 /* do not skip blanks */ + +/* +** The following are used in numeric conversions only: +** SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point; +** SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral. +*/ + +#define SIGNOK 0x080 /* +/- is (still) legal */ +#define NDIGITS 0x100 /* no digits detected */ + +#define DPTOK 0x200 /* (float) decimal point is still legal */ +#define EXPOK 0x400 /* (float) exponent (e+3, etc) still legal */ + +#define PFXOK 0x200 /* 0x prefix is (still) legal */ +#define NZDIGITS 0x400 /* no zero digits detected */ + +/* Conversion types. */ +#define CT_CHAR 0 /* %c conversion */ +#define CT_CCL 1 /* %[...] conversion */ +#define CT_STRING 2 /* %s conversion */ +#define CT_INT 3 /* integer, i.e., strtoll or strtoull */ +#define CT_FLOAT 4 /* floating, i.e., strtod */ + +static void scanalrm __P((int)); +static unsigned char *sm_sccl __P((char *, unsigned char *)); +static jmp_buf ScanTimeOut; + +/* +** SCANALRM -- handler when timeout activated for sm_io_vfscanf() +** +** Returns flow of control to where setjmp(ScanTimeOut) was set. +** +** Parameters: +** sig -- unused +** +** Returns: +** does not return +** +** Side Effects: +** returns flow of control to setjmp(ScanTimeOut). +** +** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD +** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE +** DOING. +*/ + +/* ARGSUSED0 */ +static void +scanalrm(sig) + int sig; +{ + longjmp(ScanTimeOut, 1); +} + +/* +** SM_VFSCANF -- convert input into data units +** +** Parameters: +** fp -- file pointer for input data +** timeout -- time intvl allowed to complete (milliseconds) +** fmt0 -- format for finding data units +** ap -- vectors for memory location for storing data units +** +** Results: +** Success: number of data units assigned +** Failure: SM_IO_EOF +*/ + +int +sm_vfscanf(fp, timeout, fmt0, ap) + register SM_FILE_T *fp; + int SM_NONVOLATILE timeout; + char const *fmt0; + va_list SM_NONVOLATILE ap; +{ + register unsigned char *SM_NONVOLATILE fmt = (unsigned char *) fmt0; + register int c; /* character from format, or conversion */ + register size_t width; /* field width, or 0 */ + register char *p; /* points into all kinds of strings */ + register int n; /* handy integer */ + register int flags; /* flags as defined above */ + register char *p0; /* saves original value of p when necessary */ + int nassigned; /* number of fields assigned */ + int nread; /* number of characters consumed from fp */ + int base; /* base argument to strtoll/strtoull */ + ULONGLONG_T (*ccfn)(); /* conversion function (strtoll/strtoull) */ + char ccltab[256]; /* character class table for %[...] */ + char buf[BUF]; /* buffer for numeric conversions */ + SM_EVENT *evt = NULL; + + /* `basefix' is used to avoid `if' tests in the integer scanner */ + static short basefix[17] = + { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; + + if (timeout == SM_TIME_DEFAULT) + timeout = fp->f_timeout; + if (timeout == SM_TIME_IMMEDIATE) + { + /* + ** Filling the buffer will take time and we are wanted to + ** return immediately. So... + */ + + errno = EAGAIN; + return SM_IO_EOF; + } + + if (timeout != SM_TIME_FOREVER) + { + if (setjmp(ScanTimeOut) != 0) + { + errno = EAGAIN; + return SM_IO_EOF; + } + + evt = sm_seteventm(timeout, scanalrm, 0); + } + + nassigned = 0; + nread = 0; + base = 0; /* XXX just to keep gcc happy */ + ccfn = NULL; /* XXX just to keep gcc happy */ + for (;;) + { + c = *fmt++; + if (c == 0) + { + if (evt != NULL) + sm_clrevent(evt); /* undo our timeout */ + return nassigned; + } + if (isspace(c)) + { + while ((fp->f_r > 0 || sm_refill(fp, SM_TIME_FOREVER) + == 0) && + isspace(*fp->f_p)) + nread++, fp->f_r--, fp->f_p++; + continue; + } + if (c != '%') + goto literal; + width = 0; + flags = 0; + + /* + ** switch on the format. continue if done; + ** break once format type is derived. + */ + +again: c = *fmt++; + switch (c) + { + case '%': +literal: + if (fp->f_r <= 0 && sm_refill(fp, SM_TIME_FOREVER)) + goto input_failure; + if (*fp->f_p != c) + goto match_failure; + fp->f_r--, fp->f_p++; + nread++; + continue; + + case '*': + flags |= SUPPRESS; + goto again; + case 'h': + flags |= SHORT; + goto again; + case 'l': + if (*fmt == 'l') + { + fmt++; + flags |= QUAD; + } + else + { + flags |= LONG; + } + goto again; + case 'q': + flags |= QUAD; + goto again; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + width = width * 10 + c - '0'; + goto again; + + /* + ** Conversions. + ** Those marked `compat' are for 4.[123]BSD compatibility. + ** + ** (According to ANSI, E and X formats are supposed + ** to the same as e and x. Sorry about that.) + */ + + case 'D': /* compat */ + flags |= LONG; + /* FALLTHROUGH */ + case 'd': + c = CT_INT; + ccfn = (ULONGLONG_T (*)())sm_strtoll; + base = 10; + break; + + case 'i': + c = CT_INT; + ccfn = (ULONGLONG_T (*)())sm_strtoll; + base = 0; + break; + + case 'O': /* compat */ + flags |= LONG; + /* FALLTHROUGH */ + case 'o': + c = CT_INT; + ccfn = sm_strtoull; + base = 8; + break; + + case 'u': + c = CT_INT; + ccfn = sm_strtoull; + base = 10; + break; + + case 'X': + case 'x': + flags |= PFXOK; /* enable 0x prefixing */ + c = CT_INT; + ccfn = sm_strtoull; + base = 16; + break; + + case 'E': + case 'G': + case 'e': + case 'f': + case 'g': + c = CT_FLOAT; + break; + + case 's': + c = CT_STRING; + break; + + case '[': + fmt = sm_sccl(ccltab, fmt); + flags |= NOSKIP; + c = CT_CCL; + break; + + case 'c': + flags |= NOSKIP; + c = CT_CHAR; + break; + + case 'p': /* pointer format is like hex */ + flags |= POINTER | PFXOK; + c = CT_INT; + ccfn = sm_strtoull; + base = 16; + break; + + case 'n': + if (flags & SUPPRESS) /* ??? */ + continue; + if (flags & SHORT) + *SM_VA_ARG(ap, short *) = nread; + else if (flags & LONG) + *SM_VA_ARG(ap, long *) = nread; + else + *SM_VA_ARG(ap, int *) = nread; + continue; + + /* Disgusting backwards compatibility hacks. XXX */ + case '\0': /* compat */ + if (evt != NULL) + sm_clrevent(evt); /* undo our timeout */ + return SM_IO_EOF; + + default: /* compat */ + if (isupper(c)) + flags |= LONG; + c = CT_INT; + ccfn = (ULONGLONG_T (*)()) sm_strtoll; + base = 10; + break; + } + + /* We have a conversion that requires input. */ + if (fp->f_r <= 0 && sm_refill(fp, SM_TIME_FOREVER)) + goto input_failure; + + /* + ** Consume leading white space, except for formats + ** that suppress this. + */ + + if ((flags & NOSKIP) == 0) + { + while (isspace(*fp->f_p)) + { + nread++; + if (--fp->f_r > 0) + fp->f_p++; + else if (sm_refill(fp, SM_TIME_FOREVER)) + goto input_failure; + } + /* + ** Note that there is at least one character in + ** the buffer, so conversions that do not set NOSKIP + ** can no longer result in an input failure. + */ + } + + /* Do the conversion. */ + switch (c) + { + case CT_CHAR: + /* scan arbitrary characters (sets NOSKIP) */ + if (width == 0) + width = 1; + if (flags & SUPPRESS) + { + size_t sum = 0; + for (;;) + { + if ((size_t) (n = fp->f_r) < width) + { + sum += n; + width -= n; + fp->f_p += n; + if (sm_refill(fp, + SM_TIME_FOREVER)) + { + if (sum == 0) + goto input_failure; + break; + } + } + else + { + sum += width; + fp->f_r -= width; + fp->f_p += width; + break; + } + } + nread += sum; + } + else + { + size_t r; + + r = sm_io_read(fp, SM_TIME_FOREVER, + (void *) SM_VA_ARG(ap, char *), + width); + if (r == 0) + goto input_failure; + nread += r; + nassigned++; + } + break; + + case CT_CCL: + /* scan a (nonempty) character class (sets NOSKIP) */ + if (width == 0) + width = (size_t)~0; /* `infinity' */ + + /* take only those things in the class */ + if (flags & SUPPRESS) + { + n = 0; + while (ccltab[*fp->f_p] != '\0') + { + n++, fp->f_r--, fp->f_p++; + if (--width == 0) + break; + if (fp->f_r <= 0 && + sm_refill(fp, SM_TIME_FOREVER)) + { + if (n == 0) /* XXX how? */ + goto input_failure; + break; + } + } + if (n == 0) + goto match_failure; + } + else + { + p0 = p = SM_VA_ARG(ap, char *); + while (ccltab[*fp->f_p] != '\0') + { + fp->f_r--; + *p++ = *fp->f_p++; + if (--width == 0) + break; + if (fp->f_r <= 0 && + sm_refill(fp, SM_TIME_FOREVER)) + { + if (p == p0) + goto input_failure; + break; + } + } + n = p - p0; + if (n == 0) + goto match_failure; + *p = 0; + nassigned++; + } + nread += n; + break; + + case CT_STRING: + /* like CCL, but zero-length string OK, & no NOSKIP */ + if (width == 0) + width = (size_t)~0; + if (flags & SUPPRESS) + { + n = 0; + while (!isspace(*fp->f_p)) + { + n++, fp->f_r--, fp->f_p++; + if (--width == 0) + break; + if (fp->f_r <= 0 && + sm_refill(fp, SM_TIME_FOREVER)) + break; + } + nread += n; + } + else + { + p0 = p = SM_VA_ARG(ap, char *); + while (!isspace(*fp->f_p)) + { + fp->f_r--; + *p++ = *fp->f_p++; + if (--width == 0) + break; + if (fp->f_r <= 0 && + sm_refill(fp, SM_TIME_FOREVER)) + break; + } + *p = 0; + nread += p - p0; + nassigned++; + } + continue; + + case CT_INT: + /* scan an integer as if by strtoll/strtoull */ +#if SM_CONF_BROKEN_SIZE_T + if (width == 0 || width > sizeof(buf) - 1) + width = sizeof(buf) - 1; +#else /* SM_CONF_BROKEN_SIZE_T */ + /* size_t is unsigned, hence this optimisation */ + if (--width > sizeof(buf) - 2) + width = sizeof(buf) - 2; + width++; +#endif /* SM_CONF_BROKEN_SIZE_T */ + flags |= SIGNOK | NDIGITS | NZDIGITS; + for (p = buf; width > 0; width--) + { + c = *fp->f_p; + + /* + ** Switch on the character; `goto ok' + ** if we accept it as a part of number. + */ + + switch (c) + { + + /* + ** The digit 0 is always legal, but is + ** special. For %i conversions, if no + ** digits (zero or nonzero) have been + ** scanned (only signs), we will have + ** base==0. In that case, we should set + ** it to 8 and enable 0x prefixing. + ** Also, if we have not scanned zero digits + ** before this, do not turn off prefixing + ** (someone else will turn it off if we + ** have scanned any nonzero digits). + */ + + case '0': + if (base == 0) + { + base = 8; + flags |= PFXOK; + } + if (flags & NZDIGITS) + flags &= ~(SIGNOK|NZDIGITS|NDIGITS); + else + flags &= ~(SIGNOK|PFXOK|NDIGITS); + goto ok; + + /* 1 through 7 always legal */ + case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + base = basefix[base]; + flags &= ~(SIGNOK | PFXOK | NDIGITS); + goto ok; + + /* digits 8 and 9 ok iff decimal or hex */ + case '8': case '9': + base = basefix[base]; + if (base <= 8) + break; /* not legal here */ + flags &= ~(SIGNOK | PFXOK | NDIGITS); + goto ok; + + /* letters ok iff hex */ + case 'A': case 'B': case 'C': + case 'D': case 'E': case 'F': + case 'a': case 'b': case 'c': + case 'd': case 'e': case 'f': + + /* no need to fix base here */ + if (base <= 10) + break; /* not legal here */ + flags &= ~(SIGNOK | PFXOK | NDIGITS); + goto ok; + + /* sign ok only as first character */ + case '+': case '-': + if (flags & SIGNOK) + { + flags &= ~SIGNOK; + goto ok; + } + break; + + /* x ok iff flag still set & 2nd char */ + case 'x': case 'X': + if (flags & PFXOK && p == buf + 1) + { + base = 16; /* if %i */ + flags &= ~PFXOK; + goto ok; + } + break; + } + + /* + ** If we got here, c is not a legal character + ** for a number. Stop accumulating digits. + */ + + break; + ok: + /* c is legal: store it and look at the next. */ + *p++ = c; + if (--fp->f_r > 0) + fp->f_p++; + else if (sm_refill(fp, SM_TIME_FOREVER)) + break; /* SM_IO_EOF */ + } + + /* + ** If we had only a sign, it is no good; push + ** back the sign. If the number ends in `x', + ** it was [sign] '0' 'x', so push back the x + ** and treat it as [sign] '0'. + */ + + if (flags & NDIGITS) + { + if (p > buf) + (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, + *(unsigned char *)--p); + goto match_failure; + } + c = ((unsigned char *)p)[-1]; + if (c == 'x' || c == 'X') + { + --p; + (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, c); + } + if ((flags & SUPPRESS) == 0) + { + ULONGLONG_T res; + + *p = 0; + res = (*ccfn)(buf, (char **)NULL, base); + if (flags & POINTER) + *SM_VA_ARG(ap, void **) = + (void *)(long) res; + else if (flags & QUAD) + *SM_VA_ARG(ap, LONGLONG_T *) = res; + else if (flags & LONG) + *SM_VA_ARG(ap, long *) = res; + else if (flags & SHORT) + *SM_VA_ARG(ap, short *) = res; + else + *SM_VA_ARG(ap, int *) = res; + nassigned++; + } + nread += p - buf; + break; + + case CT_FLOAT: + /* scan a floating point number as if by strtod */ + if (width == 0 || width > sizeof(buf) - 1) + width = sizeof(buf) - 1; + flags |= SIGNOK | NDIGITS | DPTOK | EXPOK; + for (p = buf; width; width--) + { + c = *fp->f_p; + + /* + ** This code mimicks the integer conversion + ** code, but is much simpler. + */ + + switch (c) + { + + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': + flags &= ~(SIGNOK | NDIGITS); + goto fok; + + case '+': case '-': + if (flags & SIGNOK) + { + flags &= ~SIGNOK; + goto fok; + } + break; + case '.': + if (flags & DPTOK) + { + flags &= ~(SIGNOK | DPTOK); + goto fok; + } + break; + case 'e': case 'E': + + /* no exponent without some digits */ + if ((flags&(NDIGITS|EXPOK)) == EXPOK) + { + flags = + (flags & ~(EXPOK|DPTOK)) | + SIGNOK | NDIGITS; + goto fok; + } + break; + } + break; + fok: + *p++ = c; + if (--fp->f_r > 0) + fp->f_p++; + else if (sm_refill(fp, SM_TIME_FOREVER)) + break; /* SM_IO_EOF */ + } + + /* + ** If no digits, might be missing exponent digits + ** (just give back the exponent) or might be missing + ** regular digits, but had sign and/or decimal point. + */ + + if (flags & NDIGITS) + { + if (flags & EXPOK) + { + /* no digits at all */ + while (p > buf) + (void) sm_io_ungetc(fp, + SM_TIME_DEFAULT, + *(unsigned char *)--p); + goto match_failure; + } + + /* just a bad exponent (e and maybe sign) */ + c = *(unsigned char *) --p; + if (c != 'e' && c != 'E') + { + (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, + c); /* sign */ + c = *(unsigned char *)--p; + } + (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, c); + } + if ((flags & SUPPRESS) == 0) + { + double res; + + *p = 0; + res = strtod(buf, (char **) NULL); + if (flags & LONG) + *SM_VA_ARG(ap, double *) = res; + else + *SM_VA_ARG(ap, float *) = res; + nassigned++; + } + nread += p - buf; + break; + } + } +input_failure: + if (evt != NULL) + sm_clrevent(evt); /* undo our timeout */ + return nassigned ? nassigned : -1; +match_failure: + if (evt != NULL) + sm_clrevent(evt); /* undo our timeout */ + return nassigned; +} + +/* +** SM_SCCL -- sequenced character comparison list +** +** Fill in the given table from the scanset at the given format +** (just after `['). Return a pointer to the character past the +** closing `]'. The table has a 1 wherever characters should be +** considered part of the scanset. +** +** Parameters: +** tab -- array flagging "active" char's to match (returned) +** fmt -- character list (within "[]") +** +** Results: +*/ + +static unsigned char * +sm_sccl(tab, fmt) + register char *tab; + register unsigned char *fmt; +{ + register int c, n, v; + + /* first `clear' the whole table */ + c = *fmt++; /* first char hat => negated scanset */ + if (c == '^') + { + v = 1; /* default => accept */ + c = *fmt++; /* get new first char */ + } + else + v = 0; /* default => reject */ + + /* should probably use memset here */ + for (n = 0; n < 256; n++) + tab[n] = v; + if (c == 0) + return fmt - 1; /* format ended before closing ] */ + + /* + ** Now set the entries corresponding to the actual scanset + ** to the opposite of the above. + ** + ** The first character may be ']' (or '-') without being special; + ** the last character may be '-'. + */ + + v = 1 - v; + for (;;) + { + tab[c] = v; /* take character c */ +doswitch: + n = *fmt++; /* and examine the next */ + switch (n) + { + + case 0: /* format ended too soon */ + return fmt - 1; + + case '-': + /* + ** A scanset of the form + ** [01+-] + ** is defined as `the digit 0, the digit 1, + ** the character +, the character -', but + ** the effect of a scanset such as + ** [a-zA-Z0-9] + ** is implementation defined. The V7 Unix + ** scanf treats `a-z' as `the letters a through + ** z', but treats `a-a' as `the letter a, the + ** character -, and the letter a'. + ** + ** For compatibility, the `-' is not considerd + ** to define a range if the character following + ** it is either a close bracket (required by ANSI) + ** or is not numerically greater than the character + ** we just stored in the table (c). + */ + + n = *fmt; + if (n == ']' || n < c) + { + c = '-'; + break; /* resume the for(;;) */ + } + fmt++; + do + { + /* fill in the range */ + tab[++c] = v; + } while (c < n); +#if 1 /* XXX another disgusting compatibility hack */ + + /* + ** Alas, the V7 Unix scanf also treats formats + ** such as [a-c-e] as `the letters a through e'. + ** This too is permitted by the standard.... + */ + + goto doswitch; +#else + c = *fmt++; + if (c == 0) + return fmt - 1; + if (c == ']') + return fmt; + break; +#endif + + case ']': /* end of scanset */ + return fmt; + + default: /* just another character */ + c = n; + break; + } + } + /* NOTREACHED */ +} diff --git a/usr/src/cmd/sendmail/libsm/vprintf.c b/usr/src/cmd/sendmail/libsm/vprintf.c new file mode 100644 index 0000000000..57acfb471a --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/vprintf.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: vprintf.c,v 1.12 2001/01/24 01:27:26 gshapiro Exp $") +#include <sm/io.h> +#include "local.h" + +/* +** SM_VPRINTF -- print to standard out with variable length args +** +** Parameters: +** timeout -- length of time allow to do the print +** fmt -- the format of the output +** ap -- the variable number of args to be used for output +** +** Returns: +** as sm_io_vfprintf() does. +*/ + +int +sm_vprintf(timeout, fmt, ap) + int timeout; + char const *fmt; + SM_VA_LOCAL_DECL +{ + return sm_io_vfprintf(smiostdout, timeout, fmt, ap); +} diff --git a/usr/src/cmd/sendmail/libsm/vsnprintf.c b/usr/src/cmd/sendmail/libsm/vsnprintf.c new file mode 100644 index 0000000000..67f9f0b121 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/vsnprintf.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: vsnprintf.c,v 1.21 2001/03/04 23:28:41 ca Exp $") +#include <limits.h> +#include <sm/io.h> +#include "local.h" + +/* +** SM_VSNPRINTF -- format data for "output" into a string +** +** Assigned 'str' to a "fake" file pointer. This allows common +** o/p formatting function sm_vprintf() to be used. +** +** Parameters: +** str -- location for output +** n -- maximum size for o/p +** fmt -- format directives +** ap -- data unit vectors for use by 'fmt' +** +** Results: +** result from sm_io_vfprintf() +** +** Side Effects: +** Limits the size ('n') to INT_MAX. +*/ + +int +sm_vsnprintf(str, n, fmt, ap) + char *str; + size_t n; + const char *fmt; + SM_VA_LOCAL_DECL +{ + int ret; + char dummy; + SM_FILE_T fake; + + /* While snprintf(3) specifies size_t stdio uses an int internally */ + if (n > INT_MAX) + n = INT_MAX; + + /* Stdio internals do not deal correctly with zero length buffer */ + if (n == 0) + { + str = &dummy; + n = 1; + } + fake.sm_magic = SmFileMagic; + fake.f_timeout = SM_TIME_FOREVER; + fake.f_timeoutstate = SM_TIME_BLOCK; + fake.f_file = -1; + fake.f_flags = SMWR | SMSTR; + fake.f_bf.smb_base = fake.f_p = (unsigned char *)str; + fake.f_bf.smb_size = fake.f_w = n - 1; + fake.f_close = NULL; + fake.f_open = NULL; + fake.f_read = NULL; + fake.f_write = NULL; + fake.f_seek = NULL; + fake.f_setinfo = fake.f_getinfo = NULL; + fake.f_type = "sm_vsnprintf:fake"; + ret = sm_io_vfprintf(&fake, SM_TIME_FOREVER, fmt, ap); + *fake.f_p = 0; + return ret; +} diff --git a/usr/src/cmd/sendmail/libsm/wbuf.c b/usr/src/cmd/sendmail/libsm/wbuf.c new file mode 100644 index 0000000000..21ac5c82b6 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/wbuf.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: wbuf.c,v 1.19 2001/03/05 03:22:41 ca Exp $") +#include <errno.h> +#include <sm/io.h> +#include "local.h" + +/* Note: This function is called from a macro located in <sm/io.h> */ + +/* +** SM_WBUF -- write character to and flush (likely now full) buffer +** +** Write the given character into the (probably full) buffer for +** the given file. Flush the buffer out if it is or becomes full, +** or if c=='\n' and the file is line buffered. +** +** Parameters: +** fp -- the file pointer +** timeout -- time to complete operation (milliseconds) +** c -- int representation of the character to add +** +** Results: +** Failure: -1 and sets errno +** Success: int value of 'c' +*/ + +int +sm_wbuf(fp, timeout, c) + register SM_FILE_T *fp; + int timeout; + register int c; +{ + register int n; + + /* + ** In case we cannot write, or longjmp takes us out early, + ** make sure w is 0 (if fully- or un-buffered) or -bf.smb_size + ** (if line buffered) so that we will get called again. + ** If we did not do this, a sufficient number of sm_io_putc() + ** calls might wrap w from negative to positive. + */ + + fp->f_w = fp->f_lbfsize; + if (cantwrite(fp)) + { + errno = EBADF; + return SM_IO_EOF; + } + c = (unsigned char)c; + + /* + ** If it is completely full, flush it out. Then, in any case, + ** stuff c into the buffer. If this causes the buffer to fill + ** completely, or if c is '\n' and the file is line buffered, + ** flush it (perhaps a second time). The second flush will always + ** happen on unbuffered streams, where bf.smb_size==1; sm_io_flush() + ** guarantees that sm_io_putc() will always call sm_wbuf() by setting + ** w to 0, so we need not do anything else. + ** Note for the timeout, only one of the sm_io_flush's will get called. + */ + + n = fp->f_p - fp->f_bf.smb_base; + if (n >= fp->f_bf.smb_size) + { + if (sm_io_flush(fp, timeout)) + return SM_IO_EOF; /* sm_io_flush() sets errno */ + n = 0; + } + fp->f_w--; + *fp->f_p++ = c; + if (++n == fp->f_bf.smb_size || (fp->f_flags & SMLBF && c == '\n')) + if (sm_io_flush(fp, timeout)) + return SM_IO_EOF; /* sm_io_flush() sets errno */ + return c; +} diff --git a/usr/src/cmd/sendmail/libsm/wsetup.c b/usr/src/cmd/sendmail/libsm/wsetup.c new file mode 100644 index 0000000000..e7a5e7924b --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/wsetup.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2000-2002 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: wsetup.c,v 1.20 2002/02/07 18:02:45 ca Exp $") +#include <stdlib.h> +#include <errno.h> +#include <sm/io.h> +#include "local.h" + +/* +** SM_WSETUP -- check writing is safe +** +** Various output routines call wsetup to be sure it is safe to write, +** because either flags does not include SMMWR, or buf is NULL. +** Used in the macro "cantwrite" found in "local.h". +** +** Parameters: +** fp -- the file pointer +** +** Results: +** Failure: SM_IO_EOF and sets errno +** Success: 0 (zero) +*/ + +int +sm_wsetup(fp) + register SM_FILE_T *fp; +{ + /* make sure stdio is set up */ + if (!Sm_IO_DidInit) + sm_init(); + + /* If we are not writing, we had better be reading and writing. */ + if ((fp->f_flags & SMWR) == 0) + { + if ((fp->f_flags & SMRW) == 0) + { + errno = EBADF; + return SM_IO_EOF; + } + if (fp->f_flags & SMRD) + { + /* clobber any ungetc data */ + if (HASUB(fp)) + FREEUB(fp); + + /* discard read buffer */ + fp->f_flags &= ~(SMRD|SMFEOF); + fp->f_r = 0; + fp->f_p = fp->f_bf.smb_base; + } + fp->f_flags |= SMWR; + } + + /* Make a buffer if necessary, then set w. */ + if (fp->f_bf.smb_base == NULL) + sm_makebuf(fp); + if (fp->f_flags & SMLBF) + { + /* + ** It is line buffered, so make lbfsize be -bufsize + ** for the sm_putc() macro. We will change lbfsize back + ** to 0 whenever we turn off SMWR. + */ + + fp->f_w = 0; + fp->f_lbfsize = -fp->f_bf.smb_size; + } + else + fp->f_w = fp->f_flags & SMNBF ? 0 : fp->f_bf.smb_size; + return 0; +} diff --git a/usr/src/cmd/sendmail/libsm/xtrap.c b/usr/src/cmd/sendmail/libsm/xtrap.c new file mode 100644 index 0000000000..5f82c3e0a4 --- /dev/null +++ b/usr/src/cmd/sendmail/libsm/xtrap.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2000 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: xtrap.c,v 1.3 2000/12/08 08:03:09 rodney Exp $") + +#include <sm/xtrap.h> + +SM_ATOMIC_UINT_T SmXtrapCount; + +SM_DEBUG_T SmXtrapDebug = SM_DEBUG_INITIALIZER("sm_xtrap", + "@(#)$Debug: sm_xtrap - raise exception at N'th xtrap point $"); + +SM_DEBUG_T SmXtrapReport = SM_DEBUG_INITIALIZER("sm_xtrap_report", + "@(#)$Debug: sm_xtrap_report - report xtrap count on exit $"); |