This commit was manufactured by cvs2svn to create branch 'RELENG_6'.

This commit is contained in:
cvs2svn 2007-04-10 20:03:43 +00:00
parent 00cf7c7fec
commit e417d4dff3
33 changed files with 4728 additions and 0 deletions

View File

@ -0,0 +1,22 @@
divert(-1)
#
# Copyright (c) 2006 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.
#
#
divert(0)
VERSIONID(`$Id: badmx.m4,v 1.1 2006/12/16 00:56:32 ca Exp $')
divert(-1)
define(`_BADMX_CHK_', 1)
LOCAL_CONFIG
Kmxlist bestmx -z: -T<TEMP>
Kbadmx regex -a<BADMX> ^(([0-9]{1,3}\.){3}[0-9]){0,1}\.$
KdnsA dns -R A -a. -T<TEMP>
KBadMXIP regex -a<BADMXIP> ifelse(defn(`_ARG_'), `', `^(127\.|10\.|0\.0\.0\.0)', `_ARG_')

View File

@ -0,0 +1,18 @@
divert(-1)
#
# Copyright (c) 2006 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.
#
#
divert(0)dnl
VERSIONID(`$Id: block_bad_helo.m4,v 1.1 2006/06/15 22:49:30 ca Exp $')
divert(-1)
define(`_BLOCK_BAD_HELO_', `')dnl
RELAY_DOMAIN(`127.0.0.1')dnl
LOCAL_DOMAIN(`[127.0.0.1]')dnl

View File

@ -0,0 +1,16 @@
divert(-1)
#
# Copyright (c) 2006 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.
#
#
divert(0)dnl
VERSIONID(`$Id: require_rdns.m4,v 1.1 2006/06/15 22:49:30 ca Exp $')
divert(-1)
define(`_REQUIRE_RDNS_', `')

View File

@ -0,0 +1,19 @@
/*
* Copyright (c) 2006 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.
*
* $Id: misc.h,v 1.1 2006/06/28 23:57:59 ca Exp $
*/
#ifndef SM_MISC_H
# define SM_MISC_H 1
int sm_memstat_open __P((void));
int sm_memstat_close __P((void));
int sm_memstat_get __P((char *, long *));
#endif /* ! SM_MISC_H */

View File

@ -0,0 +1,20 @@
/*
* Copyright (c) 2007 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.
*
* $Id: sm_os_qnx.h,v 1.1 2007/03/21 23:56:20 ca Exp $
*/
/*
** sm_os_qnx.h -- platform definitions for QNX
*/
#define SM_CONF_SYS_CDEFS_H 1
#ifndef SM_CONF_SETITIMER
# define SM_CONF_SETITIMER 0
#endif /* SM_CONF_SETITIMER */

View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2006 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.
*/
/*
** SENDMAIL.H -- MTA-specific definitions for sendmail.
*/
#ifndef _SM_SENDMAIL_H
# define _SM_SENDMAIL_H 1
/* "out of band" indicator */
#define METAQUOTE ((unsigned char)0377) /* quotes the next octet */
extern int dequote_internal_chars __P((char *, char *, int));
extern char *quote_internal_chars __P((char *, char *, int *));
extern char *str2prt __P((char *));
#endif /* ! _SM_SENDMAIL_H */

View File

@ -0,0 +1,153 @@
/* $OpenBSD: queue.h,v 1.30 2005/10/25 06:37:47 otto Exp $ */
/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
/*
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)queue.h 8.5 (Berkeley) 8/20/94
*/
#ifndef SM_TAILQ_H_
#define SM_TAILQ_H_
/*
* This file is a modified copy of queue.h from a BSD system:
* we only need tail queues here.
*
* A tail queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or
* after an existing element, at the head of the list, or at the end of
* the list. A tail queue may be traversed in either direction.
*/
/*
* Tail queue definitions.
*/
#define SM_TAILQ_HEAD(name, type) \
struct name { \
struct type *tqh_first; /* first element */ \
struct type **tqh_last; /* addr of last next element */ \
}
#define SM_TAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).tqh_first }
#define SM_TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
}
/*
* tail queue access methods
*/
#define SM_TAILQ_FIRST(head) ((head)->tqh_first)
#define SM_TAILQ_END(head) NULL
#define SM_TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define SM_TAILQ_LAST(head, headname) \
(*(((struct headname *)((head)->tqh_last))->tqh_last))
/* XXX */
#define SM_TAILQ_PREV(elm, headname, field) \
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
#define SM_TAILQ_EMPTY(head) \
(SM_TAILQ_FIRST(head) == SM_TAILQ_END(head))
#define SM_TAILQ_FOREACH(var, head, field) \
for((var) = SM_TAILQ_FIRST(head); \
(var) != SM_TAILQ_END(head); \
(var) = SM_TAILQ_NEXT(var, field))
#define SM_TAILQ_FOREACH_REVERSE(var, head, headname, field) \
for((var) = SM_TAILQ_LAST(head, headname); \
(var) != SM_TAILQ_END(head); \
(var) = SM_TAILQ_PREV(var, headname, field))
/*
* Tail queue functions.
*/
#define SM_TAILQ_INIT(head) do { \
(head)->tqh_first = NULL; \
(head)->tqh_last = &(head)->tqh_first; \
} while (0)
#define SM_TAILQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
(head)->tqh_first->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(head)->tqh_first = (elm); \
(elm)->field.tqe_prev = &(head)->tqh_first; \
} while (0)
#define SM_TAILQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.tqe_next = NULL; \
(elm)->field.tqe_prev = (head)->tqh_last; \
*(head)->tqh_last = (elm); \
(head)->tqh_last = &(elm)->field.tqe_next; \
} while (0)
#define SM_TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
(elm)->field.tqe_next->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(listelm)->field.tqe_next = (elm); \
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
} while (0)
#define SM_TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
(elm)->field.tqe_next = (listelm); \
*(listelm)->field.tqe_prev = (elm); \
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
} while (0)
#define SM_TAILQ_REMOVE(head, elm, field) do { \
if (((elm)->field.tqe_next) != NULL) \
(elm)->field.tqe_next->field.tqe_prev = \
(elm)->field.tqe_prev; \
else \
(head)->tqh_last = (elm)->field.tqe_prev; \
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
} while (0)
#define SM_TAILQ_REPLACE(head, elm, elm2, field) do { \
if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
(elm2)->field.tqe_next->field.tqe_prev = \
&(elm2)->field.tqe_next; \
else \
(head)->tqh_last = &(elm2)->field.tqe_next; \
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
*(elm2)->field.tqe_prev = (elm2); \
} while (0)
#endif /* !SM_TAILQ_H_ */

View File

@ -0,0 +1,88 @@
<HTML>
<HEAD><TITLE>smfi_addrcpt_par</TITLE></HEAD>
<BODY>
<!--
$Id: smfi_addrcpt_par.html,v 1.4 2007/03/19 16:38:02 ca Exp $
-->
<H1>smfi_addrcpt_par</H1>
<TABLE border="0" cellspacing=4 cellpadding=4>
<!---------- Synopsis ----------->
<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
<PRE>
#include &lt;libmilter/mfapi.h&gt;
int smfi_addrcpt_par(
SMFICTX *ctx,
char *rcpt,
char *args
);
</PRE>
Add a recipient for the current message including ESMTP arguments.
</TD></TR>
<!----------- Description ---------->
<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
<TABLE border="1" cellspacing=1 cellpadding=4>
<TR align="left" valign=top>
<TH width="80">Called When</TH>
<TD>Called only from <A href="xxfi_eom.html">xxfi_eom</A>.</TD>
</TR>
<TR align="left" valign=top>
<TH width="80">Effects</TH>
<TD>Add a recipient to the message envelope.</TD>
</TR>
</TABLE>
<!----------- Arguments ---------->
<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
<TABLE border="1" cellspacing=0>
<TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
<TR valign="top"><TD>ctx</TD>
<TD>Opaque context structure.
</TD></TR>
<TR valign="top"><TD>rcpt</TD>
<TD>The new recipient's address.
</TD></TR>
<TR valign="top"><TD>args</TD>
<TD>The new recipient's ESMTP parameters.
</TD></TR>
</TABLE>
</TD></TR>
<!----------- Return values ---------->
<TR>
<TH valign="top" align=left>RETURN VALUES</TH>
<TD>smfi_addrcpt will fail and return MI_FAILURE if:
<UL><LI>rcpt is NULL.
<LI>Adding recipients in the current connection state is invalid.
<LI>A network error occurs.
<LI>SMFIF_ADDRCPT_PAR was not set when
<A href="smfi_register.html">smfi_register</A> was called.
</UL>
Otherwise, it will return MI_SUCCESS.
</TD>
</TR>
<!----------- Notes ---------->
<TR align="left" valign=top>
<TH>NOTES</TH>
<TD>
A filter which calls smfi_addrcpt must have set the SMFIF_ADDRCPT_PAR flag
in the smfiDesc_str passed to
<A href="smfi_register.html">smfi_register</A>.
</TD>
</TR>
</TABLE>
<HR size="1">
<FONT size="-1">
Copyright (c) 2006 Sendmail, Inc. and its suppliers.
All rights reserved.
<BR>
By using this file, you agree to the terms and conditions set
forth in the LICENSE.
</FONT>
</BODY>
</HTML>

View File

@ -0,0 +1,94 @@
<HTML>
<HEAD><TITLE>smfi_chgfrom</TITLE></HEAD>
<BODY>
<!--
$Id: smfi_chgfrom.html,v 1.3 2006/12/21 18:30:35 ca Exp $
-->
<H1>smfi_chgfrom</H1>
<TABLE border="0" cellspacing=4 cellpadding=4>
<!---------- Synopsis ----------->
<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
<PRE>
#include &lt;libmilter/mfapi.h&gt;
int smfi_chgfrom(
SMFICTX *ctx,
const char *mail,
char *args
);
</PRE>
Change the envelope sender (MAIL From) of the current message.
</TD></TR>
<!----------- Description ---------->
<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
<TABLE border="1" cellspacing=1 cellpadding=4>
<TR align="left" valign=top>
<TH width="80">Called When</TH>
<TD>Called only from <A href="xxfi_eom.html">xxfi_eom</A>.</TD>
</TR>
<TR align="left" valign=top>
<TH width="80">Effects</TH>
<TD>Change the envelope sender (MAIL From) of the current message.</TD>
</TR>
</TABLE>
<!----------- Arguments ---------->
<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
<TABLE border="1" cellspacing=0>
<TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
<TR valign="top"><TD>ctx</TD>
<TD>Opaque context structure.
</TD></TR>
<TR valign="top"><TD>mail</TD>
<TD>The new sender address.
</TD></TR>
<TR valign="top"><TD>args</TD>
<TD>ESMTP arguments.
</TD></TR>
</TABLE>
</TD></TR>
<!----------- Return values ---------->
<TR>
<TH valign="top" align=left>RETURN VALUES</TH>
<TD>smfi_chgfrom will fail and return MI_FAILURE if:
<UL><LI>mail is NULL.
<LI>Changing the sender in the current connection state is invalid.
<LI>A network error occurs.
<LI>SMFIF_CHGFROM was not set when <A href="smfi_register.html">smfi_register</A> was called.
</UL>
Otherwise, it will return MI_SUCCESS.
</TD>
</TR>
<!----------- Notes ---------->
<TR align="left" valign=top>
<TH>NOTES</TH>
<TD>
A filter which calls smfi_chgfrom must have set the SMFIF_CHGFROM flag
in the smfiDesc_str passed to
<A href="smfi_register.html">smfi_register</A>.
<BR>
Even though all ESMTP arguments could be set via this call,
it does not make sense to do so for many of them,
e.g., SIZE and BODY.
Setting those may cause problems, proper care must be taken.
Moreover, there is no feedback from the MTA to the milter
whether the call was successful.
</TD>
</TR>
</TABLE>
<HR size="1">
<FONT size="-1">
Copyright (c) 2006 Sendmail, Inc. and its suppliers.
All rights reserved.
<BR>
By using this file, you agree to the terms and conditions set
forth in the LICENSE.
</FONT>
</BODY>
</HTML>

View File

@ -0,0 +1,107 @@
<HTML>
<HEAD><TITLE>smfi_setsymlist</TITLE></HEAD>
<BODY>
<!--
$Id: smfi_setsymlist.html,v 1.5 2006/12/21 18:30:35 ca Exp $
-->
<H1>smfi_setsymlist</H1>
<TABLE border="0" cellspacing=4 cellpadding=4>
<!---------- Synopsis ----------->
<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
<PRE>
#include &lt;libmilter/mfapi.h&gt;
int smfi_setsymlist(
SMFICTX *ctx,
int stage,
char *macros
);
</PRE>
Set the list of macros that the milter wants to receive from the MTA
for a protocol stage.
</TD></TR>
<!----------- Description ---------->
<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
<TABLE border="1" cellspacing=1 cellpadding=4>
<TR align="left" valign=top>
<TH width="80">Called When</TH>
<TD>This function must only be called during
<A HREF="xxfi_negotiate.html">xxfi_negotiate()</A>.
</TD>
</TR>
<TR align="left" valign=top>
<TH width="80">Effects</TH>
<TD>This function can be used to override the list of macros that the
milter wants to receive from the MTA.
</TD>
</TR>
</TABLE>
<!----------- Arguments ---------->
<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
<TABLE border="1" cellspacing=0>
<TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
<TR><TD>ctx</TD>
<TD>the opaque context structure.
</TD></TR>
<TR><TD>stage</TD>
<TD>the protocol stage during which the macro list should be used.
See the file
<CODE>include/libmilter/mfapi.h</CODE> for legal values,
look for the C macros with the prefix
<CODE>SMFIM_</CODE>.
Available protocol stages are at least
the initial connection, HELO/EHLO, MAIL, RCPT, DATA,
end of header, and
the end of a message.
</TD></TR>
<TR><TD>macros</TD>
<TD>list of macros (separated by space).
Example: "{rcpt_mailer} {rcpt_host}"
</TD></TR>
</TABLE>
</TD></TR>
<!----------- Return values ---------->
<TR>
<TH valign="top" align=left>RETURN VALUES</TH>
<TD>MI_FAILURE is returned if
<UL>
<LI>there is not enough free memory to make a copy of the macro list,
<LI><CODE>macros</CODE> is <CODE>NULL</CODE> or empty,
<LI><CODE>stage</CODE> is not a valid protocol stage,
<LI>the macro list for
<CODE>stage</CODE> has been set before.
</UL>
Otherwise MI_SUCCESS is returned.
</TD>
</TR>
<!----------- Notes ---------->
<TR align="left" valign=top>
<TH>NOTES</TH>
<TD>There is an internal limit on the number of macros that can be
set (currently 5),
however, this limit is not enforced by libmilter, only by the MTA,
but a possible violation of this restriction is not communicated back to
the milter.</TD>
</TR>
</TABLE>
<HR size="1">
<FONT size="-1">
Copyright (c) 2006 Sendmail, Inc. and its suppliers.
All rights reserved.
<BR>
By using this file, you agree to the terms and conditions set
forth in the LICENSE.
</FONT>
</BODY>
</HTML>

View File

