|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T t
Length: 25939 (0x6553)
Types: TextFile
Names: »transport.c«
└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
└─⟦2fafebccf⟧ »EurOpenD3/mail/smail3.1.19.tar.Z«
└─⟦bcd2bc73f⟧
└─⟦this⟧ »src/transport.c«
/* @(#)transport.c 3.25 2/20/89 04:06:27 */
/*
* Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
*
* See the file COPYING, distributed with smail, for restriction
* and warranty information.
*/
/*
* transport.c:
* process and deliver remote addresses.
*
* The routines in this file are used after an address has been
* routed and resolved and associated with a particular transport.
* This file provides some support routines for transport drivers.
*
* external functions: assign_transports, call_transports,
* write_message, remote_from_line, local_from_line,
* find_transport, find_transport_driver
*/
#include <stdio.h>
#include <ctype.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "defs.h"
#include "smail.h"
#include "addr.h"
#include "transport.h"
#include "log.h"
#include "spool.h"
#include "dys.h"
#include "exitcodes.h"
#include "smailconf.h"
#ifndef DEPEND
# include "extern.h"
# include "debug.h"
# include "error.h"
#endif
#ifdef UNIX_BSD
# include <sys/file.h>
#endif
/* variables exported from this file */
char *path_to_sender = NULL; /* uucp-style route to sender */
int cached_transports = TRUE; /* TRUE if cache_transports() called */
/* functions local to this file */
static int assign_compare();
static int write_bsmtp_prologue();
static int write_smtp_addr();
static int write_bsmtp_epilogue();
static void build_path_to_sender();
static char *transport_driv_function();
\f
/*
* assign_transports - assign instances of transports for addresses
*
* given a list of addresses as input, produce a list assigning a list
* of remote addresses to specific instances of a transport. One
* instance essentially maps onto exactly one call to the underlying
* transport driver.
*
* NOTE: This routine breaks the forward links between addr structures.
*/
struct assign_transport *
assign_transports(list)
struct addr *list; /* list of addresses */
{
extern void qsort();
register struct addr *aq; /* temp */
register struct assign_transport *asn; /* list of assigned transports */
register struct addr **aap; /* points to array for sorting */
register int i; /* index */
int ct; /* count of items in input list */
int host_ct; /* count of hosts in instance */
int addr_ct; /* count of addrs in instance */
int char_ct = 0; /* count of chars in instance */
struct transport *last_transport = NULL; /* last assigned transport */
char *last_host; /* last assigned host */
/* count the number of input items */
for (ct = 0, aq = list; aq; aq = aq->succ) {
ct++;
}
DEBUG1(DBG_REMOTE_HI, "assign_transports called with %d addrs\n", ct);
/* create an array and copy remote information to it for sorting */
aap = (struct addr **)xmalloc(ct * sizeof(*aap));
for (aq = list, i = 0; aq; aq = aq->succ, i++) {
aap[i] = aq;
}
/* NOTE: qsort does not appear to return anything */
qsort((char *)aap, ct, sizeof(*aap), assign_compare);
/*
* we now have an array sorted by transport and host.
* pass through that array and assign addr structures to
* specific transport instances, taking into account
* max_addr and max_hosts for the transports.
*/
for (asn = NULL, i = ct-1; i >= 0; --i) {
/* possible reasons to switch to a new instance */
if (
/* we have a different transport */
aap[i]->transport != last_transport ||
/* maximum number of hosts per transport call exceeded */
(last_transport->max_hosts &&
aap[i]->next_host && last_host &&
!EQ(aap[i]->next_host, last_host) &&
last_transport->max_hosts < ++host_ct) ||
/* maximum number of addrs per transport call exceeded */
(last_transport->max_addrs &&
last_transport->max_addrs < ++addr_ct) ||
/* maximum number of chars of addr per call exceeded */
(last_transport->max_chars &&
last_transport->max_chars <
(char_ct += strlen(aap[i]->next_addr) + 3)))
{
/* initialize for a new instance */
struct assign_transport *newasn;
DEBUG1(DBG_REMOTE_HI, "new instance ... transport=<%s>\n",
aap[i]->transport->name);
newasn = (struct assign_transport *)xmalloc(sizeof(*newasn));
last_transport = aap[i]->transport;
last_host = aap[i]->next_host;
host_ct = 1;
addr_ct = 1;
char_ct = strlen(aap[i]->next_addr) + 3;
/* fill in the first entry of the instance */
aap[i]->succ = NULL;
newasn->succ = asn;
asn = newasn;
} else {
/* add the addr to the previous instance */
aap[i]->succ = asn->addr;
}
asn->addr = aap[i];
DEBUG2(DBG_REMOTE_HI, "adding...host=<%s>, addr=<%s>\n",
asn->addr->next_host, asn->addr->next_addr);
}
/* all done, free temps and return the list */
xfree((char *)aap);
return asn;
}
/*
* compare two struct addrs for transport and then hostname
*/
static int
assign_compare(a, b)
struct addr **a;
struct addr **b;
{
register int ret = strcmp((*a)->transport->name, (*b)->transport->name);
if (ret) {
/* transports are unequal, return value just based on that */
return ret;
}
/* if next_host not defined, the addrs are equal */
if ((*a)->next_host == NULL || (*b)->next_host == NULL) {
return 0;
}
/* otherwise, return value is based on host */
return strcmpic((*a)->next_host, (*b)->next_host);
}
\f
/*
* call_transports - call transport drivers for assigned transports
*
* given a list produced by assign_transports, call the transport drivers
* to actually perform delivery to addresses.
*/
void
call_transports(asn, defer, fail)
register struct assign_transport *asn; /* list of assigned instances */
struct addr **defer; /* addrs to defer to a later time */
struct addr **fail; /* unresolvable addrs */
{
struct addr *succeed; /* succeeded addrs from drivers */
struct addr *tp_fail; /* fail addrs from drivers */
struct addr *shadow_list = NULL; /* shadow delivery list */
struct assign_transport *sh_asn; /* shadow transport instances */
for (; asn; asn = asn->succ) {
struct transport_driver *driver;
register struct transport *tp = asn->addr->transport;
/*
* don't deliver to remote transports if the hop_count would
* pass beyond maximum.
*/
if (hop_count >= max_hop_count && !(tp->flags&LOCAL_TPORT)) {
/*
* ERR_145 - maximum hop count exceeded
*
* DESCRIPTION
* If a remote transport were performed, the maximum hop
* count, as set by the max_hop_cound attribute in the
* config file, would be exceeded. This condition is
* assumed to be a result of a loop condition where two
* machines are sending mail back and forth to each other.
*
* ACTIONS
* An error message is sent back to the sender stating the
* error. Note that if one of the sites back to the sender
* has a shorter value for max_hop_count, then the error
* message will fail to reach the sender though, hopefully,
* a message will be sent back to the postmaster at the
* local site.
*
* RESOLUTION
* Determine which two sites are causing the loop condition
* and remove or correct the forward or alias from one of
* those two sites. If the problem is not actually a loop
* condition but a long path, a shorter path should be
* used. It is unlikely that paths longer than 10 or 15
* hops should ever be required.
*/
struct error *loop_error =
note_error(ERR_NSENDER|ERR_145,
"loop detection: maximum hop count exceeded");
register struct addr *cur;
/* NOTE: the sequence of operations here IS correct */
for (cur = asn->addr; cur; cur = cur->succ) {
cur->error = loop_error;
}
fail_delivery(asn->addr);
insert_addr_list(asn->addr, fail, loop_error);
continue;
}
/* lookup the driver */
driver = find_transport_driver(tp->driver);
if (driver == NULL) {
/* configuration error, the driver does not exist */
/*
* ERR_146 - transport driver not found
*
* DESCRIPTION
* The driver name was not in the table of transport
* drivers.
*
* ACTIONS
* Defer addresses with configuration errors.
*
* RESOLUTION
* The postmaster must check the transport configuration
* before delivery can be performed.
*/
insert_addr_list(asn->addr,
defer,
note_error(ERR_CONFERR|ERR_146,
xprintf(
"transport %s: driver %s not found",
tp->name, tp->driver)));
continue;
}
/* call the transport */
DEBUG2(DBG_REMOTE_LO, "transport %s uses driver %s\n",
tp->name, tp->driver);
succeed = NULL;
tp_fail = NULL;
(*driver->driver)(asn->addr, &succeed, defer, &tp_fail);
/* look at successful deliveries */
if (succeed) {
succeed_delivery(succeed); /* log them */
if (tp->shadow) {
/* on successful delivery, call the shadow transport */
struct transport *shadow_tp = find_transport(tp->shadow);
if (shadow_tp == NULL) {
send_to_postmaster = TRUE;
write_log(LOG_SYS|LOG_MLOG,
"transport %s: shadow transport %s not found",
tp->name, tp->shadow);
} else {
/* insert in shadow delivery list, with new transport */
register struct addr *cur;
register struct addr *next;
for (cur = succeed; cur; cur = next) {
next = cur->succ;
cur->flags |= ADDR_SHADOW;
cur->transport = shadow_tp;
cur->succ = shadow_list;
shadow_list = cur;
}
}
}
}
/* look at failed deliveries */
if (tp_fail) {
fail_delivery(tp_fail); /* log them */
if (tp->error_transport == NULL) {
insert_addr_list(tp_fail, fail, (struct error *)NULL);
} else {
/* on failed delivery, call the error transport */
struct transport *error_tp =
find_transport(tp->error_transport);
if (error_tp == NULL) {
send_to_postmaster = TRUE;
write_log(LOG_SYS|LOG_MLOG,
"transport %s: error transport %s not found",
tp->name, tp->error_transport);
} else {
/* insert in shadow delivery list, with new transport */
register struct addr *cur;
register struct addr *next;
for (cur = tp_fail; cur; cur = next) {
next = cur->succ;
cur->transport = error_tp;
cur->succ = shadow_list;
shadow_list = cur;
}
}
}
}
}
/* if there are shadow deliveries to be made, assign them and transport */
if (shadow_list == NULL) {
return; /* no shadows, just return */
}
/* assign shadow deliveries */
for (sh_asn = assign_transports(shadow_list);
sh_asn;
sh_asn = sh_asn->succ)
{
struct transport_driver *driver;
register struct transport *tp = sh_asn->addr->transport;
struct addr *dummy_defer;
/* lookup the driver */
driver = find_transport_driver(tp->driver);
if (driver == NULL) {
/* configuration error: for shadows, send to postmaster */
send_to_postmaster = TRUE;
write_log(LOG_SYS|LOG_MLOG,
"shadow transport %s: driver %s not found",
tp->name, tp->driver);
continue;
}
/* call the shadow transport */
DEBUG2(DBG_REMOTE_LO, "calling driver %s from shadow transport %s\n",
tp->driver, tp->name);
succeed = NULL;
tp_fail = NULL;
(*driver->driver)(sh_asn->addr, &succeed, &dummy_defer, &tp_fail);
/* log failed addresses */
if (fail) {
struct addr *cur;
struct addr *next;
for (cur = tp_fail; cur; cur = next) {
next = cur->succ;
write_log(LOG_SYS, "%s ... shadow transport %s failed: %s",
cur->in_addr, cur->transport->name, cur->next_addr);
if ((cur->flags & ADDR_SHADOW) == 0) {
/*
* put failed deliveries from an error_transport on
* output fail list
*/
cur->succ = *fail;
*fail = cur;
}
}
}
}
}
\f
/*
* cache_transports - call cache entrypoints for all transports
*
* cache information used by transport drivers. This can be called
* when it is determined that there will be an attempt to deliver more
* than one mail message, to increase the overall efficiency of the
* mailer.
*
* Daemons can call this periodically to recache stale data.
*/
void
cache_transports()
{
struct transport *tp; /* temp for stepping thru transports */
struct transport_driver *driver;
for (tp = transports; tp; tp = tp->succ) {
driver = find_transport_driver(tp->driver);
if (driver && driver->cache) {
(*driver->cache)(tp);
}
}
cached_transports = TRUE;
}
#ifdef notyet
/*
* finish_transports - free resources used by all directors
*
* free information that was cached by transports or used by
* transports in the process of delivering messages. Transports can
* cache data for efficiency, or can maintain state between
* invocations. This function is called when transports will no
* longer be needed, allowing transports to free any resources that
* they were using that will no longer be needed. For example, it is
* a good idea for transports to close any files that they opened, as
* file descriptors are a precious resource in some machines.
*/
void
finish_transports()
{
struct transports *tp; /* temp for stepping thru transports */
struct transport_driver *driver;
for (tp = transports; tp; tp = tp->succ) {
driver = find_transport_driver(tp->driver);
if (driver->finish) {
(*driver->finish)(dp);
}
}
cached_transports = FALSE;
}
#endif
\f
/*
* write_message - write out the current message to an open file
*
* this takes a transport file entry and a list of addr structures
* and writes the message in the manner specified in the transport
* file entry. It is intended to be called from transport drivers.
*
* If BSMTP_TPORT is set in the transport flags, an SMTP envelope
* is placed around the message. However, result codes are not
* read from the destination so this is not useful for interactive
* SMTP. The list of addr structures is only used for writing the
* SMTP envelope. HBSMTP_TPORT generates an SMTP envelope without
* the initial HELO or the final QUIT commands.
*
* NOTE: Everybody still needs to call fflush after calling
* write_message().
*
* return READ_FAIL, WRITE_FAIL or SUCCEED
*/
int
write_message(f, tp, addr)
FILE *f; /* write on this file */
struct transport *tp; /* transport file entry */
struct addr *addr; /* list of addresses */
{
int fail; /* returned failure codes */
/*
* put out the initial smtp commands up to the DATA command
*/
if (tp->flags & (BSMTP_TPORT | HBSMTP_TPORT)) {
if (write_bsmtp_prologue(f, tp, addr) == FAIL) {
return WRITE_FAIL;
}
tp->flags |= PUT_DOTS; /* force use of SMTP dot protocol */
}
/*
* write out a From<space> line, if required
*/
if (tp->flags & PUT_FROM) {
/* but what form do we need? */
if (tp->flags & LOCAL_TPORT) {
if (fputs(local_from_line(), f) == EOF) {
return WRITE_FAIL;
}
} else {
if (fputs(remote_from_line(), f) == EOF) {
return WRITE_FAIL;
}
}
/* put the end of line */
if (tp->flags & PUT_CRLF) {
if (putc('\r', f) == EOF) {
return WRITE_FAIL;
}
}
if (putc('\n', f) == EOF) {
return WRITE_FAIL;
}
}
/*
* write out the header according to the transport flags
*/
if (write_header(f, tp->flags) == FAIL) {
return WRITE_FAIL;
}
if ((tp->flags & PUT_CRLF) && putc('\r', f) == EOF) {
return WRITE_FAIL;
}
if (putc('\n', f) == EOF) {
return WRITE_FAIL;
}
if (tp->flags & DEBUG_TPORT) {
/* if we are debugging, don't dump the body, dump interesting
* debugging info instead */
extern char **save_argv; /* import this from main */
char **av;
(void) fprintf(f, "|---- original flags ----|\n");
for (av = save_argv; *av; av++) {
(void) fprintf(f, "%s\n", *av);
}
(void) fprintf(f, "|---- interesting info ----|\n");
(void) fprintf(f,"\
message_id=%s\n\
spool_dir=%s\n\
spool_fn=%s\n\
transport=%s\n\
next_host=%s\n\
msg_grade=%s\n",
message_id,
spool_dir,
spool_fn,
tp->name,
addr->next_host,
msg_grade);
send_log(f, TRUE, "|---- per-message log ----|\n");
} else {
/*
* write out the body.
*/
if ((fail = write_body(f, tp->flags)) != SUCCEED) {
return fail;
}
}
/*
* finish off the SMTP envelope
*/
if (tp->flags & (BSMTP_TPORT | HBSMTP_TPORT)) {
if (write_bsmtp_epilogue(f, tp) == FAIL) {
return WRITE_FAIL;
}
}
return SUCCEED; /* everything went fine */
}
/*
* write_bsmtp_prologue - write the initial SMTP commands for BSMTP transports
*
* write out the HELO, MAIL FROM, RCPT TO and DATA commands.
*/
static int
write_bsmtp_prologue(f, tp, addr)
FILE *f; /* file to write commands to */
struct transport *tp; /* transport entry */
struct addr *addr; /* recipient addr structures */
{
/*
* how do we end a line?
*/
char *eol = (tp->flags & PUT_CRLF)? "\r\n": "\n";
char *error;
if (! (tp->flags & HBSMTP_TPORT) &&
fprintf(f, "HELO %s%s", primary_name, eol) == EOF)
{
return FAIL;
}
/*
* determine which sender form is appropriate
*/
if (error_sender) {
/* if the special SMTP sender form <> was given, pass it along */
if (fprintf(f, "MAIL FROM:<>%s", eol) == EOF) {
return FAIL;
}
} else if (tp->flags & LOCAL_TPORT) {
if (fprintf(f, "MAIL FROM:<%s>%s", sender, eol) == EOF) {
return FAIL;
}
} else if (tp->flags & UUCP_ONLY) {
char *p = build_partial_uucp_route(sender, &error);
if (fprintf(f, "MAIL FROM:<%s!%s>%s",
uucp_name, p? p: sender, eol) == EOF)
{
if (p) {
xfree(p);
}
return FAIL;
}
if (p) {
xfree(p);
}
} else {
if (fputs("MAIL FROM:<", f) == EOF ||
write_smtp_addr(f, primary_name, sender) == EOF ||
fprintf(f, ">%s", eol) == EOF)
{
return FAIL;
}
}
/*
* write out a RCPT TO: line for each recipient address
*/
while (addr) {
if (tp->flags & LOCAL_TPORT) {
if (fprintf(f, "RCPT TO:<%s>%s", addr->next_addr, eol) == EOF) {
return FAIL;
}
} else if (tp->flags & UUCP_ONLY) {
char *p = build_partial_uucp_route(addr->next_addr);
if (fprintf(f, "RCPT TO:<%s!%s>%s", addr->next_host,
p? p: addr->next_addr, eol) == EOF)
{
return FAIL;
}
} else {
if (fputs("RCPT TO:<", f) == EOF ||
write_smtp_addr(f, addr->next_host, addr->next_addr) == EOF ||
fprintf(f, ">%s", eol) == EOF)
{
return FAIL;
}
}
addr = addr->succ;
}
if (fprintf(f, "DATA%s", eol) == EOF) {
return FAIL;
}
return SUCCEED;
}
/*
* write_smtp_addr - write out an address in SMTP form
*
* write out an address with the destination host included, as expected
* by SMTP.
*/
static int
write_smtp_addr(f, host, addr)
FILE *f; /* output */
char *host; /* destination host */
char *addr; /* address beyond host */
{
register char *p;
if (host == NULL) {
/* trivial case */
return fputs(addr, f);
}
p = back_address_token(addr, addr + strlen(addr));
if (p == NULL || p <= addr + sizeof("@") || p[-1] != '@') {
return fprintf(f, "%s@%s", addr, host);
}
if (addr[0] == '@') {
return fprintf(f, "@%s,%s", host, addr);
}
return fprintf(f, "@%s:%s", host, addr);
}
/*
* write_bsmtp_epilogue - write tailing commands for BSMTP transports
*
* finish off the data command and write out the QUIT command.
*/
static int
write_bsmtp_epilogue(f, tp)
FILE *f;
struct transport *tp;
{
if (tp->flags & HBSMTP_TPORT) {
if (fprintf(f, (tp->flags&PUT_CRLF)? ".\r\n": ".\n") == EOF) {
return FAIL;
}
return SUCCEED;
}
if (tp->flags & PUT_CRLF) {
if (fprintf(f, ".\r\nQUIT\r\n") == EOF) {
return FAIL;
}
} else {
if (fprintf(f, ".\nQUIT\n") == EOF) {
return FAIL;
}
}
return SUCCEED;
}
\f
/*
* remote_from_line - build a From_ line for remote transports
*/
char *
remote_from_line()
{
static char *from_line = NULL; /* saved From_ line */
if (from_line) {
return from_line; /* exists, use it */
}
if (path_to_sender == NULL) {
build_path_to_sender();
}
from_line = xprintf("From %s %s remote from %s",
path_to_sender, unix_date(), uucp_name);
return from_line;
}
/*
* local_from_line - build a From_ line for local transports
*/
char *
local_from_line()
{
static char *from_line = NULL; /* saved From_ line */
if (from_line) {
return from_line; /* exists, use it */
}
if (path_to_sender == NULL) {
build_path_to_sender();
}
from_line = xprintf("From %s %s", path_to_sender, unix_date());
return from_line;
}
/*
* build_path_to_sender - build a pure !-route version of sender address
*/
static void
build_path_to_sender()
{
char *error;
path_to_sender = build_uucp_route(sender, &error);
if (path_to_sender == NULL) {
write_log(LOG_SYS|LOG_MLOG,
"error building path to sender, sender=%s, error=%s",
sender, error);
path_to_sender = "Postmaster";
}
}
\f
/*
* find_transport - given a transport's name, return the transport structure
*
* return NULL if no transport of that name exists.
*/
struct transport *
find_transport(name)
register char *name; /* search key */
{
register struct transport *tp; /* temp for stepping thru transports */
/* loop through all the transports */
for (tp = transports; tp; tp = tp->succ) {
if (EQ(tp->name, name)) {
/* found the transport in question */
return tp;
}
}
return NULL; /* transport not found */
}
/*
* find_transport_driver - given a driver's name, return the driver structure
*
* return NULL if driver does not exist.
*/
struct transport_driver *
find_transport_driver(name)
register char *name; /* search key */
{
register struct transport_driver *tdp; /* pointer to table of drivers */
for (tdp = transport_drivers; tdp->name; tdp++) {
if (EQ(tdp->name, name)) {
return tdp; /* found the driver */
}
}
return NULL; /* driver not found */
}
\f
/*
* read_transport_file - read transport file
*
* read the transport file and build the transport list describing the
* entries. Return an error message or NULL.
*/
char *
read_transport_file()
{
FILE *f; /* open transport file */
char *error; /* error from read_standard_file() */
struct stat statbuf;
static struct attr_table transport_generic[] = {
{ "driver", t_string, NULL, NULL, OFFSET(transport, driver) },
{ "max_addrs", t_int, NULL, NULL, OFFSET(transport, max_addrs) },
{ "max_hosts", t_int, NULL, NULL, OFFSET(transport, max_hosts) },
{ "max_chars", t_int, NULL, NULL, OFFSET(transport, max_chars) },
{ "shadow", t_string, NULL, NULL, OFFSET(transport, shadow) },
{ "error_transport", t_string, NULL, NULL,
OFFSET(transport, error_transport) },
{ "strict", t_boolean, NULL, NULL, STRICT_TPORT },
{ "uucp", t_boolean, NULL, NULL, UUCP_ONLY },
{ "received", t_boolean, NULL, NULL, PUT_RECEIVED },
{ "return_path", t_boolean, NULL, NULL, PUT_RETURNPATH },
{ "from", t_boolean, NULL, NULL, PUT_FROM },
{ "local", t_boolean, NULL, NULL, LOCAL_TPORT },
{ "crlf", t_boolean, NULL, NULL, PUT_CRLF },
{ "bsmtp", t_boolean, NULL, NULL, BSMTP_TPORT },
{ "hbsmtp", t_boolean, NULL, NULL, HBSMTP_TPORT },
{ "dots", t_boolean, NULL, NULL, PUT_DOTS },
{ "debug", t_boolean, NULL, NULL, DEBUG_TPORT },
{ "unix_from_hack", t_boolean, NULL, NULL, UNIX_FROM_HACK },
{ "uucp_from_hack", t_boolean, NULL, NULL, UNIX_FROM_HACK },
};
struct attr_table *end_transport_generic = ENDTABLE(transport_generic);
static struct transport transport_template = {
NULL, /* name */
"pipe", /* driver, a reasonable default */
NULL, /* succ will be assigned */
PUT_RECEIVED, /* flags */
1, /* max_addrs */
1, /* max_hosts */
2000, /* max_chars, about half of NCARGS? */
NULL, /* shadow */
NULL, /* error_transport */
NULL, /* private */
};
/*
* try to open the transport file, stat file if possible
*/
if (transport_file == NULL || EQ(transport_file, "-")) {
return NULL;
}
f = fopen(transport_file, "r");
if (f == NULL) {
if (require_configs) {
return xprintf("%s: %s", transport_file, strerrno());
}
add_config_stat(transport_file, (struct stat *)NULL);
return NULL;
}
(void)fstat(fileno(f), &statbuf);
add_config_stat(transport_file, &statbuf);
/* call read_standard_file to do the real work */
error = read_standard_file(f,
(char *)&transport_template,
sizeof(struct transport),
OFFSET(transport, name),
OFFSET(transport, flags),
OFFSET(transport, succ),
transport_generic,
end_transport_generic,
transport_driv_function,
(char **)&transports);
/* finish up */
(void) fclose(f);
/* return any error message */
if (error) {
return xprintf("%s: %s", transport_file, error);
}
return NULL;
}
static char *
transport_driv_function(struct_p, driver_attrs)
char *struct_p; /* passed transport structure */
struct attribute *driver_attrs; /* driver-specific attributes */
{
struct transport *tp = (struct transport *)struct_p;
struct transport_driver *drv;
if (tp->driver == NULL) {
return xprintf("transport %s: no driver attribute", tp->name);
}
drv = find_transport_driver(tp->driver);
if (drv == NULL) {
return xprintf("transport %s: unknown driver: %s",
tp->name, tp->driver);
}
if (drv->builder) {
return (*drv->builder)(tp, driver_attrs);
}
return NULL;
}