|
|
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 q
Length: 11580 (0x2d3c)
Types: TextFile
Names: »queryprog.c«
└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
└─⟦2fafebccf⟧ »EurOpenD3/mail/smail3.1.19.tar.Z«
└─⟦bcd2bc73f⟧
└─⟦this⟧ »src/routers/queryprog.c«
/* @(#)queryprog.c 1.7 11/13/88 08:46:47 */
/*
* Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
*
* See the file COPYING, distributed with smail, for restriction
* and warranty information.
*/
/*
* queryprog.c:
*
* Specifications of the queryprogram routing driver:
*
* associated transports:
* No specific transport is set.
*
* private data:
* cmd - command to execute, include $host for hostname
* domain - domains to strip from the end of the target
* required - required domain names
* hash_len - hash slots in internal hash table
*
* private flags:
* read_path - read a path from the stdout of the program
* read_transport - read a transport from stdout of the program
*
* algorithm:
* Expand the given command, with $host expanding to the host
* that is being routed. Then execute that command and check
* the exit status. A zero exit status represents a match, a
* non-zero exit status represents no match.
*
* Always returns one-hop routes; i.e., a next_host value is
* returned, but no route.
*
* notes:
* The match status values are cached.
*/
#include <stdio.h>
#include <ctype.h>
#include "defs.h"
#include "../smail.h"
#include "../smailconf.h"
#include "../parse.h"
#include "../addr.h"
#include "../route.h"
#include "../transport.h"
#include "../hash.h"
#include "../child.h"
#include "../lookup.h"
#include "../dys.h"
#include "rtlib.h"
#include "queryprog.h"
#ifndef DEPEND
# include "../extern.h"
# include "../debug.h"
# include "../error.h"
#endif
/* functions local to this file */
static int queryprogram_lookup();
static int decode_query_output();
static struct error *parse_error();
static struct error *temp_command_error();
static struct error *output_parse_error();
\f
/*
* rtd_queryprogram - call a program to verify neighboring hostnames
*/
/* ARGSUSED */
void
rtd_queryprogram(rp, in, out, defer, fail)
struct router *rp; /* router table entry */
struct addr *in; /* input addr structures */
struct addr **out; /* non-failed addr structures */
struct addr **defer; /* addrs to defer to a later time */
struct addr **fail; /* unresolvable addrs */
{
rtd_standard(rp, in, out, defer, fail, queryprogram_lookup);
}
/*
* rtv_queryprogram - verify by calling a program.
*/
/*ARGSUSED*/
void
rtv_queryprogram(rp, in, retry, okay, defer, fail)
struct router *rp; /* router entry */
struct addr *in; /* input local-form addrs */
struct addr **retry; /* output list of unmatched addrs */
struct addr **okay; /* output list of verified addrs */
struct addr **defer; /* temporariliy unverifiable addrs */
struct addr **fail; /* unverified addrs */
{
rtv_standard(rp, in, retry, okay, defer, fail, queryprogram_lookup);
}
/*
* rtb_queryprogram - read the configuration file attributes
*/
char *
rtb_queryprogram(rp, attrs)
struct router *rp; /* director entry being defined */
struct attribute *attrs; /* list of per-driver attributes */
{
char *error;
static struct attr_table queryprogram_attributes[] = {
{ "cmd", t_string, NULL, NULL, OFFSET(queryprogram_private, cmd) },
{ "domain", t_string, NULL, NULL,
OFFSET(queryprogram_private, domain) },
{ "required", t_string, NULL, NULL,
OFFSET(queryprogram_private, required) },
{ "hash_table_len", t_int, NULL, NULL,
OFFSET(queryprogram_private, hash_table_len) },
{ "read_path", t_boolean, NULL, NULL, QP_READ_PATH },
{ "read_transport", t_boolean, NULL, NULL, QP_READ_TRANSPORT },
};
static struct attr_table *end_queryprogram_attributes =
ENDTABLE(queryprogram_attributes);
static struct queryprogram_private queryprogram_template = {
"/usr/bin/uuinfo -q $host", /* cmd */
NULL, /* domain */
NULL, /* required */
20, /* hash_table_len */
NULL, /* cache - for internal use */
};
struct queryprogram_private *priv; /* new queryprogram_private struct */
/* copy the template private data */
priv = (struct queryprogram_private *)xmalloc(sizeof(*priv));
(void)memcpy((char *)priv, (char *)&queryprogram_template, sizeof(*priv));
rp->private = (char *)priv;
/* fill in the attributes of the private data */
error = fill_attributes((char *)priv,
attrs,
&rp->flags,
queryprogram_attributes,
end_queryprogram_attributes);
if (error) {
return error;
} else {
return NULL;
}
}
/*
* queryprogram_lookup - call a program to route to a target
*
* Use the algorithm described at the top of this source file for
* finding a match for a target.
*
* Return one of the following values:
*
* These return codes apply only to the specific address:
* DB_SUCCEED Matched the target host.
* DB_NOMATCH Did not match the target host.
* DB_FAIL Encountered a parse error in the command output.
*
* These return codes apply to this router in general:
* FILE_AGAIN The process could not be created, retry later.
* FILE_FAIL A major error has been caught in router,
* notify postmaster.
*/
/*ARGSUSED*/
static int
queryprogram_lookup(rp, addr, fl, rt_info, error_p)
struct router *rp; /* router table entry */
struct addr *addr; /* addr structure */
int fl; /* flags from rt[dv]_standard */
struct rt_info *rt_info; /* return route info here */
struct error **error_p; /* return lookup error here */
{
static struct addr temp; /* temp address */
static char *next_host = NULL; /* returned next_host, can be freed */
char **cmd; /* cmd vectors for child process */
int pid; /* pid of child process */
FILE *f = NULL; /* output of child, if desired */
char *domain_part = NULL; /* match the second domain part */
char *error = NULL; /* scan error */
int status; /* child's exit status */
char *target = addr->target;
struct queryprogram_private *priv;
priv = (struct queryprogram_private *)rp->private;
if (priv->required) {
if (match_end_domain(priv->required, target) == NULL) {
/* did not end in a required domain */
return DB_NOMATCH;
}
}
if (target[0] == '.') {
target++; /* ignore initial dot */
}
/*
* strip any optional domain
*/
if (priv->domain) {
domain_part = match_end_domain(priv->domain, target);
if (domain_part) {
DEBUG1(DBG_DRIVER_HI, "strip \"%s\"\n", domain_part);
domain_part[0] = '\0';
}
}
/* build the query command, using the stripped target name */
if (next_host) {
xfree(next_host);
}
next_host = COPY_STRING(target);
temp.next_host = next_host;
cmd = build_cmd_line(priv->cmd, &temp, (char *)NULL, error);
if (domain_part) {
*domain_part = '.';
}
if (cmd == NULL) {
*error_p = parse_error(rp, priv->cmd, error);
return FILE_FAIL;
}
/* create a child process and get its exit status */
pid = open_child(cmd, (char **)NULL,
(FILE **)NULL,
rp->flags & (QP_READ_PATH | QP_READ_TRANSPORT)?
&f: (FILE **)NULL,
-1, CHILD_DEVNULL | CHILD_MINENV | CHILD_NOCLOSE,
nobody_uid, nobody_gid);
if (pid == -1) {
/* temporary failure, try again at a later time */
*error_p = temp_command_error(rp);
return FILE_AGAIN;
}
rt_info->next_host = next_host;
rt_info->matchlen = strlen(addr->target);
if (f) {
/* get path and/or transport from input line */
char *output = read_entry(f);
if (output && output[0] != '\0') {
if (decode_query_output(rp, output, rt_info, error) == FAIL) {
(void) close_child((FILE *)NULL, f, pid);
*error_p = output_parse_error(rp, error);
return DB_FAIL;
}
}
}
/* get the child's status */
status = close_child((FILE *)NULL, f, pid);
return status==0? DB_SUCCEED: DB_NOMATCH;
}
/*
* decode_query_output - decode the output from a queried command
*
* decode a transport and or a path returned by a queried command.
* Return this information in an rt_info structure.
*/
static int
decode_query_output(rp, output, rt_info, error)
struct router *rp; /* the router in question */
char *output; /* the output of the program */
struct rt_info *rt_info; /* return routing info here */
char **error; /* return error message here */
{
register char *p = output;
if (rp->flags & QP_READ_PATH) {
char *path;
char *next_host;
char *route;
/* look for a path as first space-delimited field */
while (isspace(*p)) p++;
path = p;
while (*p && !isspace(*p)) p++;
if (*p) {
*p++ = '\0';
}
/* break the path into a next_host and route */
if (parse_address(path, &next_host, &route) == FAIL) {
/* remainder contains error msg */
*error = route;
return FAIL;
}
if (next_host == NULL && route != NULL) {
/* trap the case of a single host in the path */
rt_info->next_host = route;
} else {
rt_info->next_host = next_host;
rt_info->route = route;
}
}
if (rp->flags & QP_READ_TRANSPORT) {
char *transport;
/* look for a transport as the next field */
while (isspace(*p)) p++;
transport = p;
while (*p && !isspace(*p)) p++;
*p = '\0';
if (transport[0]) {
struct transport *tp;
tp = find_transport(transport);
if (tp == NULL) {
*error = "program returned unknown transport";
return FAIL;
}
rt_info->transport = tp;
}
}
return SUCCEED;
}
\f
static struct error *
parse_error(rp, cmd, parse_error_text)
struct router *rp;
char *cmd;
char *parse_error_text;
{
char *error_text;
/*
* ERR_160 - queryprogram cmd expansion error
*
* DESCRIPTION
* An error was encountered while expanding the command
* in the queryprogram driver. The specific error
* message is stored in `error'.
*
* ACTIONS
* Defer the message with a configuration error.
*
* RESOLUTION
* The postmaster should check the validity of the
* queryprogram driver entry.
*/
error_text = xprintf("router %s: error expanding `%s': %s",
rp->name, cmd, parse_error_text);
DEBUG1(DBG_DRIVER_LO, "%s\n", error_text);
return note_error(ERR_160, error_text);
}
static struct error *
temp_command_error(rp)
struct router *rp;
{
char *error_text;
/*
* ERR_161 - temporary failure in executing a command
*
* DESCRIPTION
* An error was encountered while creating the child
* process. This is most likely a temporary condition
* caused by running out of paging space or process
* slots. Try again later.
*
* ACTIONS
* Defer the message.
*
* RESOLUTION
* Hopefully, a later queue run will succeed.
*/
error_text = xprintf("router %s: command execution failed: %s",
rp->name, strerrno());
DEBUG1(DBG_DRIVER_LO, "%s\n", error_text);
return note_error(ERR_161, error_text);
}
static struct error *
output_parse_error(rp, parse_error_text)
struct router *rp;
char *parse_error_text;
{
char *error_text;
/*
* ERR_171 - query_program command output format error
*
* DESCRIPTION
* An error was encountered while parsing the output of the
* command executed by the query_program router driver.
*
* ACTIONS
* Defer the message as a configuration error.
*/
error_text = xprintf("router %s: parse error in command output: %s",
rp->name, parse_error_text);
DEBUG1(DBG_DRIVER_LO, "%s\n", error_text);
return note_error(ERR_171, error_text);
}