@ -0,0 +1,86 @@
<HTML>
<HEAD><TITLE>smfi_version()</TITLE></HEAD>
<BODY>
<!--
$Id: smfi_version.html,v 1.5 2007/03/22 17:30:57 ca Exp $
-->
<H1>smfi_version()</H1>
<TABLE BORDER="0" CELLSPACING=4 CELLPADDING=4>
<!---------- Synopsis ----------->
<TR><TH VALIGN="TOP" ALIGN=LEFT WIDTH=100>SYNOPSIS</TH><TD>
<PRE>
#include &lt;libmilter/mfapi.h&gt;
int smfi_version(
unsigned int *pmajor,
unsigned int *pminor,
unsigned int *ppl
);
</PRE>
Get the (runtime) version of libmilter.
</TD></TR>
<!----------- Description ---------->
<TR><TH VALIGN="TOP" ALIGN=LEFT>DESCRIPTION</TH><TD>
<TABLE BORDER="1" CELLSPACING=1 CELLPADDING=4>
<TR ALIGN="LEFT" VALIGN=TOP>
<TH WIDTH="80">Called When</TH>
<TD>smfi_version may be called at any time.</TD>
</TR>
<TR ALIGN="LEFT" VALIGN=TOP>
<TH WIDTH="80">Effects</TH>
<TD>None.</TD>
</TR>
</TABLE>
<!----------- Arguments ---------->
<TR><TH VALIGN="TOP" ALIGN=LEFT>ARGUMENTS</TH><TD>
<TABLE BORDER="1" CELLSPACING=0>
<TR BGCOLOR="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
<TR VALIGN="TOP"><TD>pmajor</TD>
<TD>Pointer to an unsigned int variable to store major version number.
</TD></TR>
<TR VALIGN="TOP"><TD>pminor</TD>
<TD>Pointer to an unsigned int variable to store minor version number.
</TD></TR>
<TR VALIGN="TOP"><TD>ppl</TD>
<TD>Pointer to an unsigned int variable to store patch level number.
</TD></TR>
</TABLE>
</TD></TR>
<!----------- Return values ---------->
<TR>
<TH VALIGN="TOP" ALIGN=LEFT>RETURN VALUES</TH>
<TD>smfi_version returns MI_SUCCESS.</TD>
</TR>
</TABLE>
Note: the compile time version of libmilter is available in the macro
<CODE>SMFI_VERSION</CODE>.
A milter can check this macro to determine which functions to use
(at compile time via C preprocessor statements).
Using this macro and the
<CODE>smfi_version()</CODE>
function,
a milter can determine at runtime whether it has been (dynamically)
linked against the expected libmilter version.
To extract the major and minor version as well as the current patch level
from this macro, the macros
<CODE>SM_LM_VRS_MAJOR(v)</CODE>,
<CODE>SM_LM_VRS_MINOR(v)</CODE>, and
<CODE>SM_LM_VRS_PLVL(v)</CODE>
can be used, respectively.
<HR SIZE="1">
<FONT SIZE="-1">
Copyright (c) 2006, 2007 Sendmail, Inc. and its suppliers.
All rights reserved.
<BR>
By using this file, you agree to the terms and conditions set
forth in the LICENSE.
</FONT>
</BODY>
</HTML>

View File

@ -0,0 +1,89 @@
<HTML>
<HEAD><TITLE>xxfi_data</TITLE></HEAD>
<BODY>
<!--
$Id: xxfi_data.html,v 1.4 2007/01/25 01:00:20 ca Exp $
-->
<H1>xxfi_data</H1>
<TABLE border="0" cellspacing=4 cellpadding=4>
<!---------- Synopsis ----------->
<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
<PRE>
#include &lt;libmilter/mfapi.h&gt;
sfsistat (*xxfi_data)(
SMFICTX *ctx
);
</PRE>
Handle the DATA command.
</TD></TR>
<!----------- Description ---------->
<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
<TABLE border="1" cellspacing=1 cellpadding=4>
<TR align="left" valign=top>
<TH width="80">Called When</TH>
<TD>xxfi_data is called when the client uses the DATA command.
</TR>
<TR align="left" valign=top>
<TH>Default Behavior</TH>
<TD>Do nothing; return SMFIS_CONTINUE.</TD>
</TR>
</TABLE>
<!----------- Arguments ---------->
<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
<TABLE border="1" cellspacing=0>
<TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
<TR valign="top"><TD>ctx</TD>
<TD>Opaque context structure.
</TD></TR>
</TABLE>
</TD></TR>
<!----------- Return values ---------->
<TR>
<TH valign="top" align=left>SPECIAL RETURN VALUES</TH>
<TD><TABLE border="1" cellspacing=0>
<TR bgcolor="#dddddd"><TH>Return value</TH><TH>Description</TH></TR>
<TR valign="top">
<TD>SMFIS_TEMPFAIL</TD>
<TD>Reject this message with a temporary error.
</TD>
</TR>
<TR valign="top">
<TD>SMFIS_REJECT</TD>
<TD>Reject this message.
</TD>
</TR>
<TR valign="top">
<TD>SMFIS_DISCARD</TD>
<TD>Accept and silently discard this message.
</TD>
</TR>
<TR valign="top">
<TD>SMFIS_ACCEPT</TD>
<TD>Accept this message.
</TD>
</TR>
</TABLE>
</TR>
<!----------- Notes ---------->
<TR>
<TH valign="top" align=left>NOTES</TH>
<TD>For more details on ESMTP responses, please see RFC
<A href="http://www.rfc-editor.org/rfc/rfc1869.txt">1869</A>.</TD>
</TR>
</TABLE>
<HR size="1">
<FONT size="-1">
Copyright (c) 2006 Sendmail, Inc. and its suppliers.
All rights reserved.
<BR>
By using this file, you agree to the terms and conditions set
forth in the LICENSE.
</FONT>
</BODY>
</HTML>

View File

@ -0,0 +1,277 @@
<HTML>
<HEAD><TITLE>xxfi_negotiate</TITLE></HEAD>
<BODY>
<!--
$Id: xxfi_negotiate.html,v 1.23 2006/12/20 18:57:08 ca Exp $
-->
<H1>xxfi_negotiate</H1>
<TABLE border="0" cellspacing=4 cellpadding=4>
<!---------- Synopsis ----------->
<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
<PRE>
#include &lt;libmilter/mfapi.h&gt;
#include &lt;libmilter/mfdef.h&gt;
sfsistat (*xxfi_negotiate)(
SMFICTX *ctx,
unsigned long f0,
unsigned long f1,
unsigned long f2,
unsigned long f3,
unsigned long *pf0,
unsigned long *pf1,
unsigned long *pf2,
unsigned long *pf3);
</PRE>
</TD></TR>
<!----------- Description ---------->
<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
<TABLE border="1" cellspacing=1 cellpadding=4>
<TR>
<TH valign="top" align=left width=80>Called When</TH>
<TD>Once, at the start of each SMTP connection.</TD>
</TR>
<TR>
<TH valign="top" align=left width=80>Default Behavior</TH>
<TD>Return SMFIS_ALL_OPTS to change nothing.</TD>
</TR>
</TABLE>
<!----------- Arguments ---------->
<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
<TABLE border="1" cellspacing=0>
<TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
<TR><TD>ctx</TD>
<TD>the opaque context structure.
</TD></TR>
<TR><TD>f0</TD>
<TD>the actions offered by the MTA.
</TD></TR>
<TR><TD>f1</TD>
<TD>the protocol steps offered by the MTA.
<TR><TD>f2</TD>
<TD>for future extensions.
</TD></TR>
<TR><TD>f3</TD>
<TD>for future extensions.
</TD></TR>
<TR><TD>pf0</TD>
<TD>the actions requested by the milter.
</TD></TR>
<TR><TD>pf1</TD>
<TD>the protocol steps requested by the milter.
<TR><TD>pf2</TD>
<TD>for future extensions.
</TD></TR>
<TR><TD>pf3</TD>
<TD>for future extensions.
</TD></TR>
</TABLE>
</TD></TR>
<!----------- Return values ---------->
<TR>
<TH valign="top" align=left>SPECIAL RETURN VALUES</TH>
<TD><TABLE border="1" cellspacing=0>
<TR bgcolor="#dddddd"><TH>Return value</TH><TH>Description</TH></TR>
<TR valign="top">
<TD>SMFIS_ALL_OPTS</TD>
<TD>
If a milter just wants to inspect the available protocol steps
and actions, then it can return
SMFIS_ALL_OPTS
and the MTA will make all protocol steps and actions available
to the milter.
In this case, no values should be assigned to the output parameters
<CODE>pf0</CODE> - <CODE>pf3</CODE>
as they will be ignored.
</TD>
</TR>
<TR valign="top">
<TD>SMFIS_REJECT</TD>
<TD>Milter startup fails and it will not be contacted again
(for the current connection).
</TD>
</TR>
<TR valign="top">
<TD>SMFIS_CONTINUE</TD>
<TD>Continue processing.
In this case the milter <EM>must</EM> set all output parameters
<CODE>pf0</CODE> - <CODE>pf3</CODE>.
See below for an explanation how to set those output parameters.
</TD>
</TR>
</TABLE>
</TR>
<!----------- Notes ---------->
<TR>
<TH valign="top" align=left>NOTES</TH>
<TD>This function allows a milter to dynamically determine and
request operations and actions during startup.
In previous versions, the actions (f0) were fixed in the
<A HREF="smfi_register.html#flags">flags</A> field of the
<A HREF="smfi_register.html#smfiDesc">smfiDesc</A>
structure
and the protocol steps (f1) were implicitly derived by checking whether
a callback was defined.
Due to the extensions in the new milter version,
such a static selection will not work if a milter requires
new actions that are not available when talking to an older MTA.
Hence in the negotiation callback a milter can determine
which operations are available and dynamically select
those which it needs and which are offered.
If some operations are not available, the milter may either fall back
to an older mode or abort the session and ask the user to upgrade.
<!--
<P>
The protocol steps are defined in
<CODE>include/libmilter/mfdef.h</CODE>:
the macros start with
<CODE>SMFIP_</CODE>.
-->
<P>
Protocol steps
(<CODE>f1</CODE>, <CODE>*pf1</CODE>):
<UL>
<LI><A NAME="SMFIP_RCPT_REJ"><CODE>SMFIP_RCPT_REJ</CODE></A>:
By setting this bit, a milter can request that the
MTA should also send <CODE>RCPT</CODE> commands that have been rejected
because the user is unknown (or similar reasons), but not those
which have been rejected because of syntax errors etc.
If a milter requests this protocol step,
then it should check the macro
<CODE>{rcpt_mailer}</CODE>:
if that is set to
<CODE>error</CODE>,
then the recipient will be rejected by the MTA.
Usually the macros
<CODE>{rcpt_host}</CODE> and <CODE>{rcpt_addr}</CODE>
will contain an enhanced status code and an error text
in that case, respectively.
<LI><A NAME="SMFIP_SKIP"><CODE>SMFIP_SKIP</CODE></A>
indicates that the MTA understand the
<A HREF="api.html#SMFIS_SKIP">SMFIS_SKIP</A>
return code.
<LI><A NAME="SMFIP_NR_"><CODE>SMFIP_NR_*</CODE></A>
indicates that the MTA understand the
<A HREF="api.html#SMFIS_NOREPLY">SMFIS_NOREPLY</A>
return code.
There are flags for various protocol stages:
<UL>
<LI><A NAME="SMFIP_NR_CONN"><CODE>SMFIP_NR_CONN</CODE></A>:
<A HREF="xxfi_connect.html">xxfi_connect()</A>
<LI><A NAME="SMFIP_NR_HELO"><CODE>SMFIP_NR_HELO</CODE></A>:
<A HREF="xxfi_helo.html">xxfi_helo()</A>
<LI><A NAME="SMFIP_NR_MAIL"><CODE>SMFIP_NR_MAIL</CODE></A>:
<A HREF="xxfi_envfrom.html">xxfi_envfrom()</A>
<LI><A NAME="SMFIP_NR_RCPT"><CODE>SMFIP_NR_RCPT</CODE></A>:
<A HREF="xxfi_envrcpt.html">xxfi_envrcpt()</A>
<LI><A NAME="SMFIP_NR_DATA"><CODE>SMFIP_NR_DATA</CODE></A>:
<A HREF="xxfi_data.html">xxfi_data()</A>
<LI><A NAME="SMFIP_NR_UNKN"><CODE>SMFIP_NR_UNKN</CODE></A>:
<A HREF="xxfi_unknown.html">xxfi_unknown()</A>
<LI><A NAME="SMFIP_NR_EOH"><CODE>SMFIP_NR_EOH</CODE></A>:
<A HREF="xxfi_eoh.html">xxfi_eoh()</A>
<LI><A NAME="SMFIP_NR_BODY"><CODE>SMFIP_NR_BODY</CODE></A>:
<A HREF="xxfi_body.html">xxfi_body()</A>
<LI><A NAME="SMFIP_NR_HDR"><CODE>SMFIP_NR_HDR</CODE></A>:
<A HREF="xxfi_header.html">xxfi_header()</A>
</UL>
<LI><A NAME="SMFIP_HDR_LEADSPC"><CODE>SMFIP_HDR_LEADSPC</CODE></A>
indicates that the MTA can send header values with leading space intact.
If this protocol step is requested, then the MTA will also not add a leading
space to headers when they are added, inserted, or changed.
<!--
:'a,.s;^#define \(SMFIP_NO[A-Z]*\)[ ].*;<LI><A NAME="\1"><CODE>\1</CODE></A>:;
-->
<LI>The MTA can be instructed not to send information about
various SMTP stages, these flags start with:
<A NAME="SMFIP_NO"><CODE>SMFIP_NO*</CODE></A>.
<UL>
<LI><A NAME="SMFIP_NOCONNECT"><CODE>SMFIP_NOCONNECT</CODE></A>:
<A HREF="xxfi_connect.html">xxfi_connect()</A>
<LI><A NAME="SMFIP_NOHELO"><CODE>SMFIP_NOHELO</CODE></A>:
<A HREF="xxfi_header.html">xxfi_header()</A>
<LI><A NAME="SMFIP_NOMAIL"><CODE>SMFIP_NOMAIL</CODE></A>:
<A HREF="xxfi_envfrom.html">xxfi_envfrom()</A>
<LI><A NAME="SMFIP_NORCPT"><CODE>SMFIP_NORCPT</CODE></A>:
<A HREF="xxfi_envrcpt.html">xxfi_envrcpt()</A>
<LI><A NAME="SMFIP_NOBODY"><CODE>SMFIP_NOBODY</CODE></A>:
<A HREF="xxfi_body.html">xxfi_body()</A>
<LI><A NAME="SMFIP_NOHDRS"><CODE>SMFIP_NOHDRS</CODE></A>:
<A HREF="xxfi_header.html">xxfi_header()</A>
<LI><A NAME="SMFIP_NOEOH"><CODE>SMFIP_NOEOH</CODE></A>:
<A HREF="xxfi_eoh.html">xxfi_eoh()</A>
<LI><A NAME="SMFIP_NOUNKNOWN"><CODE>SMFIP_NOUNKNOWN</CODE></A>:
<A HREF="xxfi_unknown.html">xxfi_unknown()</A>
<LI><A NAME="SMFIP_NODATA"><CODE>SMFIP_NODATA</CODE></A>:
<A HREF="xxfi_data.html">xxfi_data()</A>
</UL>
For each of these xxfi_* callbacks that a milter does not use
the corresponding flag <EM>should</EM> be set in
<CODE>*pf1</CODE>.
</UL>
<P>
The available actions
(<CODE>f0</CODE>, <CODE>*pf0</CODE>)
are
<!--
defined in
<CODE>include/libmilter/mfapi.h</CODE>:
the macros start with
<CODE>SMFIF_</CODE>;
these are
-->
described
<A HREF="smfi_register.html#flags">elsewhere (xxfi_flags)</A>.
<P>
If a milter returns SMFIS_CONTINUE, then it <EM>must</EM>
set the desired actions and protocol steps
via the (output) parameters
<CODE>pf0</CODE>
and
<CODE>pf1</CODE>
(which correspond to
<CODE>f0</CODE>
and
<CODE>f1</CODE>, respectively).
The (output) parameters
<CODE>pf2</CODE> and
<CODE>pf3</CODE>
should be set to 0 for compatibility with future versions.
</TD>
</TR>
</TABLE>
<HR size="1">
<FONT size="-1">
Copyright (c) 2006 Sendmail, Inc. and its suppliers.
All rights reserved.
<BR>
By using this file, you agree to the terms and conditions set
forth in the LICENSE.
</FONT>
</BODY>
</HTML>

View File

@ -0,0 +1,84 @@
<HTML>
<HEAD><TITLE>xxfi_unknown</TITLE></HEAD>
<BODY>
<!--
$Id: xxfi_unknown.html,v 1.3 2006/12/21 18:30:36 ca Exp $
-->
<H1>xxfi_unknown</H1>
<TABLE border="0" cellspacing=4 cellpadding=4>
<!---------- Synopsis ----------->
<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
<PRE>
#include &lt;libmilter/mfapi.h&gt;
sfsistat (*xxfi_unknown)(
SMFICTX *ctx,
const char *arg
);
</PRE>
Handle unknown and unimplemented SMTP commands.
</TD></TR>
<!----------- Description ---------->
<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
<TABLE border="1" cellspacing=1 cellpadding=4>
<TR align="left" valign=top>
<TH width="80">Called When</TH>
<TD>xxfi_unknown is called when the client uses an SMTP command
that is either unknown or not implemented by the MTA.
</TR>
<TR align="left" valign=top>
<TH>Default Behavior</TH>
<TD>Do nothing; return SMFIS_CONTINUE.</TD>
</TR>
</TABLE>
<!----------- Arguments ---------->
<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
<TABLE border="1" cellspacing=0>
<TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
<TR valign="top"><TD>ctx</TD>
<TD>Opaque context structure.
</TD></TR>
<TR valign="top"><TD>arg</TD>
<TD>SMTP command including all arguments.
</TD></TR>
</TABLE>
</TD></TR>
<!----------- Return values ---------->
<TR>
<TH valign="top" align=left>SPECIAL RETURN VALUES</TH>
<TD><TABLE border="1" cellspacing=0>
<TR bgcolor="#dddddd"><TH>Return value</TH><TH>Description</TH></TR>
<TR valign="top">
<TD>SMFIS_TEMPFAIL</TD>
<TD>Reject this message with a temporary error.
</TD>
</TR>
<TR valign="top">
<TD>SMFIS_REJECT</TD>
<TD>Reject this message.
</TD>
</TR>
</TABLE>
</TR>
<!----------- Notes ---------->
<TR>
<TH valign="top" align=left>NOTES</TH>
<TD>The SMTP command will always be rejected by the server,
it is only possible to return a different error code.
</TR>
</TABLE>
<HR size="1">
<FONT size="-1">
Copyright (c) 2006 Sendmail, Inc. and its suppliers.
All rights reserved.
<BR>
By using this file, you agree to the terms and conditions set
forth in the LICENSE.
</FONT>
</BODY>
</HTML>

View File

@ -0,0 +1,298 @@
/*
* Copyright (c) 2006 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.
*
* $Id: example.c,v 8.3 2006/12/20 21:22:34 ca Exp $
*/
/*
** A trivial example filter that logs all email to a file.
** This milter also has some callbacks which it does not really use,
** but they are defined to serve as an example.
*/
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include "libmilter/mfapi.h"
#include "libmilter/mfdef.h"
#ifndef true
# define false 0
# define true 1
#endif /* ! true */
struct mlfiPriv
{
char *mlfi_fname;
FILE *mlfi_fp;
};
#define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx))
static unsigned long mta_caps = 0;
sfsistat
mlfi_cleanup(ctx, ok)
SMFICTX *ctx;
bool ok;
{
sfsistat rstat = SMFIS_CONTINUE;
struct mlfiPriv *priv = MLFIPRIV;
char *p;
char host[512];
char hbuf[1024];
if (priv == NULL)
return rstat;
/* close the archive file */
if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF)
{
/* failed; we have to wait until later */
rstat = SMFIS_TEMPFAIL;
(void) unlink(priv->mlfi_fname);
}
else if (ok)
{
/* add a header to the message announcing our presence */
if (gethostname(host, sizeof host) < 0)
snprintf(host, sizeof host, "localhost");
p = strrchr(priv->mlfi_fname, '/');
if (p == NULL)
p = priv->mlfi_fname;
else
p++;
snprintf(hbuf, sizeof hbuf, "%s@%s", p, host);
smfi_addheader(ctx, "X-Archived", hbuf);
}
else
{
/* message was aborted -- delete the archive file */
(void) unlink(priv->mlfi_fname);
}
/* release private memory */
free(priv->mlfi_fname);
free(priv);
smfi_setpriv(ctx, NULL);
/* return status */
return rstat;
}
sfsistat
mlfi_envfrom(ctx, envfrom)
SMFICTX *ctx;
char **envfrom;
{
struct mlfiPriv *priv;
int fd = -1;
/* allocate some private memory */
priv = malloc(sizeof *priv);
if (priv == NULL)
{
/* can't accept this message right now */
return SMFIS_TEMPFAIL;
}
memset(priv, '\0', sizeof *priv);
/* open a file to store this message */
priv->mlfi_fname = strdup("/tmp/msg.XXXXXXXX");
if (priv->mlfi_fname == NULL)
{
free(priv);
return SMFIS_TEMPFAIL;
}
if ((fd = mkstemp(priv->mlfi_fname)) < 0 ||
(priv->mlfi_fp = fdopen(fd, "w+")) == NULL)
{
if (fd >= 0)
(void) close(fd);
free(priv->mlfi_fname);
free(priv);
return SMFIS_TEMPFAIL;
}
/* save the private data */
smfi_setpriv(ctx, priv);
/* continue processing */
return SMFIS_CONTINUE;
}
sfsistat
mlfi_header(ctx, headerf, headerv)
SMFICTX *ctx;
char *headerf;
char *headerv;
{
/* write the header to the log file */
fprintf(MLFIPRIV->mlfi_fp, "%s: %s\r\n", headerf, headerv);
/* continue processing */
return ((mta_caps & SMFIP_NR_HDR) != 0)
? SMFIS_NOREPLY : SMFIS_CONTINUE;
}
sfsistat
mlfi_eoh(ctx)
SMFICTX *ctx;
{
/* output the blank line between the header and the body */
fprintf(MLFIPRIV->mlfi_fp, "\r\n");
/* continue processing */
return SMFIS_CONTINUE;
}
sfsistat
mlfi_body(ctx, bodyp, bodylen)
SMFICTX *ctx;
u_char *bodyp;
size_t bodylen;
{
/* output body block to log file */
if (fwrite(bodyp, bodylen, 1, MLFIPRIV->mlfi_fp) <= 0)
{
/* write failed */
(void) mlfi_cleanup(ctx, false);
return SMFIS_TEMPFAIL;
}
/* continue processing */
return SMFIS_CONTINUE;
}
sfsistat
mlfi_eom(ctx)
SMFICTX *ctx;
{
return mlfi_cleanup(ctx, true);
}
sfsistat
mlfi_close(ctx)
SMFICTX *ctx;
{
return SMFIS_ACCEPT;
}
sfsistat
mlfi_abort(ctx)
SMFICTX *ctx;
{
return mlfi_cleanup(ctx, false);
}
sfsistat
mlfi_unknown(ctx, cmd)
SMFICTX *ctx;
char *cmd;
{
return SMFIS_CONTINUE;
}
sfsistat
mlfi_data(ctx)
SMFICTX *ctx;
{
return SMFIS_CONTINUE;
}
sfsistat
mlfi_negotiate(ctx, f0, f1, f2, f3, pf0, pf1, pf2, pf3)
SMFICTX *ctx;
unsigned long f0;
unsigned long f1;
unsigned long f2;
unsigned long f3;
unsigned long *pf0;
unsigned long *pf1;
unsigned long *pf2;
unsigned long *pf3;
{
/* milter actions: add headers */
*pf0 = SMFIF_ADDHDRS;
/* milter protocol steps: all but connect, HELO, RCPT */
*pf1 = SMFIP_NOCONNECT|SMFIP_NOHELO|SMFIP_NORCPT;
mta_caps = f1;
if ((mta_caps & SMFIP_NR_HDR) != 0)
*pf1 |= SMFIP_NR_HDR;
*pf2 = 0;
*pf3 = 0;
return SMFIS_CONTINUE;
}
struct smfiDesc smfilter =
{
"SampleFilter", /* filter name */
SMFI_VERSION, /* version code -- do not change */
SMFIF_ADDHDRS, /* flags */
NULL, /* connection info filter */
NULL, /* SMTP HELO command filter */
mlfi_envfrom, /* envelope sender filter */
NULL, /* envelope recipient filter */
mlfi_header, /* header filter */
mlfi_eoh, /* end of header */
mlfi_body, /* body block filter */
mlfi_eom, /* end of message */
mlfi_abort, /* message aborted */
mlfi_close, /* connection cleanup */
mlfi_unknown, /* unknown/unimplemented SMTP commands */
mlfi_data, /* DATA command filter */
mlfi_negotiate /* option negotation at connection startup */
};
int
main(argc, argv)
int argc;
char *argv[];
{
bool setconn;
int c;
setconn = false;
/* Process command line options */
while ((c = getopt(argc, argv, "p:")) != -1)
{
switch (c)
{
case 'p':
if (optarg == NULL || *optarg == '\0')
{
(void) fprintf(stderr, "Illegal conn: %s\n",
optarg);
exit(EX_USAGE);
}
(void) smfi_setconn(optarg);
setconn = true;
break;
}
}
if (!setconn)
{
fprintf(stderr, "%s: Missing required -p argument\n", argv[0]);
exit(EX_USAGE);
}
if (smfi_register(smfilter) == MI_FAILURE)
{
fprintf(stderr, "smfi_register failed\n");
exit(EX_UNAVAILABLE);
}
return smfi_main();
}

View File

@ -0,0 +1,225 @@
/*
* Copyright (c) 2006 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.
*
*/
#include "libmilter.h"
#if _FFR_THREAD_MONITOR
/*
** Thread Monitoring
** Todo: more error checking (return code from function calls)
** add comments.
*/
bool Monitor = false; /* use monitoring? */
static unsigned int Mon_exec_time = 0;
/* mutex protects Mon_cur_ctx, Mon_ctx_head, and ctx_start */
static smutex_t Mon_mutex;
static scond_t Mon_cv;
/*
** Current ctx to monitor.
** Invariant:
** Mon_cur_ctx == NULL || Mon_cur_ctx is thread which was started the longest
** time ago.
**
** Basically the entries in the list are ordered by time because new
** entries are appended at the end. However, due to the concurrent
** execution (multi-threaded) and no guaranteed order of wakeups
** after a mutex_lock() attempt, the order might not be strict,
** i.e., if the list contains e1 and e2 (in that order) then
** the the start time of e2 can be (slightly) smaller than that of e1.
** However, this slight inaccurracy should not matter for the proper
** working of this algorithm.
*/
static SMFICTX_PTR Mon_cur_ctx = NULL;
static smfi_hd_T Mon_ctx_head; /* head of the linked list of active contexts */
/*
** SMFI_SET_MAX_EXEC_TIME -- set maximum execution time for a thread
**
** Parameters:
** tm -- maximum execution time for a thread
**
** Returns:
** MI_SUCCESS
*/
int
smfi_set_max_exec_time(tm)
unsigned int tm;
{
Mon_exec_time = tm;
return MI_SUCCESS;
}
/*
** MI_MONITOR_THREAD -- monitoring thread
**
** Parameters:
** arg -- ignored (required by pthread_create())
**
** Returns:
** NULL on termination.
*/
static void *
mi_monitor_thread(arg)
void *arg;
{
sthread_t tid;
int r;
time_t now, end;
SM_ASSERT(Monitor);
SM_ASSERT(Mon_exec_time > 0);
tid = (sthread_t) sthread_get_id();
if (pthread_detach(tid) != 0)
{
/* log an error */
return (void *)1;
}
/*
** NOTE: this is "flow through" code,
** do NOT use do { } while ("break" is used here!)
*/
#define MON_CHK_STOP \
now = time(NULL); \
end = Mon_cur_ctx->ctx_start + Mon_exec_time; \
if (now > end) \
{ \
smi_log(SMI_LOG_ERR, \
"WARNING: monitor timeout triggered, now=%ld, end=%ld, tid=%ld, state=0x%x",\
(long) now, (long) end, \
(long) Mon_cur_ctx->ctx_id, Mon_cur_ctx->ctx_state);\
mi_stop_milters(MILTER_STOP); \
break; \
}
(void) smutex_lock(&Mon_mutex);
while (mi_stop() == MILTER_CONT)
{
if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0)
{
struct timespec abstime;
MON_CHK_STOP;
abstime.tv_sec = end;
abstime.tv_nsec = 0;
r = pthread_cond_timedwait(&Mon_cv, &Mon_mutex,
&abstime);
}
else
r = pthread_cond_wait(&Mon_cv, &Mon_mutex);
if (mi_stop() != MILTER_CONT)
break;
if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0)
{
MON_CHK_STOP;
}
}
(void) smutex_unlock(&Mon_mutex);
return NULL;
}
/*
** MI_MONITOR_INIT -- initialize monitoring thread
**
** Parameters: none
**
** Returns:
** MI_SUCCESS/MI_FAILURE
*/
int
mi_monitor_init()
{
int r;
sthread_t tid;
SM_ASSERT(!Monitor);
if (Mon_exec_time <= 0)
return MI_SUCCESS;
Monitor = true;
if (!smutex_init(&Mon_mutex))
return MI_FAILURE;
if (scond_init(&Mon_cv) != 0)
return MI_FAILURE;
SM_TAILQ_INIT(&Mon_ctx_head);
r = thread_create(&tid, mi_monitor_thread, (void *)NULL);
if (r != 0)
return r;
return MI_SUCCESS;
}
/*
** MI_MONITOR_WORK_BEGIN -- record start of thread execution
**
** Parameters:
** ctx -- session context
** cmd -- milter command char
**
** Returns:
** 0
*/
int
mi_monitor_work_begin(ctx, cmd)
SMFICTX_PTR ctx;
int cmd;
{
(void) smutex_lock(&Mon_mutex);
if (NULL == Mon_cur_ctx)
{
Mon_cur_ctx = ctx;
(void) scond_signal(&Mon_cv);
}
ctx->ctx_start = time(NULL);
SM_TAILQ_INSERT_TAIL(&Mon_ctx_head, ctx, ctx_mon_link);
(void) smutex_unlock(&Mon_mutex);
return 0;
}
/*
** MI_MONITOR_WORK_END -- record end of thread execution
**
** Parameters:
** ctx -- session context
** cmd -- milter command char
**
** Returns:
** 0
*/
int
mi_monitor_work_end(ctx, cmd)
SMFICTX_PTR ctx;
int cmd;
{
(void) smutex_lock(&Mon_mutex);
ctx->ctx_start = 0;
SM_TAILQ_REMOVE(&Mon_ctx_head, ctx, ctx_mon_link);
if (Mon_cur_ctx == ctx)
{
if (SM_TAILQ_EMPTY(&Mon_ctx_head))
Mon_cur_ctx = NULL;
else
Mon_cur_ctx = SM_TAILQ_FIRST(&Mon_ctx_head);
}
(void) smutex_unlock(&Mon_mutex);
return 0;
}
#endif /* _FFR_THREAD_MONITOR */

View File

@ -0,0 +1,792 @@
/*
* Copyright (c) 2003-2004, 2006 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.
*
* Contributed by Jose Marcio Martins da Cruz - Ecole des Mines de Paris
* Jose-Marcio.Martins@ensmp.fr
*/
#include <sm/gen.h>
SM_RCSID("@(#)$Id: worker.c,v 8.9 2006/12/18 18:26:51 ca Exp $")
#include "libmilter.h"
#if _FFR_WORKERS_POOL
typedef struct taskmgr_S taskmgr_T;
#define TM_SIGNATURE 0x23021957
struct taskmgr_S
{
long tm_signature; /* has the controller been initialized */
sthread_t tm_tid; /* thread id of controller */
smfi_hd_T tm_ctx_head; /* head of the linked list of contexts */
int tm_nb_workers; /* number of workers in the pool */
int tm_nb_idle; /* number of workers waiting */
int tm_p[2]; /* poll control pipe */
smutex_t tm_w_mutex; /* linked list access mutex */
scond_t tm_w_cond; /* */
};
static taskmgr_T Tskmgr = {0};
#define WRK_CTX_HEAD Tskmgr.tm_ctx_head
#define RD_PIPE (Tskmgr.tm_p[0])
#define WR_PIPE (Tskmgr.tm_p[1])
#define PIPE_SEND_SIGNAL() \
do \
{ \
char evt = 0x5a; \
int fd = WR_PIPE; \
if (write(fd, &evt, sizeof(evt)) != sizeof(evt)) \
smi_log(SMI_LOG_ERR, \
"Error writing to event pipe: %s", \
sm_errstring(errno)); \
} while (0)
#ifndef USE_PIPE_WAKE_POLL
# define USE_PIPE_WAKE_POLL 1
#endif /* USE_PIPE_WAKE_POLL */
/* poll check periodicity (default 10000 - 10 s) */
#define POLL_TIMEOUT 10000
/* worker conditional wait timeout (default 10 s) */
#define COND_TIMEOUT 10
/* functions */
static int mi_close_session __P((SMFICTX_PTR));
static void *mi_worker __P((void *));
static void *mi_pool_controller __P((void *));
static int mi_list_add_ctx __P((SMFICTX_PTR));
static int mi_list_del_ctx __P((SMFICTX_PTR));
/*
** periodicity of cleaning up old sessions (timedout)
** sessions list will be checked to find old inactive
** sessions each DT_CHECK_OLD_SESSIONS sec
*/
#define DT_CHECK_OLD_SESSIONS 600
#ifndef OLD_SESSION_TIMEOUT
# define OLD_SESSION_TIMEOUT ctx->ctx_timeout
#endif /* OLD_SESSION_TIMEOUT */
/* session states - with respect to the pool of workers */
#define WKST_INIT 0 /* initial state */
#define WKST_READY_TO_RUN 1 /* command ready do be read */
#define WKST_RUNNING 2 /* session running on a worker */
#define WKST_READY_TO_WAIT 3 /* session just finished by a worker */
#define WKST_WAITING 4 /* waiting for new command */
#define WKST_CLOSING 5 /* session finished */
#ifndef MIN_WORKERS
# define MIN_WORKERS 2 /* minimum number of threads to keep around */
#endif
#define MIN_IDLE 1 /* minimum number of idle threads */
/*
** Macros for threads and mutex management
*/
#define TASKMGR_LOCK() \
do \
{ \
if (!smutex_lock(&Tskmgr.tm_w_mutex)) \
smi_log(SMI_LOG_ERR, "TASKMGR_LOCK error"); \
} while (0)
#define TASKMGR_UNLOCK() \
do \
{ \
if (!smutex_unlock(&Tskmgr.tm_w_mutex)) \
smi_log(SMI_LOG_ERR, "TASKMGR_UNLOCK error"); \
} while (0)
#define TASKMGR_COND_WAIT() \
scond_timedwait(&Tskmgr.tm_w_cond, &Tskmgr.tm_w_mutex, COND_TIMEOUT)
#define TASKMGR_COND_SIGNAL() \
do \
{ \
if (scond_signal(&Tskmgr.tm_w_cond) != 0) \
smi_log(SMI_LOG_ERR, "TASKMGR_COND_SIGNAL error"); \
} while (0)
#define LAUNCH_WORKER(ctx) \
do \
{ \
int r; \
sthread_t tid; \
\
if ((r = thread_create(&tid, mi_worker, ctx)) != 0) \
smi_log(SMI_LOG_ERR, "LAUNCH_WORKER error: %s",\
sm_errstring(r)); \
} while (0)
#if POOL_DEBUG
# define POOL_LEV_DPRINTF(lev, x) \
do { \
if ((lev) < ctx->ctx_dbg) \
sm_dprintf x; \
} while (0)
#else /* POOL_DEBUG */
# define POOL_LEV_DPRINTF(lev, x)
#endif /* POOL_DEBUG */
/*
** MI_START_SESSION -- Start a session in the pool of workers
**
** Parameters:
** ctx -- context structure
**
** Returns:
** MI_SUCCESS/MI_FAILURE
*/
int
mi_start_session(ctx)
SMFICTX_PTR ctx;
{
static long id = 0;
SM_ASSERT(Tskmgr.tm_signature == TM_SIGNATURE);
SM_ASSERT(ctx != NULL);
POOL_LEV_DPRINTF(4, ("PIPE r=[%d] w=[%d]", RD_PIPE, WR_PIPE));
TASKMGR_LOCK();
if (mi_list_add_ctx(ctx) != MI_SUCCESS)
{
TASKMGR_UNLOCK();
return MI_FAILURE;
}
ctx->ctx_sid = id++;
/* if there is an idle worker, signal it, otherwise start new worker */
if (Tskmgr.tm_nb_idle > 0)
{
ctx->ctx_wstate = WKST_READY_TO_RUN;
TASKMGR_COND_SIGNAL();
}
else
{
ctx->ctx_wstate = WKST_RUNNING;
LAUNCH_WORKER(ctx);
}
TASKMGR_UNLOCK();
return MI_SUCCESS;
}
/*
** MI_CLOSE_SESSION -- Close a session and clean up data structures
**
** Parameters:
** ctx -- context structure
**
** Returns:
** MI_SUCCESS/MI_FAILURE
*/
static int
mi_close_session(ctx)
SMFICTX_PTR ctx;
{
SM_ASSERT(ctx != NULL);
(void) mi_list_del_ctx(ctx);
if (ValidSocket(ctx->ctx_sd))
{
(void) closesocket(ctx->ctx_sd);
ctx->ctx_sd = INVALID_SOCKET;
}
if (ctx->ctx_reply != NULL)
{
free(ctx->ctx_reply);
ctx->ctx_reply = NULL;
}
if (ctx->ctx_privdata != NULL)
{
smi_log(SMI_LOG_WARN, "%s: private data not NULL",
ctx->ctx_smfi->xxfi_name);
}
mi_clr_macros(ctx, 0);
free(ctx);
return MI_SUCCESS;
}
/*
** MI_POOL_CONTROLER_INIT -- Launch the worker pool controller
** Must be called before starting sessions.
**
** Parameters:
** none
**
** Returns:
** MI_SUCCESS/MI_FAILURE
*/
int
mi_pool_controller_init()
{
sthread_t tid;
int r, i;
if (Tskmgr.tm_signature == TM_SIGNATURE)
return MI_SUCCESS;
SM_TAILQ_INIT(&WRK_CTX_HEAD);
Tskmgr.tm_tid = (sthread_t) -1;
Tskmgr.tm_nb_workers = 0;
Tskmgr.tm_nb_idle = 0;
if (pipe(Tskmgr.tm_p) != 0)
{
smi_log(SMI_LOG_ERR, "can't create event pipe: %s",
sm_errstring(r));
return MI_FAILURE;
}
POOL_LEV_DPRINTF(4, ("PIPE r=[%d] w=[%d]", RD_PIPE, WR_PIPE));
(void) smutex_init(&Tskmgr.tm_w_mutex);
(void) scond_init(&Tskmgr.tm_w_cond);
/* Launch the pool controller */
if ((r = thread_create(&tid, mi_pool_controller, (void *) NULL)) != 0)
{
smi_log(SMI_LOG_ERR, "can't create controller thread: %s",
sm_errstring(r));
return MI_FAILURE;
}
Tskmgr.tm_tid = tid;
Tskmgr.tm_signature = TM_SIGNATURE;
/* Create the pool of workers */
for (i = 0; i < MIN_WORKERS; i++)
{
if ((r = thread_create(&tid, mi_worker, (void *) NULL)) != 0)
{
smi_log(SMI_LOG_ERR, "can't create workers crew: %s",
sm_errstring(r));
return MI_FAILURE;
}
}
return MI_SUCCESS;
}
/*
** MI_POOL_CONTROLLER -- manage the pool of workers
** This thread must be running when listener begins
** starting sessions
**
** Parameters:
** arg -- unused
**
** Returns:
** NULL
**
** Control flow:
** for (;;)
** Look for timed out sessions
** Select sessions to wait for sendmail command
** Poll set of file descriptors
** if timeout
** continue
** For each file descriptor ready
** launch new thread if no worker available
** else
** signal waiting worker
*/
/* Poll structure array (pollfd) size step */
#define PFD_STEP 256
#define WAIT_FD(i) (pfd[i].fd)
#define WAITFN "POLL"
static void *
mi_pool_controller(arg)
void *arg;
{
struct pollfd *pfd = NULL;
int dim_pfd = 0;
bool rebuild_set = true;
int pcnt = 0; /* error count for poll() failures */
Tskmgr.tm_tid = sthread_get_id();
if (pthread_detach(Tskmgr.tm_tid) != 0)
{
smi_log(SMI_LOG_ERR, "Failed to detach pool controller thread");
return NULL;
}
pfd = (struct pollfd *) malloc(PFD_STEP * sizeof(struct pollfd));
if (pfd == NULL)
{
smi_log(SMI_LOG_ERR, "Failed to malloc pollfd array: %s",
sm_errstring(errno));
return NULL;
}
dim_pfd = PFD_STEP;
for (;;)
{
SMFICTX_PTR ctx;
int nfd, rfd, i;
time_t now;
time_t lastcheck;
POOL_LEV_DPRINTF(4, ("Let's %s again...", WAITFN));
if (mi_stop() != MILTER_CONT)
break;
TASKMGR_LOCK();
now = time(NULL);
/* check for timed out sessions? */
if (lastcheck + DT_CHECK_OLD_SESSIONS < now)
{
SM_TAILQ_FOREACH(ctx, &WRK_CTX_HEAD, ctx_link)
{
if (ctx->ctx_wstate == WKST_WAITING)
{
if (ctx->ctx_wait == 0)
{
ctx->ctx_wait = now;
continue;
}
/* if session timed out, close it */
if (ctx->ctx_wait + OLD_SESSION_TIMEOUT
< now)
{
sfsistat (*fi_close) __P((SMFICTX *));
POOL_LEV_DPRINTF(4,
("Closing old connection: sd=%d id=%d",
ctx->ctx_sd,
ctx->ctx_sid));
if ((fi_close = ctx->ctx_smfi->xxfi_close) != NULL)
(void) (*fi_close)(ctx);
mi_close_session(ctx);
ctx = SM_TAILQ_FIRST(&WRK_CTX_HEAD);
continue;
}
}
}
lastcheck = now;
}
if (rebuild_set)
{
/*
** Initialize poll set.
** Insert into the poll set the file descriptors of
** all sessions waiting for a command from sendmail.
*/
nfd = 0;
/* begin with worker pipe */
pfd[nfd].fd = RD_PIPE;
pfd[nfd].events = MI_POLL_RD_FLAGS;
pfd[nfd].revents = 0;
nfd++;
SM_TAILQ_FOREACH(ctx, &WRK_CTX_HEAD, ctx_link)
{
/*
** update ctx_wait - start of wait moment -
** for timeout
*/
if (ctx->ctx_wstate == WKST_READY_TO_WAIT)
ctx->ctx_wait = now;
/* add the session to the pollfd array? */
if ((ctx->ctx_wstate == WKST_READY_TO_WAIT) ||
(ctx->ctx_wstate == WKST_WAITING))
{
/*
** Resize the pollfd array if it
** isn't large enough.
*/
if (nfd >= dim_pfd)
{
struct pollfd *tpfd;
size_t new;
new = (dim_pfd + PFD_STEP) *
sizeof(*tpfd);
tpfd = (struct pollfd *)
realloc(pfd, new);
if (tpfd != NULL)
{
pfd = tpfd;
dim_pfd += PFD_STEP;
}
else
{
smi_log(SMI_LOG_ERR,
"Failed to realloc pollfd array:%s",
sm_errstring(errno));
}
}
/* add the session to pollfd array */
if (nfd < dim_pfd)
{
ctx->ctx_wstate = WKST_WAITING;
pfd[nfd].fd = ctx->ctx_sd;
pfd[nfd].events = MI_POLL_RD_FLAGS;
pfd[nfd].revents = 0;
nfd++;
}
}
}
}
TASKMGR_UNLOCK();
/* Everything is ready, let's wait for an event */
rfd = poll(pfd, nfd, POLL_TIMEOUT);
POOL_LEV_DPRINTF(4, ("%s returned: at epoch %d value %d",
WAITFN, now, nfd));
/* timeout */
if (rfd == 0)
continue;
rebuild_set = true;
/* error */
if (rfd < 0)
{
if (errno == EINTR)
continue;
pcnt++;
smi_log(SMI_LOG_ERR,
"%s() failed (%s), %s",
WAITFN, sm_errstring(errno),
pcnt >= MAX_FAILS_S ? "abort" : "try again");
if (pcnt >= MAX_FAILS_S)
goto err;
}
pcnt = 0;
/* something happened */
for (i = 0; i < nfd; i++)
{
if (pfd[i].revents == 0)
continue;
POOL_LEV_DPRINTF(4, ("%s event on pfd[%d/%d]=%d ",
WAITFN, i, nfd,
WAIT_FD(i)));
/* has a worker signaled an end of task ? */
if (WAIT_FD(i) == RD_PIPE)
{
char evt = 0;
int r = 0;
POOL_LEV_DPRINTF(4,
("PIPE WILL READ evt = %08X %08X",
pfd[i].events, pfd[i].revents));
if ((pfd[i].revents & MI_POLL_RD_FLAGS) != 0)
{
r = read(RD_PIPE, &evt, sizeof(evt));
if (r == sizeof(evt))
{
/* Do nothing */
}
}
POOL_LEV_DPRINTF(4,
("PIPE DONE READ i=[%d] fd=[%d] r=[%d] evt=[%d]",
i, RD_PIPE, r, evt));
if ((pfd[i].revents & ~MI_POLL_RD_FLAGS) != 0)
{
/* Exception handling */
}
continue;
}
/* no ! sendmail wants to send a command */
SM_TAILQ_FOREACH(ctx, &WRK_CTX_HEAD, ctx_link)
{
if (ctx->ctx_wstate != WKST_WAITING)
continue;
POOL_LEV_DPRINTF(4,
("Checking context sd=%d - fd=%d ",
ctx->ctx_sd , WAIT_FD(i)));
if (ctx->ctx_sd == pfd[i].fd)
{
TASKMGR_LOCK();
POOL_LEV_DPRINTF(4,
("TASK: found %d for fd[%d]=%d",
ctx->ctx_sid, i, WAIT_FD(i)));
if (Tskmgr.tm_nb_idle > 0)
{
ctx->ctx_wstate = WKST_READY_TO_RUN;
TASKMGR_COND_SIGNAL();
}
else
{
ctx->ctx_wstate = WKST_RUNNING;
LAUNCH_WORKER(ctx);
}
TASKMGR_UNLOCK();
break;
}
}
POOL_LEV_DPRINTF(4,
("TASK %s FOUND - Checking PIPE for fd[%d]",
ctx != NULL ? "" : "NOT", WAIT_FD(i)));
}
}
err:
if (pfd != NULL)
free(pfd);
Tskmgr.tm_signature = 0;
for (;;)
{
SMFICTX_PTR ctx;
ctx = SM_TAILQ_FIRST(&WRK_CTX_HEAD);
if (ctx == NULL)
break;
mi_close_session(ctx);
}
(void) smutex_destroy(&Tskmgr.tm_w_mutex);
(void) scond_destroy(&Tskmgr.tm_w_cond);
return NULL;
}
/*
** Look for a task ready to run.
** Value of ctx is NULL or a pointer to a task ready to run.
*/
#define GET_TASK_READY_TO_RUN() \
SM_TAILQ_FOREACH(ctx, &WRK_CTX_HEAD, ctx_link) \
{ \
if (ctx->ctx_wstate == WKST_READY_TO_RUN) \
{ \
ctx->ctx_wstate = WKST_RUNNING; \
break; \
} \
}
/*
** MI_WORKER -- worker thread
** executes tasks distributed by the mi_pool_controller
** or by mi_start_session
**
** Parameters:
** arg -- pointer to context structure
**
** Returns:
** NULL pointer
*/
static void *
mi_worker(arg)
void *arg;
{
SMFICTX_PTR ctx;
bool done;
sthread_t t_id;
int r;
ctx = (SMFICTX_PTR) arg;
done = false;
if (ctx != NULL)
ctx->ctx_wstate = WKST_RUNNING;
t_id = sthread_get_id();
if (pthread_detach(t_id) != 0)
{
smi_log(SMI_LOG_ERR, "Failed to detach worker thread");
if (ctx != NULL)
ctx->ctx_wstate = WKST_READY_TO_RUN;
return NULL;
}
TASKMGR_LOCK();
Tskmgr.tm_nb_workers++;
TASKMGR_UNLOCK();
while (!done)
{
if (mi_stop() != MILTER_CONT)
break;
/* let's handle next task... */
if (ctx != NULL)
{
int res;
POOL_LEV_DPRINTF(4,
("worker %d: new task -> let's handle it",
t_id));
res = mi_engine(ctx);
POOL_LEV_DPRINTF(4,
("worker %d: mi_engine returned %d", t_id, res));
TASKMGR_LOCK();
if (res != MI_CONTINUE)
{
ctx->ctx_wstate = WKST_CLOSING;
/*
** Delete context from linked list of
** sessions and close session.
*/
mi_close_session(ctx);
}
else
{
ctx->ctx_wstate = WKST_READY_TO_WAIT;
POOL_LEV_DPRINTF(4,
("writing to event pipe..."));
/*
** Signal task controller to add new session
** to poll set.
*/
PIPE_SEND_SIGNAL();
}
TASKMGR_UNLOCK();
ctx = NULL;
}
/* check if there is any task waiting to be served */
TASKMGR_LOCK();
GET_TASK_READY_TO_RUN();
/* Got a task? */
if (ctx != NULL)
{
TASKMGR_UNLOCK();
continue;
}
/*
** if not, let's check if there is enough idle workers
** if yes: quit
*/
if (Tskmgr.tm_nb_workers > MIN_WORKERS &&
Tskmgr.tm_nb_idle > MIN_IDLE)
done = true;
POOL_LEV_DPRINTF(4, ("worker %d: checking ... %d %d", t_id,
Tskmgr.tm_nb_workers, Tskmgr.tm_nb_idle + 1));
if (done)
{
POOL_LEV_DPRINTF(4, ("worker %d: quitting... ", t_id));
Tskmgr.tm_nb_workers--;
TASKMGR_UNLOCK();
continue;
}
/*
** if no task ready to run, wait for another one
*/
Tskmgr.tm_nb_idle++;
TASKMGR_COND_WAIT();
Tskmgr.tm_nb_idle--;
/* look for a task */
GET_TASK_READY_TO_RUN();
TASKMGR_UNLOCK();
}
return NULL;
}
/*
** MI_LIST_ADD_CTX -- add new session to linked list
**
** Parameters:
** ctx -- context structure
**
** Returns:
** MI_FAILURE/MI_SUCCESS
*/
static int
mi_list_add_ctx(ctx)
SMFICTX_PTR ctx;
{
SM_ASSERT(ctx != NULL);
SM_TAILQ_INSERT_TAIL(&WRK_CTX_HEAD, ctx, ctx_link);
return MI_SUCCESS;
}
/*
** MI_LIST_DEL_CTX -- remove session from linked list when finished
**
** Parameters:
** ctx -- context structure
**
** Returns:
** MI_FAILURE/MI_SUCCESS
*/
static int
mi_list_del_ctx(ctx)
SMFICTX_PTR ctx;
{
SM_ASSERT(ctx != NULL);
if (SM_TAILQ_EMPTY(&WRK_CTX_HEAD))
return MI_FAILURE;
SM_TAILQ_REMOVE(&WRK_CTX_HEAD, ctx, ctx_link);
return MI_SUCCESS;
}
#endif /* _FFR_WORKERS_POOL */

View File

@ -0,0 +1,235 @@
/*
* Copyright (c) 2006 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.
*/
#include <sm/gen.h>
SM_IDSTR(id, "@(#)$Id: t-qic.c,v 1.9 2006/08/24 21:26:13 ca Exp $")
#include <stdio.h>
#include <sm/sendmail.h>
#include <sm/assert.h>
#include <sm/heap.h>
#include <sm/string.h>
#include <sm/test.h>
extern bool SmTestVerbose;
void
show_diff(s1, s2)
const char *s1;
const char *s2;
{
int i;
for (i = 0; s1[i] != '\0' && s2[i] != '\0'; i++)
{
if (s1[i] != s2[i])
{
fprintf(stderr, "i=%d, s1[]=%u, s2[]=%u\n",
i, (unsigned char) s1[i],
(unsigned char) s2[i]);
return;
}
}
if (s1[i] != s2[i])
{
fprintf(stderr, "i=%d, s1[]=%u, s2[]=%u\n",
i, (unsigned char) s1[i], (unsigned char) s2[i]);
}
}
char *quote_unquote __P((char *, char *, int, int));
char *
quote_unquote(in, out, outlen, exp)
char *in;
char *out;
int outlen;
int exp;
{
char *obp, *bp;
char line_back[1024];
char line_in[1024];
int cmp;
sm_strlcpy(line_in, in, sizeof(line_in));
obp = quote_internal_chars(in, out, &outlen);
bp = str2prt(line_in);
dequote_internal_chars(obp, line_back, sizeof(line_back));
cmp = strcmp(line_in, line_back);
SM_TEST(exp == cmp);
if (cmp != exp && !SmTestVerbose)
{
fprintf(stderr, "in: %s\n", bp);
bp = str2prt(line_back);
fprintf(stderr, "out:%s\n", bp);
fprintf(stderr, "cmp=%d\n", cmp);
show_diff(in, line_back);
}
if (SmTestVerbose)
{
fprintf(stderr, "%s -> ", bp);
bp = str2prt(obp);
fprintf(stderr, "%s\n", bp);
fprintf(stderr, "cmp=%d\n", cmp);
}
return obp;
}
struct sm_qic_S
{
char *qic_in;
char *qic_out;
int qic_exp;
};
typedef struct sm_qic_S sm_qic_T;
int
main(argc, argv)
int argc;
char *argv[];
{
char line_in[1024], line[256], line_out[32], *obp;
int i, los, cmp;
sm_qic_T inout[] = {
{ "", "", 0 }
, { "abcdef", "abcdef", 0 }
, { "01234567890123456789", "01234567890123456789", 0 }
, { "01234567890123456789\001", "01234567890123456789\001",
0 }
, { "012345\2067890123456789", "012345\377\2067890123456789",
0 }
, { "\377", "\377\377", 0 }
, { "\240", "\240", 0 }
, { "\220", "\377\220", 0 }
, { "\240\220", "\240\377\220", 0 }
, { "\377\377", "\377\377\377\377", 0 }
, { "\377a\377b", "\377\377a\377\377b", 0 }
, { "\376a\377b", "\376a\377\377b", 0 }
, { "\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240",
"\377\200\377\201\377\202\377\203\377\204\377\205\377\206\377\207\377\210\377\211\377\212\377\213\377\214\377\215\377\216\377\217\377\220\377\221\377\222\377\223\377\224\377\225\377\226\377\227\377\230\377\231\377\232\377\233\377\234\377\235\377\236\377\237\240",
0 }
, { NULL, NULL, 0 }
};
sm_test_begin(argc, argv, "test meta quoting");
for (i = 0; i < sizeof(line_out); i++)
line_out[i] = '\0';
for (i = 0; i < sizeof(line_in); i++)
line_in[i] = '\0';
for (i = 0; i < sizeof(line_in) / 2; i++)
{
char ch;
ch = 0200 + i;
if ('\0' == ch)
ch = '0';
line_in[i] = ch;
}
los = sizeof(line_out) / 2;
obp = quote_unquote(line_in, line_out, los, 0);
if (obp != line_out)
SM_FREE(obp);
for (i = 0; i < sizeof(line_in); i++)
line_in[i] = '\0';
for (i = 0; i < sizeof(line_in) / 2; i++)
{
char ch;
ch = 0200 + i;
if ('\0' == ch)
ch = '0';
line_in[i] = ch;
}
los = sizeof(line_in);
obp = quote_unquote(line_in, line_in, los, 0);
if (obp != line_in)
SM_FREE(obp);
for (i = 0; inout[i].qic_in != NULL; i++)
{
los = sizeof(line_out) / 2;
obp = quote_unquote(inout[i].qic_in, line_out, los,
inout[i].qic_exp);
cmp = strcmp(inout[i].qic_out, obp);
SM_TEST(inout[i].qic_exp == cmp);
if (inout[i].qic_exp != cmp && !SmTestVerbose)
{
char *bp;
bp = str2prt(obp);
fprintf(stderr, "got: %s\n", bp);
bp = str2prt(inout[i].qic_out);
fprintf(stderr, "exp:%s\n", bp);
fprintf(stderr, "cmp=%d\n", cmp);
show_diff(inout[i].qic_in, inout[i].qic_out);
}
if (obp != line_out)
SM_FREE(obp);
}
/* use same buffer for in and out */
for (i = 0; inout[i].qic_in != NULL; i++)
{
bool same;
same = strcmp(inout[i].qic_in, inout[i].qic_out) == 0;
los = sm_strlcpy(line, inout[i].qic_in, sizeof(line));
SM_TEST(los + 1 < sizeof(line));
++los;
obp = quote_unquote(line, line, los, inout[i].qic_exp);
cmp = strcmp(inout[i].qic_out, obp);
SM_TEST(inout[i].qic_exp == cmp);
if (inout[i].qic_exp != cmp && !SmTestVerbose)
{
char *bp;
bp = str2prt(obp);
fprintf(stderr, "got: %s\n", bp);
bp = str2prt(inout[i].qic_out);
fprintf(stderr, "exp:%s\n", bp);
fprintf(stderr, "cmp=%d\n", cmp);
show_diff(inout[i].qic_in, inout[i].qic_out);
}
if (obp != line)
{
SM_TEST(!same);
if (same)
show_diff(obp, inout[i].qic_out);
SM_FREE(obp);
}
}
/* use NULL buffer for out */
for (i = 0; inout[i].qic_in != NULL; i++)
{
los = 0;
obp = quote_unquote(inout[i].qic_in, NULL, los,
inout[i].qic_exp);
SM_TEST(obp != NULL);
cmp = strcmp(inout[i].qic_out, obp);
SM_TEST(inout[i].qic_exp == cmp);
if (inout[i].qic_exp != cmp && !SmTestVerbose)
{
char *bp;
bp = str2prt(obp);
fprintf(stderr, "got: %s\n", bp);
bp = str2prt(inout[i].qic_out);
fprintf(stderr, "exp:%s\n", bp);
fprintf(stderr, "cmp=%d\n", cmp);
show_diff(inout[i].qic_in, inout[i].qic_out);
}
}
return sm_test_end();
}

View File

@ -0,0 +1,250 @@
/*
* Copyright (c) 2006 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.
*
*/
#include <sm/gen.h>
SM_RCSID("@(#)$Id: util.c,v 1.9 2006/08/30 18:35:51 ca Exp $")
#include <sm/setjmp.h>
#include <sm/conf.h>
#include <sm/assert.h>
#include <sm/heap.h>
#include <sm/string.h>
#include <sm/sendmail.h>
#include <ctype.h>
/*
** STR2PRT -- convert "unprintable" characters in a string to \oct
**
** Parameters:
** s -- string to convert
**
** Returns:
** converted string.
** This is a static local buffer, string must be copied
** before this function is called again!
*/
char *
str2prt(s)
char *s;
{
int l;
char c, *h;
bool ok;
static int len = 0;
static char *buf = NULL;
if (s == NULL)
return NULL;
ok = true;
for (h = s, l = 1; *h != '\0'; h++, l++)
{
if (*h == '\\')
{
++l;
ok = false;
}
else if (!(isascii(*h) && isprint(*h)))
{
l += 3;
ok = false;
}
}
if (ok)
return s;
if (l > len)
{
char *nbuf = sm_pmalloc_x(l);
if (buf != NULL)
sm_free(buf);
len = l;
buf = nbuf;
}
for (h = buf; *s != '\0' && l > 0; s++, l--)
{
c = *s;
if (isascii(c) && isprint(c) && c != '\\')
{
*h++ = c;
}
else
{
*h++ = '\\';
--l;
switch (c)
{
case '\\':
*h++ = '\\';
break;
case '\t':
*h++ = 't';
break;
case '\n':
*h++ = 'n';
break;
case '\r':
*h++ = 'r';
break;
default:
SM_ASSERT(l >= 2);
(void) sm_snprintf(h, l, "%03o",
(unsigned int)((unsigned char) c));
/*
** XXX since l is unsigned this may
** wrap around if the calculation is screwed
** up...
*/
l -= 2;
h += 3;
break;
}
}
}
*h = '\0';
buf[len - 1] = '\0';
return buf;
}
/*
** QUOTE_INTERNAL_CHARS -- do quoting of internal characters
**
** Necessary to make sure that we don't have metacharacters such
** as the internal versions of "$*" or "$&" in a string.
** The input and output pointers can be the same.
**
** Parameters:
** ibp -- a pointer to the string to translate
** obp -- a pointer to an output buffer
** bsp -- pointer to the length of the output buffer
**
** Returns:
** A possibly new bp (if the buffer needed to grow); if
** it is different, *bsp will updated to the size of
** the new buffer and the caller is responsible for
** freeing the memory.
*/
#define SM_MM_QUOTE(ch) (((ch) & 0377) == METAQUOTE || (((ch) & 0340) == 0200))
char *
quote_internal_chars(ibp, obp, bsp)
char *ibp;
char *obp;
int *bsp;
{
char *ip, *op;
int bufused, olen;
bool buffer_same, needs_quoting;
buffer_same = ibp == obp;
needs_quoting = false;
/* determine length of output string (starts at 1 for trailing '\0') */
for (ip = ibp, olen = 1; *ip != '\0'; ip++, olen++)
{
if (SM_MM_QUOTE(*ip))
{
olen++;
needs_quoting = true;
}
}
/* is the output buffer big enough? */
if (olen > *bsp)
{
obp = sm_malloc_x(olen);
buffer_same = false;
*bsp = olen;
}
/*
** shortcut: no change needed?
** Note: we don't check this first as some bozo may use the same
** buffers but restrict the size of the output buffer to less
** than the length of the input buffer in which case we need to
** allocate a new buffer.
*/
if (!needs_quoting)
{
if (!buffer_same)
{
bufused = sm_strlcpy(obp, ibp, *bsp);
SM_ASSERT(bufused <= olen);
}
return obp;
}
if (buffer_same)
{
obp = sm_malloc_x(olen);
buffer_same = false;
*bsp = olen;
}
for (ip = ibp, op = obp, bufused = 0; *ip != '\0'; ip++)
{
if (SM_MM_QUOTE(*ip))
{
SM_ASSERT(bufused < olen);
op[bufused++] = METAQUOTE;
}
SM_ASSERT(bufused < olen);
op[bufused++] = *ip;
}
op[bufused] = '\0';
return obp;
}
/*
** DEQUOTE_INTERNAL_CHARS -- undo the effect of quote_internal_chars
**
** Parameters:
** ibp -- a pointer to the string to be translated.
** obp -- a pointer to the output buffer. Can be the
** same as ibp.
** obs -- the size of the output buffer.
**
** Returns:
** number of character added to obp
*/
int
dequote_internal_chars(ibp, obp, obs)
char *ibp;
char *obp;
int obs;
{
char *ip, *op;
int len;
bool quoted;
quoted = false;
len = 0;
for (ip = ibp, op = obp; *ip != '\0'; ip++)
{
if ((*ip & 0377) == METAQUOTE && !quoted)
{
quoted = true;
continue;
}
if (op < &obp[obs - 1])
{
*op++ = *ip;
++len;
}
quoted = false;
}
*op = '\0';
return len;
}

View File

@ -0,0 +1,62 @@
/*
* Copyright (c) 2006 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.
*
* $Id: daemon.h,v 8.3 2006/07/13 22:57:03 ca Exp $
*/
#ifndef DAEMON_H
#define DAEMON_H 1
#if DAEMON_C
# define EXTERN
#else
# define EXTERN extern
#endif
/* structure to describe a daemon or a client */
struct daemon
{
int d_socket; /* fd for socket */
SOCKADDR d_addr; /* socket for incoming */
unsigned short d_port; /* port number */
int d_listenqueue; /* size of listen queue */
int d_tcprcvbufsize; /* size of TCP receive buffer */
int d_tcpsndbufsize; /* size of TCP send buffer */
time_t d_refuse_connections_until;
bool d_firsttime;
int d_socksize;
BITMAP256 d_flags; /* flags; see sendmail.h */
char *d_mflags; /* flags for use in macro */
char *d_name; /* user-supplied name */
int d_dm; /* DeliveryMode */
int d_refuseLA;
int d_queueLA;
int d_delayLA;
int d_maxchildren;
#if MILTER
char *d_inputfilterlist;
struct milter *d_inputfilters[MAXFILTERS];
#endif /* MILTER */
#if _FFR_SS_PER_DAEMON
int d_supersafe;
#endif /* _FFR_SS_PER_DAEMON */
};
typedef struct daemon DAEMON_T;
EXTERN DAEMON_T Daemons[MAXDAEMONS];
#define DPO_NOTSET (-1) /* daemon option (int) not set */
/* see also sendmail.h: SuperSafe values */
extern bool refuseconnections __P((ENVELOPE *, int, bool));
#undef EXTERN
#endif /* ! DAEMON_H */

View File

@ -0,0 +1,86 @@
/*
* Copyright (c) 2006 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.
*
* $Id: map.h,v 8.3 2006/12/19 19:49:51 ca Exp $
*/
#ifndef _MAP_H
# define _MAP_H 1
extern char *arith_map_lookup __P((MAP *, char *, char **, int *));
extern char *bestmx_map_lookup __P((MAP *, char *, char **, int *));
extern char *bogus_map_lookup __P((MAP *, char *, char **, int *));
extern bool bt_map_open __P((MAP *, int));
extern char *db_map_lookup __P((MAP *, char *, char **, int *));
extern void db_map_store __P((MAP *, char *, char *));
extern void db_map_close __P((MAP *));
extern bool dequote_init __P((MAP *, char *));
extern char *dequote_map __P((MAP *, char *, char **, int *));
extern bool dns_map_open __P((MAP *, int));
extern bool dns_map_parseargs __P((MAP *, char *));
extern char *dns_map_lookup __P((MAP *, char *, char **, int *));
extern bool dprintf_map_parseargs __P((MAP *, char *));
extern char *dprintf_map_lookup __P((MAP *, char *, char **, int *));
extern bool hash_map_open __P((MAP *, int));
extern bool host_map_init __P((MAP *, char *));
extern char *host_map_lookup __P((MAP *, char *, char **, int *));
extern char *impl_map_lookup __P((MAP *, char *, char **, int *));
extern void impl_map_store __P((MAP *, char *, char *));
extern bool impl_map_open __P((MAP *, int));
extern void impl_map_close __P((MAP *));
extern char *macro_map_lookup __P((MAP *, char *, char **, int *));
extern bool map_parseargs __P((MAP *, char *));
extern bool nis_map_open __P((MAP *, int));
extern char *nis_map_lookup __P((MAP *, char *, char **, int *));
extern bool null_map_open __P((MAP *, int));
extern void null_map_close __P((MAP *));
extern char *null_map_lookup __P((MAP *, char *, char **, int *));
extern void null_map_store __P((MAP *, char *, char *));
extern char *prog_map_lookup __P((MAP *, char *, char **, int *));
extern bool regex_map_init __P((MAP *, char *));
extern char *regex_map_lookup __P((MAP *, char *, char **, int *));
extern char *seq_map_lookup __P((MAP *, char *, char **, int *));
extern void seq_map_store __P((MAP *, char *, char *));
extern bool seq_map_parse __P((MAP *, char *));
extern char *stab_map_lookup __P((MAP *, char *, char **, int *));
extern void stab_map_store __P((MAP *, char *, char *));
extern bool stab_map_open __P((MAP *, int));
extern bool switch_map_open __P((MAP *, int));
extern bool syslog_map_parseargs __P((MAP *, char *));
extern char *syslog_map_lookup __P((MAP *, char *, char **, int *));
extern bool text_map_open __P((MAP *, int));
extern char *text_map_lookup __P((MAP *, char *, char **, int *));
extern char *udb_map_lookup __P((MAP *, char *, char **, int *));
extern bool user_map_open __P((MAP *, int));
extern char *user_map_lookup __P((MAP *, char *, char **, int *));
#endif /* ! _MAP_H */

12
etc/rc.d/FILESYSTEMS Normal file
View File

@ -0,0 +1,12 @@
#!/bin/sh
#
# $FreeBSD$
#
# PROVIDE: FILESYSTEMS
# REQUIRE: root mountcritlocal zfs
# This is a dummy dependency, for services which require file systems
# to be mounted before starting. It also serves as the default early /
# late divider; after this point, rc.d directories are rescanned to
# catch scripts from other file systems than /.

111
share/man/man4/edsc.4 Normal file
View File

@ -0,0 +1,111 @@
.\" Copyright (c) 1983, 1991, 1993
.\" The Regents of the University of California. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. All advertising materials mentioning features or use of this software
.\" must display the following acknowledgement:
.\" This product includes software developed by the University of
.\" California, Berkeley and its contributors.
.\" 4. Neither the name of the University nor the names of its contributors
.\" may be used to endorse or promote products derived from this software
.\" without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" From: @(#)lo.4 8.1 (Berkeley) 6/5/93
.\" $FreeBSD$
.\"
.Dd March 25, 2007
.Dt EDSC 4
.Os
.Sh NAME
.Nm edsc
.Nd Ethernet discard network interface
.Sh SYNOPSIS
.Cd "device edsc"
.Sh DESCRIPTION
The
.Nm
interface is a software discard mechanism which may be
used for performance analysis and software testing.
It imitates an Ethernet device, which
allows for its use in conjunction with such drivers as
.Xr if_bridge 4
and
.Xr vlan 4 .
.Pp
As with other network interfaces, an
.Nm
interface must have network addresses assigned for each address family
with which it is to be used.
These addresses may be set or changed with the
.Dv SIOCSIFADDR
.Xr ioctl 2
or
.Xr ifconfig 8
utility.
.Pp
Each
.Nm
interface is created at runtime using interface cloning.
This is most easily done with the
.Xr ifconfig 8
.Cm create
command or using the
.Va cloned_interfaces
variable in
.Xr rc.conf 5 .
.Sh SEE ALSO
.Xr ioctl 2 ,
.Xr arp 4 ,
.Xr if_bridge 4 ,
.Xr inet 4 ,
.Xr intro 4 ,
.Xr vlan 4 ,
.Xr rc.conf 5 ,
.Xr arp 8 ,
.Xr ifconfig 8
.Sh HISTORY
The
.Nm
device was derived from the
.Xr disc 4
device and first appeared in
.Fx 7.0 .
This manpage was adapted from
.Xr disc 4 .
.Sh CAVEATS
Since outgoing packets are just discarded by
.Nm ,
ARP requests stay unreplied.
Consequently, an IP packet cannot be sent via
.Nm
until a static
.Xr arp 4
entry is created for its next hop using
.Xr arp 8 .
.Pp
Initially an
.Nm
interface has a zero link level address.
It can be changed with
.Xr ifconfig 8
.Cm lladdr
if needed.

994
sys/cam/scsi/scsi_sg.c Normal file
View File

@ -0,0 +1,994 @@
/*-
* Copyright (c) 2007 Scott Long
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification, immediately at the beginning of the file.
* 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* scsi_sg peripheral driver. This driver is meant to implement the Linux
* SG passthrough interface for SCSI.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/types.h>
#include <sys/bio.h>
#include <sys/malloc.h>
#include <sys/fcntl.h>
#include <sys/ioccom.h>
#include <sys/conf.h>
#include <sys/errno.h>
#include <sys/devicestat.h>
#include <sys/proc.h>
#include <sys/uio.h>
#include <cam/cam.h>
#include <cam/cam_ccb.h>
#include <cam/cam_periph.h>
#include <cam/cam_queue.h>
#include <cam/cam_xpt_periph.h>
#include <cam/cam_debug.h>
#include <cam/cam_sim.h>
#include <cam/scsi/scsi_all.h>
#include <cam/scsi/scsi_message.h>
#include <cam/scsi/scsi_sg.h>
#include <compat/linux/linux_ioctl.h>
typedef enum {
SG_FLAG_OPEN = 0x01,
SG_FLAG_LOCKED = 0x02,
SG_FLAG_INVALID = 0x04
} sg_flags;
typedef enum {
SG_STATE_NORMAL
} sg_state;
typedef enum {
SG_RDWR_FREE,
SG_RDWR_INPROG,
SG_RDWR_DONE
} sg_rdwr_state;
typedef enum {
SG_CCB_RDWR_IO,
SG_CCB_WAITING
} sg_ccb_types;
#define ccb_type ppriv_field0
#define ccb_rdwr ppriv_ptr1
struct sg_rdwr {
TAILQ_ENTRY(sg_rdwr) rdwr_link;
int tag;
int state;
int buf_len;
char *buf;
union ccb *ccb;
union {
struct sg_header hdr;
struct sg_io_hdr io_hdr;
} hdr;
};
struct sg_softc {
sg_state state;
sg_flags flags;
struct devstat *device_stats;
TAILQ_HEAD(, sg_rdwr) rdwr_done;
struct cdev *dev;
struct cdev *devalias;
int sg_timeout;
int sg_user_timeout;
uint8_t pd_type;
union ccb saved_ccb;
};
static d_open_t sgopen;
static d_close_t sgclose;
static d_ioctl_t sgioctl;
static d_write_t sgwrite;
static d_read_t sgread;
static periph_init_t sginit;
static periph_ctor_t sgregister;
static periph_oninv_t sgoninvalidate;
static periph_dtor_t sgcleanup;
static periph_start_t sgstart;
static void sgasync(void *callback_arg, uint32_t code,
struct cam_path *path, void *arg);
static void sgdone(struct cam_periph *periph, union ccb *done_ccb);
static int sgsendccb(struct cam_periph *periph, union ccb *ccb);
static int sgsendrdwr(struct cam_periph *periph, union ccb *ccb);
static int sgerror(union ccb *ccb, uint32_t cam_flags,
uint32_t sense_flags);
static void sg_scsiio_status(struct ccb_scsiio *csio,
u_short *hoststat, u_short *drvstat);
static int scsi_group_len(u_char cmd);
static struct periph_driver sgdriver =
{
sginit, "sg",
TAILQ_HEAD_INITIALIZER(sgdriver.units), /* gen */ 0
};
PERIPHDRIVER_DECLARE(sg, sgdriver);
static struct cdevsw sg_cdevsw = {
.d_version = D_VERSION,
.d_flags = D_NEEDGIANT,
.d_open = sgopen,
.d_close = sgclose,
.d_ioctl = sgioctl,
.d_write = sgwrite,
.d_read = sgread,
.d_name = "sg",
};
static int sg_version = 30125;
static void
sginit(void)
{
cam_status status;
struct cam_path *path;
/*
* Install a global async callback. This callback will receive aync
* callbacks like "new device found".
*/
status = xpt_create_path(&path, /*periph*/NULL, CAM_XPT_PATH_ID,
CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
if (status == CAM_REQ_CMP) {
struct ccb_setasync csa;
xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5);
csa.ccb_h.func_code = XPT_SASYNC_CB;
csa.event_enable = AC_FOUND_DEVICE;
csa.callback = sgasync;
csa.callback_arg = NULL;
xpt_action((union ccb *)&csa);
status = csa.ccb_h.status;
xpt_free_path(path);
}
if (status != CAM_REQ_CMP) {
printf("sg: Failed to attach master async callbac "
"due to status 0x%x!\n", status);
}
}
static void
sgoninvalidate(struct cam_periph *periph)
{
struct sg_softc *softc;
struct ccb_setasync csa;
softc = (struct sg_softc *)periph->softc;
/*
* Deregister any async callbacks.
*/
xpt_setup_ccb(&csa.ccb_h, periph->path, /*priority*/5);
csa.ccb_h.func_code = XPT_SASYNC_CB;
csa.event_enable = 0;
csa.callback = sgasync;
csa.callback_arg = periph;
xpt_action((union ccb *)&csa);
softc->flags |= SG_FLAG_INVALID;
/*
* XXX Return all queued I/O with ENXIO.
* XXX Handle any transactions queued to the card
* with XPT_ABORT_CCB.
*/
if (bootverbose) {
xpt_print(periph->path, "lost device\n");
}
}
static void
sgcleanup(struct cam_periph *periph)
{
struct sg_softc *softc;
softc = (struct sg_softc *)periph->softc;
devstat_remove_entry(softc->device_stats);
destroy_dev(softc->dev);
destroy_dev(softc->devalias);
if (bootverbose) {
xpt_print(periph->path, "removing device entry\n");
}
free(softc, M_DEVBUF);
}
static void
sgasync(void *callback_arg, uint32_t code, struct cam_path *path, void *arg)
{
struct cam_periph *periph;
periph = (struct cam_periph *)callback_arg;
switch (code) {
case AC_FOUND_DEVICE:
{
struct ccb_getdev *cgd;
cam_status status;
cgd = (struct ccb_getdev *)arg;
if (cgd == NULL)
break;
/*
* Allocate a peripheral instance for this device and
* start the probe process.
*/
status = cam_periph_alloc(sgregister, sgoninvalidate,
sgcleanup, sgstart, "sg",
CAM_PERIPH_BIO, cgd->ccb_h.path,
sgasync, AC_FOUND_DEVICE, cgd);
if ((status != CAM_REQ_CMP) && (status != CAM_REQ_INPROG)) {
const struct cam_status_entry *entry;
entry = cam_fetch_status_entry(status);
printf("sgasync: Unable to attach new device "
"due to status %#x: %s\n", status, entry ?
entry->status_text : "Unknown");
}
break;
}
default:
cam_periph_async(periph, code, path, arg);
break;
}
}
static cam_status
sgregister(struct cam_periph *periph, void *arg)
{
struct sg_softc *softc;
struct ccb_setasync csa;
struct ccb_getdev *cgd;
int no_tags;
cgd = (struct ccb_getdev *)arg;
if (periph == NULL) {
printf("sgregister: periph was NULL!!\n");
return (CAM_REQ_CMP_ERR);
}
if (cgd == NULL) {
printf("sgregister: no getdev CCB, can't register device\n");
return (CAM_REQ_CMP_ERR);
}
softc = malloc(sizeof(*softc), M_DEVBUF, M_ZERO | M_NOWAIT);
if (softc == NULL) {
printf("sgregister: Unable to allocate softc\n");
return (CAM_REQ_CMP_ERR);
}
softc->state = SG_STATE_NORMAL;
softc->pd_type = SID_TYPE(&cgd->inq_data);
softc->sg_timeout = SG_DEFAULT_TIMEOUT / SG_DEFAULT_HZ * hz;
softc->sg_user_timeout = SG_DEFAULT_TIMEOUT;
TAILQ_INIT(&softc->rdwr_done);
periph->softc = softc;
/*
* We pass in 0 for all blocksize, since we don't know what the
* blocksize of the device is, if it even has a blocksize.
*/
no_tags = (cgd->inq_data.flags & SID_CmdQue) == 0;
softc->device_stats = devstat_new_entry("sg",
unit2minor(periph->unit_number), 0,
DEVSTAT_NO_BLOCKSIZE
| (no_tags ? DEVSTAT_NO_ORDERED_TAGS : 0),
softc->pd_type |
DEVSTAT_TYPE_IF_SCSI |
DEVSTAT_TYPE_PASS,
DEVSTAT_PRIORITY_PASS);
/* Register the device */
softc->dev = make_dev(&sg_cdevsw, unit2minor(periph->unit_number),
UID_ROOT, GID_OPERATOR, 0600, "%s%d",
periph->periph_name, periph->unit_number);
softc->devalias = make_dev_alias(softc->dev, "sg%c",
'a' + periph->unit_number);
softc->dev->si_drv1 = periph;
/*
* Add as async callback so that we get
* notified if this device goes away.
*/
xpt_setup_ccb(&csa.ccb_h, periph->path, /*priority*/5);
csa.ccb_h.func_code = XPT_SASYNC_CB;
csa.event_enable = AC_LOST_DEVICE;
csa.callback = sgasync;
csa.callback_arg = periph;
xpt_action((union ccb *)&csa);
if (bootverbose)
xpt_announce_periph(periph, NULL);
return (CAM_REQ_CMP);
}
static void
sgstart(struct cam_periph *periph, union ccb *start_ccb)
{
struct sg_softc *softc;
softc = (struct sg_softc *)periph->softc;
switch (softc->state) {
case SG_STATE_NORMAL:
start_ccb->ccb_h.ccb_type = SG_CCB_WAITING;
SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
periph_links.sle);
periph->immediate_priority = CAM_PRIORITY_NONE;
wakeup(&periph->ccb_list);
break;
}
}
static void
sgdone(struct cam_periph *periph, union ccb *done_ccb)
{
struct sg_softc *softc;
struct ccb_scsiio *csio;
softc = (struct sg_softc *)periph->softc;
csio = &done_ccb->csio;
switch (csio->ccb_h.ccb_type) {
case SG_CCB_WAITING:
/* Caller will release the CCB */
wakeup(&done_ccb->ccb_h.cbfcnp);
return;
case SG_CCB_RDWR_IO:
{
struct sg_rdwr *rdwr;
int state;
devstat_end_transaction(softc->device_stats,
csio->dxfer_len,
csio->tag_action & 0xf,
((csio->ccb_h.flags & CAM_DIR_MASK) ==
CAM_DIR_NONE) ? DEVSTAT_NO_DATA :
(csio->ccb_h.flags & CAM_DIR_OUT) ?
DEVSTAT_WRITE : DEVSTAT_READ,
NULL, NULL);
rdwr = done_ccb->ccb_h.ccb_rdwr;
state = rdwr->state;
rdwr->state = SG_RDWR_DONE;
wakeup(rdwr);
break;
}
default:
panic("unknown sg CCB type");
}
}
static int
sgopen(struct cdev *dev, int flags, int fmt, struct thread *td)
{
struct cam_periph *periph;
struct sg_softc *softc;
int error = 0;
periph = (struct cam_periph *)dev->si_drv1;
if (periph == NULL)
return (ENXIO);
softc = (struct sg_softc *)periph->softc;
if (softc->flags & SG_FLAG_INVALID)
return (ENXIO);
/*
* Don't allow access when we're running at a high securelevel.
*/
error = securelevel_gt(td->td_ucred, 1);
if (error)
return (error);
if ((error = cam_periph_lock(periph, PRIBIO | PCATCH)) != 0)
return (error);
if ((softc->flags & SG_FLAG_OPEN) == 0) {
if (cam_periph_acquire(periph) != CAM_REQ_CMP)
return (ENXIO);
softc->flags |= SG_FLAG_OPEN;
}
cam_periph_unlock(periph);
return (error);
}
static int
sgclose(struct cdev *dev, int flag, int fmt, struct thread *td)
{
struct cam_periph *periph;
struct sg_softc *softc;
int error;
periph = (struct cam_periph *)dev->si_drv1;
if (periph == NULL)
return (ENXIO);
softc = (struct sg_softc *)periph->softc;
if ((error = cam_periph_lock(periph, PRIBIO)) != 0)
return (error);
softc->flags &= ~SG_FLAG_OPEN;
cam_periph_unlock(periph);
cam_periph_release(periph);
return (0);
}
static int
sgioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
{
union ccb *ccb;
struct ccb_scsiio *csio;
struct cam_periph *periph;
struct sg_softc *softc;
struct sg_io_hdr req;
int dir, error;
periph = (struct cam_periph *)dev->si_drv1;
if (periph == NULL)
return (ENXIO);
softc = (struct sg_softc *)periph->softc;
error = 0;
switch (cmd) {
case LINUX_SCSI_GET_BUS_NUMBER: {
int busno;
busno = xpt_path_path_id(periph->path);
error = copyout(&busno, arg, sizeof(busno));
break;
}
case LINUX_SCSI_GET_IDLUN: {
struct scsi_idlun idlun;
struct cam_sim *sim;
idlun.dev_id = xpt_path_target_id(periph->path);
sim = xpt_path_sim(periph->path);
idlun.host_unique_id = sim->unit_number;
error = copyout(&idlun, arg, sizeof(idlun));
break;
}
case SG_GET_VERSION_NUM:
case LINUX_SG_GET_VERSION_NUM:
error = copyout(&sg_version, arg, sizeof(sg_version));
break;
case SG_SET_TIMEOUT:
case LINUX_SG_SET_TIMEOUT: {
u_int user_timeout;
error = copyin(arg, &user_timeout, sizeof(u_int));
if (error == 0) {
softc->sg_user_timeout = user_timeout;
softc->sg_timeout = user_timeout / SG_DEFAULT_HZ * hz;
}
break;
}
case SG_GET_TIMEOUT:
case LINUX_SG_GET_TIMEOUT:
/*
* The value is returned directly to the syscall.
*/
td->td_retval[0] = softc->sg_user_timeout;
error = 0;
break;
case SG_IO:
case LINUX_SG_IO:
error = copyin(arg, &req, sizeof(req));
if (error)
break;
if (req.cmd_len > IOCDBLEN) {
error = EINVAL;
break;
}
if (req.iovec_count != 0) {
error = EOPNOTSUPP;
break;
}
ccb = cam_periph_getccb(periph, /*priority*/5);
csio = &ccb->csio;
error = copyin(req.cmdp, &csio->cdb_io.cdb_bytes,
req.cmd_len);
if (error) {
xpt_release_ccb(ccb);
break;
}
switch(req.dxfer_direction) {
case SG_DXFER_TO_DEV:
dir = CAM_DIR_OUT;
break;
case SG_DXFER_FROM_DEV:
dir = CAM_DIR_IN;
break;
case SG_DXFER_TO_FROM_DEV:
dir = CAM_DIR_IN | CAM_DIR_OUT;
break;
case SG_DXFER_NONE:
default:
dir = CAM_DIR_NONE;
break;
}
cam_fill_csio(csio,
/*retries*/1,
sgdone,
dir|CAM_DEV_QFRZDIS,
MSG_SIMPLE_Q_TAG,
req.dxferp,
req.dxfer_len,
req.mx_sb_len,
req.cmd_len,
req.timeout);
error = sgsendccb(periph, ccb);
if (error) {
req.host_status = DID_ERROR;
req.driver_status = DRIVER_INVALID;
xpt_release_ccb(ccb);
break;
}
req.status = csio->scsi_status;
req.masked_status = (csio->scsi_status >> 1) & 0x7f;
sg_scsiio_status(csio, &req.host_status, &req.driver_status);
req.resid = csio->resid;
req.duration = csio->ccb_h.timeout;
req.info = 0;
error = copyout(&req, arg, sizeof(req));
if ((error == 0) && (csio->ccb_h.status & CAM_AUTOSNS_VALID)
&& (req.sbp != NULL)) {
req.sb_len_wr = req.mx_sb_len - csio->sense_resid;
error = copyout(&csio->sense_data, req.sbp,
req.sb_len_wr);
}
xpt_release_ccb(ccb);
break;
case SG_GET_RESERVED_SIZE:
case LINUX_SG_GET_RESERVED_SIZE: {
int size = 32768;
error = copyout(&size, arg, sizeof(size));
break;
}
case SG_GET_SCSI_ID:
case LINUX_SG_GET_SCSI_ID:
{
struct sg_scsi_id id;
id.host_no = 0; /* XXX */
id.channel = xpt_path_path_id(periph->path);
id.scsi_id = xpt_path_target_id(periph->path);
id.lun = xpt_path_lun_id(periph->path);
id.scsi_type = softc->pd_type;
id.h_cmd_per_lun = 1;
id.d_queue_depth = 1;
id.unused[0] = 0;
id.unused[1] = 0;
error = copyout(&id, arg, sizeof(id));
break;
}
case SG_EMULATED_HOST:
case SG_SET_TRANSFORM:
case SG_GET_TRANSFORM:
case SG_GET_NUM_WAITING:
case SG_SCSI_RESET:
case SG_GET_REQUEST_TABLE:
case SG_SET_KEEP_ORPHAN:
case SG_GET_KEEP_ORPHAN:
case SG_GET_ACCESS_COUNT:
case SG_SET_FORCE_LOW_DMA:
case SG_GET_LOW_DMA:
case SG_GET_SG_TABLESIZE:
case SG_SET_FORCE_PACK_ID:
case SG_GET_PACK_ID:
case SG_SET_RESERVED_SIZE:
case SG_GET_COMMAND_Q:
case SG_SET_COMMAND_Q:
case SG_SET_DEBUG:
case SG_NEXT_CMD_LEN:
case LINUX_SG_EMULATED_HOST:
case LINUX_SG_SET_TRANSFORM:
case LINUX_SG_GET_TRANSFORM:
case LINUX_SG_GET_NUM_WAITING:
case LINUX_SG_SCSI_RESET:
case LINUX_SG_GET_REQUEST_TABLE:
case LINUX_SG_SET_KEEP_ORPHAN:
case LINUX_SG_GET_KEEP_ORPHAN:
case LINUX_SG_GET_ACCESS_COUNT:
case LINUX_SG_SET_FORCE_LOW_DMA:
case LINUX_SG_GET_LOW_DMA:
case LINUX_SG_GET_SG_TABLESIZE:
case LINUX_SG_SET_FORCE_PACK_ID:
case LINUX_SG_GET_PACK_ID:
case LINUX_SG_SET_RESERVED_SIZE:
case LINUX_SG_GET_COMMAND_Q:
case LINUX_SG_SET_COMMAND_Q:
case LINUX_SG_SET_DEBUG:
case LINUX_SG_NEXT_CMD_LEN:
default:
#ifdef CAMDEBUG
printf("sgioctl: rejecting cmd 0x%lx\n", cmd);
#endif
error = ENODEV;
break;
}
return (error);
}
static int
sgwrite(struct cdev *dev, struct uio *uio, int ioflag)
{
union ccb *ccb;
struct cam_periph *periph;
struct ccb_scsiio *csio;
struct sg_softc *sc;
struct sg_header *hdr;
struct sg_rdwr *rdwr;
u_char cdb_cmd;
char *buf;
int error = 0, cdb_len, buf_len, dir;
periph = dev->si_drv1;
sc = periph->softc;
rdwr = malloc(sizeof(*rdwr), M_DEVBUF, M_WAITOK | M_ZERO);
hdr = &rdwr->hdr.hdr;
/* Copy in the header block and sanity check it */
if (uio->uio_resid < sizeof(*hdr)) {
error = EINVAL;
goto out_hdr;
}
error = uiomove(hdr, sizeof(*hdr), uio);
if (error)
goto out_hdr;
ccb = xpt_alloc_ccb();
if (ccb == NULL) {
error = ENOMEM;
goto out_hdr;
}
xpt_setup_ccb(&ccb->ccb_h, periph->path, /*priority*/5);
csio = &ccb->csio;
/*
* Copy in the CDB block. The designers of the interface didn't
* bother to provide a size for this in the header, so we have to
* figure it out ourselves.
*/
if (uio->uio_resid < 1)
goto out_ccb;
error = uiomove(&cdb_cmd, 1, uio);
if (error)
goto out_ccb;
if (hdr->twelve_byte)
cdb_len = 12;
else
cdb_len = scsi_group_len(cdb_cmd);
/*
* We've already read the first byte of the CDB and advanced the uio
* pointer. Just read the rest.
*/
csio->cdb_io.cdb_bytes[0] = cdb_cmd;
error = uiomove(&csio->cdb_io.cdb_bytes[1], cdb_len - 1, uio);
if (error)
goto out_ccb;
/*
* Now set up the data block. Again, the designers didn't bother
* to make this reliable.
*/
buf_len = uio->uio_resid;
if (buf_len != 0) {
buf = malloc(buf_len, M_DEVBUF, M_WAITOK | M_ZERO);
error = uiomove(buf, buf_len, uio);
if (error)
goto out_buf;
dir = CAM_DIR_OUT;
} else if (hdr->reply_len != 0) {
buf = malloc(hdr->reply_len, M_DEVBUF, M_WAITOK | M_ZERO);
buf_len = hdr->reply_len;
dir = CAM_DIR_IN;
} else {
buf = NULL;
buf_len = 0;
dir = CAM_DIR_NONE;
}
cam_fill_csio(csio,
/*retries*/1,
sgdone,
dir|CAM_DEV_QFRZDIS,
MSG_SIMPLE_Q_TAG,
buf,
buf_len,
SG_MAX_SENSE,
cdb_len,
sc->sg_timeout);
/*
* Send off the command and hope that it works. This path does not
* go through sgstart because the I/O is supposed to be asynchronous.
*/
rdwr->buf = buf;
rdwr->buf_len = buf_len;
rdwr->tag = hdr->pack_id;
rdwr->ccb = ccb;
rdwr->state = SG_RDWR_INPROG;
ccb->ccb_h.ccb_rdwr = rdwr;
ccb->ccb_h.ccb_type = SG_CCB_RDWR_IO;
TAILQ_INSERT_TAIL(&sc->rdwr_done, rdwr, rdwr_link);
return (sgsendrdwr(periph, ccb));
out_buf:
free(buf, M_DEVBUF);
out_ccb:
xpt_free_ccb(ccb);
out_hdr:
free(rdwr, M_DEVBUF);
return (error);
}
static int
sgread(struct cdev *dev, struct uio *uio, int ioflag)
{
struct ccb_scsiio *csio;
struct cam_periph *periph;
struct sg_softc *sc;
struct sg_header *hdr;
struct sg_rdwr *rdwr;
u_short hstat, dstat;
int error, pack_len, reply_len, pack_id;
periph = dev->si_drv1;
sc = periph->softc;
/* XXX The pack len field needs to be updated and written out instead
* of discarded. Not sure how to do that.
*/
uio->uio_rw = UIO_WRITE;
if ((error = uiomove(&pack_len, 4, uio)) != 0)
return (error);
if ((error = uiomove(&reply_len, 4, uio)) != 0)
return (error);
if ((error = uiomove(&pack_id, 4, uio)) != 0)
return (error);
uio->uio_rw = UIO_READ;
search:
TAILQ_FOREACH(rdwr, &sc->rdwr_done, rdwr_link) {
if (rdwr->tag == pack_id)
break;
}
if ((rdwr == NULL) || (rdwr->state != SG_RDWR_DONE)) {
if (tsleep(rdwr, PCATCH, "sgread", 0) == ERESTART)
return (EAGAIN);
goto search;
}
TAILQ_REMOVE(&sc->rdwr_done, rdwr, rdwr_link);
hdr = &rdwr->hdr.hdr;
csio = &rdwr->ccb->csio;
sg_scsiio_status(csio, &hstat, &dstat);
hdr->host_status = hstat;
hdr->driver_status = dstat;
hdr->target_status = csio->scsi_status >> 1;
switch (hstat) {
case DID_OK:
case DID_PASSTHROUGH:
case DID_SOFT_ERROR:
hdr->result = 0;
break;
case DID_NO_CONNECT:
case DID_BUS_BUSY:
case DID_TIME_OUT:
hdr->result = EBUSY;
break;
case DID_BAD_TARGET:
case DID_ABORT:
case DID_PARITY:
case DID_RESET:
case DID_BAD_INTR:
case DID_ERROR:
default:
hdr->result = EIO;
break;
}
if (dstat == DRIVER_SENSE) {
bcopy(&csio->sense_data, hdr->sense_buffer,
min(csio->sense_len, SG_MAX_SENSE));
#ifdef CAMDEBUG
scsi_sense_print(csio);
#endif
}
error = uiomove(&hdr->result, sizeof(*hdr) -
offsetof(struct sg_header, result), uio);
if ((error == 0) && (hdr->result == 0))
error = uiomove(rdwr->buf, rdwr->buf_len, uio);
xpt_free_ccb(rdwr->ccb);
free(rdwr->buf, M_DEVBUF);
free(rdwr, M_DEVBUF);
return (error);
}
static int
sgsendccb(struct cam_periph *periph, union ccb *ccb)
{
struct sg_softc *softc;
struct cam_periph_map_info mapinfo;
int error, need_unmap = 0;
softc = periph->softc;
if (((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE)
&& (ccb->csio.data_ptr != NULL)) {
bzero(&mapinfo, sizeof(mapinfo));
error = cam_periph_mapmem(ccb, &mapinfo);
if (error)
return (error);
need_unmap = 1;
}
error = cam_periph_runccb(ccb,
sgerror,
CAM_RETRY_SELTO,
SF_RETRY_UA,
softc->device_stats);
if (need_unmap)
cam_periph_unmapmem(ccb, &mapinfo);
return (error);
}
static int
sgsendrdwr(struct cam_periph *periph, union ccb *ccb)
{
struct sg_softc *softc;
softc = periph->softc;
devstat_start_transaction(softc->device_stats, NULL);
xpt_action(ccb);
return (0);
}
static int
sgerror(union ccb *ccb, uint32_t cam_flags, uint32_t sense_flags)
{
struct cam_periph *periph;
struct sg_softc *softc;
periph = xpt_path_periph(ccb->ccb_h.path);
softc = (struct sg_softc *)periph->softc;
return (cam_periph_error(ccb, cam_flags, sense_flags,
&softc->saved_ccb));
}
static void
sg_scsiio_status(struct ccb_scsiio *csio, u_short *hoststat, u_short *drvstat)
{
int status;
status = csio->ccb_h.status;
switch (status & CAM_STATUS_MASK) {
case CAM_REQ_CMP:
*hoststat = DID_OK;
*drvstat = 0;
break;
case CAM_REQ_CMP_ERR:
*hoststat = DID_ERROR;
*drvstat = 0;
break;
case CAM_REQ_ABORTED:
*hoststat = DID_ABORT;
*drvstat = 0;
break;
case CAM_REQ_INVALID:
*hoststat = DID_ERROR;
*drvstat = DRIVER_INVALID;
break;
case CAM_DEV_NOT_THERE:
*hoststat = DID_BAD_TARGET;
*drvstat = 0;
case CAM_SEL_TIMEOUT:
*hoststat = DID_NO_CONNECT;
*drvstat = 0;
break;
case CAM_CMD_TIMEOUT:
*hoststat = DID_TIME_OUT;
*drvstat = 0;
break;
case CAM_SCSI_STATUS_ERROR:
*hoststat = DID_ERROR;
*drvstat = 0;
case CAM_SCSI_BUS_RESET:
*hoststat = DID_RESET;
*drvstat = 0;
break;
case CAM_UNCOR_PARITY:
*hoststat = DID_PARITY;
*drvstat = 0;
break;
case CAM_SCSI_BUSY:
*hoststat = DID_BUS_BUSY;
*drvstat = 0;
default:
*hoststat = DID_ERROR;
*drvstat = DRIVER_ERROR;
}
if (status & CAM_AUTOSNS_VALID)
*drvstat = DRIVER_SENSE;
}
static int
scsi_group_len(u_char cmd)
{
int len[] = {6, 10, 10, 12, 12, 12, 10, 10};
int group;
group = (cmd >> 5) & 0x7;
return (len[group]);
}

144
sys/cam/scsi/scsi_sg.h Normal file
View File

@ -0,0 +1,144 @@
/*
* Structures and definitions for SCSI commands to the SG passthrough device.
*
* $FreeBSD$
*/
#ifndef _SCSI_SG_H
#define _SCSI_SG_H
#define SGIOC '"'
#define SG_SET_TIMEOUT _IO(SGIOC, 0x01)
#define SG_GET_TIMEOUT _IO(SGIOC, 0x02)
#define SG_EMULATED_HOST _IO(SGIOC, 0x03)
#define SG_SET_TRANSFORM _IO(SGIOC, 0x04)
#define SG_GET_TRANSFORM _IO(SGIOC, 0x05)
#define SG_GET_COMMAND_Q _IO(SGIOC, 0x70)
#define SG_SET_COMMAND_Q _IO(SGIOC, 0x71)
#define SG_GET_RESERVED_SIZE _IO(SGIOC, 0x72)
#define SG_SET_RESERVED_SIZE _IO(SGIOC, 0x75)
#define SG_GET_SCSI_ID _IO(SGIOC, 0x76)
#define SG_SET_FORCE_LOW_DMA _IO(SGIOC, 0x79)
#define SG_GET_LOW_DMA _IO(SGIOC, 0x7a)
#define SG_SET_FORCE_PACK_ID _IO(SGIOC, 0x7b)
#define SG_GET_PACK_ID _IO(SGIOC, 0x7c)
#define SG_GET_NUM_WAITING _IO(SGIOC, 0x7d)
#define SG_SET_DEBUG _IO(SGIOC, 0x7e)
#define SG_GET_SG_TABLESIZE _IO(SGIOC, 0x7f)
#define SG_GET_VERSION_NUM _IO(SGIOC, 0x82)
#define SG_NEXT_CMD_LEN _IO(SGIOC, 0x83)
#define SG_SCSI_RESET _IO(SGIOC, 0x84)
#define SG_IO _IO(SGIOC, 0x85)
#define SG_GET_REQUEST_TABLE _IO(SGIOC, 0x86)
#define SG_SET_KEEP_ORPHAN _IO(SGIOC, 0x87)
#define SG_GET_KEEP_ORPHAN _IO(SGIOC, 0x88)
#define SG_GET_ACCESS_COUNT _IO(SGIOC, 0x89)
struct sg_io_hdr {
int interface_id;
int dxfer_direction;
u_char cmd_len;
u_char mx_sb_len;
u_short iovec_count;
u_int dxfer_len;
void *dxferp;
u_char *cmdp;
u_char *sbp;
u_int timeout;
u_int flags;
int pack_id;
void *usr_ptr;
u_char status;
u_char masked_status;
u_char msg_status;
u_char sb_len_wr;
u_short host_status;
u_short driver_status;
int resid;
u_int duration;
u_int info;
};
#define SG_DXFER_NONE -1
#define SG_DXFER_TO_DEV -2
#define SG_DXFER_FROM_DEV -3
#define SG_DXFER_TO_FROM_DEV -4
#define SG_DXFER_UNKNOWN -5
#define SG_MAX_SENSE 16
struct sg_header {
int pack_len;
int reply_len;
int pack_id;
int result;
u_int twelve_byte:1;
u_int target_status:5;
u_int host_status:8;
u_int driver_status:8;
u_int other_flags:10;
u_char sense_buffer[SG_MAX_SENSE];
};
struct sg_scsi_id {
int host_no;
int channel;
int scsi_id;
int lun;
int scsi_type;
short h_cmd_per_lun;
short d_queue_depth;
int unused[2];
};
struct scsi_idlun {
uint32_t dev_id;
uint32_t host_unique_id;
};
/*
* Host codes
*/
#define DID_OK 0x00 /* OK */
#define DID_NO_CONNECT 0x01 /* timeout during connect */
#define DID_BUS_BUSY 0x02 /* timeout during command */
#define DID_TIME_OUT 0x03 /* other timeout */
#define DID_BAD_TARGET 0x04 /* bad target */
#define DID_ABORT 0x05 /* abort */
#define DID_PARITY 0x06 /* parity error */
#define DID_ERROR 0x07 /* internal error */
#define DID_RESET 0x08 /* reset by somebody */
#define DID_BAD_INTR 0x09 /* unexpected interrupt */
#define DID_PASSTHROUGH 0x0a /* passthrough */
#define DID_SOFT_ERROR 0x0b /* low driver wants retry */
#define DID_IMM_RETRY 0x0c /* retry without decreasing retrycnt */
/*
* Driver codes
*/
#define DRIVER_OK 0x00
#define DRIVER_BUSY 0x01
#define DRIVER_SOFT 0x02
#define DRIVER_MEDIA 0x03
#define DRIVER_ERROR 0x04
#define DRIVER_INVALID 0x05
#define DRIVER_TIMEOUT 0x06
#define DRIVER_HARD 0x07
#define DRIVER_SENSE 0x08
#define SUGGEST_RETRY 0x10
#define SUGGEST_ABORT 0x20
#define SUGGEST_REMAP 0x30
#define SUGGEST_DIE 0x40
#define SUGGEST_SENSE 0x80
#define SUGGEST_IS_OK 0xff
#define DRIVER_MASK 0x0f
#define SUGGEST_MASK 0xf0
/* Other definitions */
/* HZ isn't always available, so simulate it */
#define SG_DEFAULT_HZ 1000
#define SG_DEFAULT_TIMEOUT (60*SG_DEFAULT_HZ)
#endif /* !_SCSI_SG_H */

View File

@ -0,0 +1,8 @@
# $FreeBSD$
.PATH: ${.CURDIR}/../../net
KMOD= if_edsc
SRCS= if_edsc.c
.include <bsd.kmod.mk>

354
sys/net/if_edsc.c Normal file
View File

@ -0,0 +1,354 @@
/*-
* Copyright (c) 1982, 1986, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following edsclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following edsclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE EDSCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* From: @(#)if_loop.c 8.1 (Berkeley) 6/10/93
* $FreeBSD$
*/
/*
* Discard interface driver for protocol testing and timing.
* Mimics an Ethernet device so that VLANs can be attached to it etc.
*/
#include <sys/param.h> /* types, important constants */
#include <sys/kernel.h> /* SYSINIT for load-time initializations */
#include <sys/malloc.h> /* malloc(9) */
#include <sys/module.h> /* module(9) */
#include <sys/mbuf.h> /* mbuf(9) */
#include <sys/socket.h> /* struct ifreq */
#include <sys/sockio.h> /* socket ioctl's */
/* #include <sys/systm.h> if you need printf(9) or other all-purpose globals */
#include <net/bpf.h> /* bpf(9) */
#include <net/ethernet.h> /* Ethernet related constants and types */
#include <net/if.h> /* basic part of ifnet(9) */
#include <net/if_clone.h> /* network interface cloning */
#include <net/if_types.h> /* IFT_ETHER and friends */
#include <net/if_var.h> /* kernel-only part of ifnet(9) */
/*
* Software configuration of an interface specific to this device type.
*/
struct edsc_softc {
struct ifnet *sc_ifp; /* ptr to generic interface configuration */
/*
* A non-null driver can keep various things here, for instance,
* the hardware revision, cached values of write-only registers, etc.
*/
};
/*
* Simple cloning methods.
* IFC_SIMPLE_DECLARE() expects precisely these names.
*/
static int edsc_clone_create(struct if_clone *, int, caddr_t);
static void edsc_clone_destroy(struct ifnet *);
/*
* Interface driver methods.
*/
static void edsc_init(void *dummy);
/* static void edsc_input(struct ifnet *ifp, struct mbuf *m); would be here */
static int edsc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data);
static void edsc_start(struct ifnet *ifp);
/*
* We'll allocate softc instances from this.
*/
static MALLOC_DEFINE(M_EDSC, "edsc", "Ethernet discard interface");
/*
* Attach to the interface cloning framework under the name of "edsc".
* The second argument is the number of units to be created from
* the outset. It's also the minimum number of units allowed.
* We don't want any units created as soon as the driver is loaded.
*/
IFC_SIMPLE_DECLARE(edsc, 0);
/*
* Create an interface instance.
*/
static int
edsc_clone_create(struct if_clone *ifc, int unit, caddr_t params)
{
struct edsc_softc *sc;
struct ifnet *ifp;
static u_char eaddr[ETHER_ADDR_LEN]; /* 0:0:0:0:0:0 */
/*
* Allocate soft and ifnet structures. Link each to the other.
*/
sc = malloc(sizeof(struct edsc_softc), M_EDSC, M_WAITOK | M_ZERO);
ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
if (ifp == NULL) {
free(sc, M_EDSC);
return (ENOSPC);
}
ifp->if_softc = sc;
/*
* Get a name for this particular interface in its ifnet structure.
*/
if_initname(ifp, ifc->ifc_name, unit);
/*
* Typical Ethernet interface flags: we can do broadcast and
* multicast but can't hear our own broadcasts or multicasts.
*/
ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX;
/*
* We can pretent we have the whole set of hardware features
* because we just discard all packets we get from the upper layer.
* However, the features are disabled initially. They can be
* enabled via edsc_ioctl() when needed.
*/
ifp->if_capabilities =
IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM |
IFCAP_HWCSUM | IFCAP_TSO |
IFCAP_JUMBO_MTU;
ifp->if_capenable = 0;
/*
* Set the interface driver methods.
*/
ifp->if_init = edsc_init;
/* ifp->if_input = edsc_input; */
ifp->if_ioctl = edsc_ioctl;
ifp->if_start = edsc_start;
/*
* Set the maximum output queue length from the global parameter.
*/
ifp->if_snd.ifq_maxlen = ifqmaxlen;
/*
* Do ifnet initializations common to all Ethernet drivers
* and attach to the network interface framework.
* TODO: Pick a non-zero link level address.
*/
ether_ifattach(ifp, eaddr);
/*
* Now we can mark the interface as running, i.e., ready
* for operation.
*/
ifp->if_drv_flags |= IFF_DRV_RUNNING;
return (0);
}
/*
* Destroy an interface instance.
*/
static void
edsc_clone_destroy(struct ifnet *ifp)
{
struct edsc_softc *sc = ifp->if_softc;
/*
* Detach from the network interface framework.
*/
ether_ifdetach(ifp);
/*
* Free memory occupied by ifnet and softc.
*/
if_free(ifp);
free(sc, M_EDSC);
}
/*
* This method is invoked from ether_ioctl() when it's time
* to bring up the hardware.
*/
static void
edsc_init(void *dummy)
{
#if 0 /* what a hardware driver would do here... */
struct edsc_soft *sc = (struct edsc_softc *)dummy;
struct ifnet *ifp = sc->sc_ifp;
/* blah-blah-blah */
#endif
}
/*
* Network interfaces are controlled via the ioctl(2) syscall.
*/
static int
edsc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct ifreq *ifr = (struct ifreq *)data;
switch (cmd) {
case SIOCSIFCAP:
#if 1
/*
* Just turn on any capabilities requested.
* The generic ifioctl() function has already made sure
* that they are supported, i.e., set in if_capabilities.
*/
ifp->if_capenable = ifr->ifr_reqcap;
#else
/*
* A h/w driver would need to analyze the requested
* bits and program the hardware, e.g.:
*/
mask = ifp->if_capenable ^ ifr->ifr_reqcap;
if (mask & IFCAP_VLAN_HWTAGGING) {
ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
if (ifp->if_capenable & IFCAP_VLAN_HWTAGGING)
/* blah-blah-blah */
else
/* etc-etc-etc */
}
#endif
break;
default:
/*
* Offload the rest onto the common Ethernet handler.
*/
return (ether_ioctl(ifp, cmd, data));
}
return (0);
}
/*
* Process the output queue.
*/
static void
edsc_start(struct ifnet *ifp)
{
struct mbuf *m;
/*
* A hardware interface driver can set IFF_DRV_OACTIVE
* in ifp->if_drv_flags:
*
* ifp->if_drv_flags |= IFF_DRV_OACTIVE;
*
* to prevent if_start from being invoked again while the
* transmission is under way. The flag is to protect the
* device's transmitter, not the method itself. The output
* queue is locked and several threads can process it in
* parallel safely, so the driver can use other means to
* serialize access to the transmitter.
*
* If using IFF_DRV_OACTIVE, the driver should clear the flag
* not earlier than the current transmission is complete, e.g.,
* upon an interrupt from the device, not just before returning
* from if_start. This method merely starts the transmission,
* which may proceed asynchronously.
*/
/*
* We loop getting packets from the queue until it's empty.
* A h/w driver would loop until the device can accept more
* data into its buffer, or while there are free transmit
* descriptors, or whatever.
*/
for (;;) {
/*
* Try to dequeue one packet. Stop if the queue is empty.
* Use IF_DEQUEUE() here if ALTQ(9) support is unneeded.
*/
IFQ_DEQUEUE(&ifp->if_snd, m);
if (m == NULL)
break;
/*
* Let bpf(9) at the packet.
*/
BPF_MTAP(ifp, m);
/*
* Update the interface counters.
*/
ifp->if_obytes += m->m_pkthdr.len;
ifp->if_opackets++;
/*
* Finally, just drop the packet.
* TODO: Reply to ARP requests unless IFF_NOARP is set.
*/
m_freem(m);
}
/*
* ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
* would be here only if the transmission were synchronous.
*/
}
/*
* This function provides handlers for module events, namely load and unload.
*/
static int
edsc_modevent(module_t mod, int type, void *data)
{
switch (type) {
case MOD_LOAD:
/*
* Connect to the network interface cloning framework.
*/
if_clone_attach(&edsc_cloner);
break;
case MOD_UNLOAD:
/*
* Disconnect from the cloning framework.
* Existing interfaces will be disposed of properly.
*/
if_clone_detach(&edsc_cloner);
break;
default:
/*
* There are other event types, but we don't handle them.
* See module(9).
*/
return (EOPNOTSUPP);
}
return (0);
}
static moduledata_t edsc_mod = {
"if_edsc", /* name */
edsc_modevent, /* event handler */
NULL /* additional data */
};
DECLARE_MODULE(if_edsc, edsc_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);

43
sys/sys/_sx.h Normal file
View File

@ -0,0 +1,43 @@
/*-
* Copyright (c) 2007 Attilio Rao <attilio@freebsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice(s), this list of conditions and the following disclaimer as
* the first lines of this file unmodified other than the possible
* addition of one or more copyright notices.
* 2. Redistributions in binary form must reproduce the above copyright
* notice(s), this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* $FreeBSD$
*/
#ifndef _SYS__SX_H_
#define _SYS__SX_H_
/*
* Shared/exclusive lock main structure definition.
*/
struct sx {
struct lock_object lock_object;
volatile uintptr_t sx_lock;
volatile unsigned sx_recurse;
};
#endif /* !_SYS__SX_H_ */

View File

@ -0,0 +1,3 @@
data
for validation
of sed(1)

View File

@ -0,0 +1,4 @@
foo
foo
foo
foo

View File

@ -0,0 +1,4 @@
input
data
for validation
foo

View File

@ -0,0 +1,3 @@
input
data
foo

View File

@ -0,0 +1,3 @@
input
data
foo