pppoed(8) - a server to accept PPPoE connections

This commit is contained in:
Brian Somers 1999-11-21 23:39:14 +00:00
parent 31dd45c4e0
commit dbc7ba8d31
3 changed files with 735 additions and 0 deletions

9
libexec/pppoed/Makefile Normal file
View File

@ -0,0 +1,9 @@
# $FreeBSD$
PROG= pppoed
SRCS= pppoed.c
DPADD= ${LIBNETGRAPH}
LDADD= -lnetgraph
MAN8= pppoed.8
.include <bsd.prog.mk>

169
libexec/pppoed/pppoed.8 Normal file
View File

@ -0,0 +1,169 @@
.\"-
.\" Copyright (c) 1999 Brian Somers <brian@Awfulhak.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, 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.
.\"
.\" 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.
.\"
.\" $FreeBSD$
.\"
.Dd November 8, 1999
.Dt PPPOED 8
.Os FreeBSD
.Sh NAME
.Nm pppoed
.Nd handle incoming PPP over Ethernet connections
.Sh SYNOPSIS
.Nm pppoed
.Op Fl Fd
.Op Fl a Ar name
.Op Fl e Ar exec
.Op Fl p Ar provider
.Ar interface
.Sh DESCRIPTION
The
.Nm
program listens to the given
.Ar interface
for PPP over Ethernet (PPPoE) service request packets, and actions them
by negotiating a session then envoking a
.Em PPP
program. The negotiation is implemented by the
.Dq pppoe
netgraph node. See
.Xr ng_pppoe 8
for details.
.Pp
.Nm
will only offer services to clients requesting services from the given
.Ar provider ,
which is taken as an empty name if not provided.
If a provider name of
.Dq \&*
is given, any PPPoE requests will be offered service.
.Pp
The supplied
.Ar name
will be given as the access concentrator name when establishing the
connection. If no
.Ar name
is given, the current base hostname is used.
.Pp
After receiving a request (PADI) from the PPPoE netgraph node,
.Nm
.Xr fork 2 Ns No s
a child process and returns to service further requests. The child process
offers service
.Pq using Ar name
and waits for a
.Dv SUCCESS
indication from the PPPoE node. On receipt of the SUCCESS indication,
.Nm
will execute
.Pp
.D1 Ic exec /usr/sbin/ppp -direct Ar provider
.Pp
as a shell sub-process. It is possible to specify another command using the
.Ar exec
argument. This is mandatory if
.Ar provider
is not given. The child process will have standard input and standard output
attached to the same
.Xr netgraph 4
data socket
.Pq see Xr ng_socket 8
when started.
.Pp
Upon invocation,
.Nm
will attach a
.Dq pppoe
netgraph node to the relevant
.Dq ether
node using
.Dq Ar interface Ns No \&:
as the node name, and then connect that
.Dq pppoe
node to a local
.Dq socket
node. If the
.Fl F
flag has not been given,
.Nm
will then go into the background and disassociate itself from the controlling
terminal. When the
.Fl F
flag is given,
.Nm
stays in the foreground.
.Pp
If the
.Fl d
flag is given, additional diagnostics are provided (see the
.Sx DIAGNOSTICS
section below).
.Sh DIAGNOSTICS
After creating the necessary
.Xr netgraph 4
nodes as described above,
.Nm
uses
.Xr syslogd 8
to report all incoming connections. If the
.Fl d
flag is given,
.Nm
will report on the child processes creation of a new netgraph socket, it's
service offer and the invocation of the
.Em PPP
program.
.Pp
It is sometimes useful to add the following to
.Pa /etc/syslog.conf :
.Pp
.Dl !pppoed
.Dl *.* /var/log/pppoed.log
.Pp
and the following to
.Pa /etc/newsyslog.conf :
.Pp
.Dl /var/log/pppoed.log 640 3 100 * Z
.Pp
.Sh SEE ALSO
.Xr netgraph 4 ,
.Xr syslog.conf 5 ,
.Xr ng_pppoe 8 ,
.Xr ng_socket 8 ,
.Xr ppp 8 ,
.Xr syslogd 8
.Sh BUGS
If another netgraph node is using the given interface,
.Nm
will fail to start. This is because
.Xr netgraph 4
does not currently allow node chaining. This may change in the future.
.Sh HISTORY
The
.Nm
program was written by
.An Brian Somers Aq brian@Awfulhak.org
and first appeared in
.Fx 3.4 .

557
libexec/pppoed/pppoed.c Normal file
View File

@ -0,0 +1,557 @@
/*-
* Copyright (c) 1999 Brian Somers <brian@Awfulhak.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, 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.
*
* 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.
*
* $FreeBSD$
*/
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netgraph.h>
#include <net/ethernet.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netgraph/ng_ether.h>
#include <netgraph/ng_message.h>
#include <netgraph/ng_pppoe.h>
#include <netgraph/ng_socket.h>
#include <errno.h>
#include <paths.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <sys/fcntl.h>
#ifndef NOKLDLOAD
#include <sys/linker.h>
#include <sys/module.h>
#endif
#include <sys/uio.h>
#include <sys/wait.h>
#include <syslog.h>
#include <termios.h>
#include <unistd.h>
#define DEFAULT_EXEC_PREFIX "exec /usr/sbin/ppp -direct "
static int
usage(const char *prog)
{
fprintf(stderr, "Usage: %s [-Fd] [-a name] [-e exec] [-p provider]"
" interface\n", prog);
return EX_USAGE;
}
static void
Fairwell(int sig)
{
char buf[] = "Received signal XX, exiting";
/* No stdio in a signal handler */
buf[16] = '0' + ((sig / 10) % 10);
buf[17] = '0' + (sig % 10);
syslog(LOG_INFO, buf);
signal(sig, SIG_DFL);
raise(sig);
}
static int
ConfigureNode(const char *prog, const char *iface, const char *provider,
int cs, int ds, int debug, struct ngm_connect *ngc)
{
/*
* We're going to do this with the passed `ds' & `cs' descriptors:
*
* .---------.
* | ether |
* | <iface> |
* `---------'
* (orphan) ds cs
* | | |
* | | |
* (ethernet) | |
* .---------. .-----------.
* | pppoe | | socket |
* | <iface> |(pppoe-<pid>)<---->(pppoe-<pid>)| <unnamed> |
* `--------- `-----------'
* (exec-<pid>)
* ^ .-----------. .-------------.
* | | socket | | ppp -direct |
* `--->(exec-<pid>)| <unnamed> |--fd--| provider |
* `-----------' `-------------'
*
* where there are potentially many ppp processes running off of the
* same PPPoE node.
* The exec-<pid> hook isn't made 'till we Spawn().
*/
char *epath, *spath;
struct ngpppoe_init_data *data;
const struct hooklist *hlist;
const struct nodeinfo *ninfo;
const struct linkinfo *nlink;
struct ngm_mkpeer mkp;
struct ng_mesg *resp;
u_char rbuf[2048];
int f, plen;
/*
* Ask for a list of hooks attached to the "ether" node. This node should
* magically exist as a way of hooking stuff onto an ethernet device
*/
epath = (char *)alloca(strlen(iface) + 2);
sprintf(epath, "%s:", iface);
if (debug)
fprintf(stderr, "Sending NGM_LISTHOOKS to %s\n", epath);
if (NgSendMsg(cs, epath, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0) < 0) {
if (errno == ENOENT)
fprintf(stderr, "%s Cannot send a netgraph message: Invalid interface\n",
epath);
else
fprintf(stderr, "%s Cannot send a netgraph message: %s\n",
epath, strerror(errno));
return EX_UNAVAILABLE;
}
/* Get our list back */
resp = (struct ng_mesg *)rbuf;
if (NgRecvMsg(cs, resp, sizeof rbuf, NULL) < 0) {
perror("Cannot get netgraph response");
return EX_UNAVAILABLE;
}
hlist = (const struct hooklist *)resp->data;
ninfo = &hlist->nodeinfo;
if (debug)
fprintf(stderr, "Got reply from id [%x]: Type %s with %d hooks\n",
ninfo->id, ninfo->type, ninfo->hooks);
/* Make sure we've got the right type of node */
if (strncmp(ninfo->type, NG_ETHER_NODE_TYPE, sizeof NG_ETHER_NODE_TYPE - 1)) {
fprintf(stderr, "%s Unexpected node type ``%s'' (wanted ``"
NG_ETHER_NODE_TYPE "'')\n", epath, ninfo->type);
return EX_DATAERR;
}
/* look for a hook already attached. */
for (f = 0; f < ninfo->hooks; f++) {
nlink = &hlist->link[f];
if (debug)
fprintf(stderr, " Got [%x]:%s -> [%x]:%s\n", ninfo->id,
nlink->ourhook, nlink->nodeinfo.id, nlink->peerhook);
if (!strcmp(nlink->ourhook, NG_ETHER_HOOK_ORPHAN) ||
!strcmp(nlink->ourhook, NG_ETHER_HOOK_DIVERT)) {
/*
* Something is using the data coming out of this `ether' node.
* If it's a PPPoE node, we use that node, otherwise we complain that
* someone else is using the node.
*/
if (strcmp(nlink->nodeinfo.type, NG_PPPOE_NODE_TYPE)) {
fprintf(stderr, "%s Node type %s is currently active\n",
epath, nlink->nodeinfo.type);
return EX_UNAVAILABLE;
}
break;
}
}
if (f == ninfo->hooks) {
/*
* Create a new PPPoE node connected to the `ether' node using
* the magic `orphan' and `ethernet' hooks
*/
snprintf(mkp.type, sizeof mkp.type, "%s", NG_PPPOE_NODE_TYPE);
snprintf(mkp.ourhook, sizeof mkp.ourhook, "%s", NG_ETHER_HOOK_ORPHAN);
snprintf(mkp.peerhook, sizeof mkp.peerhook, "%s", NG_PPPOE_HOOK_ETHERNET);
if (debug)
fprintf(stderr, "Send MKPEER: %s%s -> [type %s]:%s\n", epath,
mkp.ourhook, mkp.type, mkp.peerhook);
if (NgSendMsg(cs, epath, NGM_GENERIC_COOKIE,
NGM_MKPEER, &mkp, sizeof mkp) < 0) {
fprintf(stderr, "%s Cannot create a peer PPPoE node: %s\n",
epath, strerror(errno));
return EX_OSERR;
}
}
/* Connect the PPPoE node to our socket node. */
snprintf(ngc->path, sizeof ngc->path, "%s%s", epath, NG_ETHER_HOOK_ORPHAN);
snprintf(ngc->ourhook, sizeof ngc->ourhook, "pppoe-%ld", (long)getpid());
memcpy(ngc->peerhook, ngc->ourhook, sizeof ngc->peerhook);
if (NgSendMsg(cs, ".:", NGM_GENERIC_COOKIE,
NGM_CONNECT, ngc, sizeof *ngc) < 0) {
perror("Cannot CONNECT PPPoE and socket nodes");
return EX_OSERR;
}
plen = strlen(provider);
data = (struct ngpppoe_init_data *)alloca(sizeof *data + plen + 1);
snprintf(data->hook, sizeof data->hook, "%s", ngc->peerhook);
strcpy(data->data, provider);
data->data_len = plen;
spath = (char *)alloca(strlen(ngc->peerhook) + 3);
strcpy(spath, ".:");
strcpy(spath + 2, ngc->ourhook);
if (debug) {
if (provider)
fprintf(stderr, "Sending PPPOE_LISTEN to %s, provider %s\n",
spath, provider);
else
fprintf(stderr, "Sending PPPOE_LISTEN to %s\n", spath);
}
if (NgSendMsg(cs, spath, NGM_PPPOE_COOKIE, NGM_PPPOE_LISTEN,
data, sizeof *data + plen) == -1) {
fprintf(stderr, "%s: Cannot LISTEN on netgraph node: %s\n",
spath, strerror(errno));
return EX_OSERR;
}
return 0;
}
static void
Spawn(const char *prog, const char *acname, const char *exec,
struct ngm_connect ngc, int cs, int ds, void *request, int sz,
int debug)
{
char msgbuf[sizeof(struct ng_mesg) + sizeof(struct ngpppoe_sts)];
struct ng_mesg *rep = (struct ng_mesg *)msgbuf;
struct ngpppoe_sts *sts = (struct ngpppoe_sts *)(msgbuf + sizeof *rep);
struct ngpppoe_init_data *data;
char unknown[14], *path;
const char *msg;
int ret, slen;
switch ((ret = fork())) {
case -1:
syslog(LOG_ERR, "fork: %m");
break;
case 0:
switch (fork()) {
case 0:
break;
case -1:
_exit(errno);
default:
_exit(0);
}
close(cs);
close(ds);
/* Create a new socket node */
if (debug)
syslog(LOG_INFO, "Creating a new socket node");
if (NgMkSockNode(NULL, &cs, &ds) == -1) {
syslog(LOG_ERR, "Cannot create netgraph socket node: %m");
_exit(EX_CANTCREAT);
}
/* Connect the PPPoE node to our new socket node. */
snprintf(ngc.ourhook, sizeof ngc.ourhook, "exec-%ld", (long)getpid());
memcpy(ngc.peerhook, ngc.ourhook, sizeof ngc.peerhook);
if (debug)
syslog(LOG_INFO, "Sending CONNECT from .:%s -> %s.%s",
ngc.ourhook, ngc.path, ngc.peerhook);
if (NgSendMsg(cs, ".:", NGM_GENERIC_COOKIE,
NGM_CONNECT, &ngc, sizeof ngc) < 0) {
syslog(LOG_ERR, "Cannot CONNECT PPPoE and socket nodes: %m");
_exit(EX_OSERR);
}
/*
* If we tell the socket node not to LINGER, it will go away when
* the last hook is removed.
*/
if (debug)
syslog(LOG_INFO, "Sending NGM_SOCK_CMD_NOLINGER to socket");
if (NgSendMsg(cs, ".:", NGM_SOCKET_COOKIE,
NGM_SOCK_CMD_NOLINGER, NULL, 0) < 0) {
syslog(LOG_ERR, "Cannot send NGM_SOCK_CMD_NOLINGER: %m");
_exit(EX_OSERR);
}
/* Put the PPPoE node into OFFER mode */
slen = strlen(acname);
data = (struct ngpppoe_init_data *)alloca(sizeof *data + slen + 1);
snprintf(data->hook, sizeof data->hook, "%s", ngc.ourhook);
strcpy(data->data, acname);
data->data_len = slen;
path = (char *)alloca(strlen(ngc.ourhook) + 3);
strcpy(path, ".:");
strcpy(path + 2, ngc.ourhook);
syslog(LOG_INFO, "Offering to %s as access concentrator %s",
path, acname);
if (NgSendMsg(cs, path, NGM_PPPOE_COOKIE, NGM_PPPOE_OFFER,
data, sizeof *data + slen) == -1) {
syslog(LOG_INFO, "%s: Cannot OFFER on netgraph node: %m", path);
_exit(EX_OSERR);
}
/* And send our request data to the waiting node */
if (debug)
syslog(LOG_INFO, "Sending original request to %s (%d bytes)", path, sz);
if (NgSendData(ds, ngc.ourhook, request, sz) == -1) {
syslog(LOG_ERR, "Cannot send original request to %s: %m", path);
_exit(EX_OSERR);
}
/* Then wait for a success indication */
if (debug)
syslog(LOG_INFO, "Waiting for a SUCCESS reply %s", path);
do {
if (NgRecvMsg(cs, rep, sizeof msgbuf, NULL) < 0) {
syslog(LOG_ERR, "%s: Cannot receive a message: %m", path);
_exit(EX_OSERR);
}
if (rep->header.version != NG_VERSION) {
syslog(LOG_ERR, "%ld: Unexpected netgraph version, expected %ld",
(long)rep->header.version, (long)NG_VERSION);
_exit(EX_PROTOCOL);
}
if (rep->header.typecookie != NGM_PPPOE_COOKIE) {
syslog(LOG_INFO, "%ld: Unexpected netgraph cookie, expected %ld",
(long)rep->header.typecookie, (long)NGM_PPPOE_COOKIE);
continue;
}
switch (rep->header.cmd) {
case NGM_PPPOE_SET_FLAG: msg = "SET_FLAG"; break;
case NGM_PPPOE_CONNECT: msg = "CONNECT"; break;
case NGM_PPPOE_LISTEN: msg = "LISTEN"; break;
case NGM_PPPOE_OFFER: msg = "OFFER"; break;
case NGM_PPPOE_SUCCESS: msg = "SUCCESS"; break;
case NGM_PPPOE_FAIL: msg = "FAIL"; break;
case NGM_PPPOE_CLOSE: msg = "CLOSE"; break;
case NGM_PPPOE_GET_STATUS: msg = "GET_STATUS"; break;
default:
snprintf(unknown, sizeof unknown, "<%d>", (int)rep->header.cmd);
msg = unknown;
break;
}
switch (rep->header.cmd) {
case NGM_PPPOE_FAIL:
case NGM_PPPOE_CLOSE:
syslog(LOG_ERR, "Received NGM_PPPOE_%s (hook \"%s\")",
msg, sts->hook);
_exit(0);
}
syslog(LOG_INFO, "Received NGM_PPPOE_%s (hook \"%s\")", msg, sts->hook);
} while (rep->header.cmd != NGM_PPPOE_SUCCESS);
dup2(ds, STDIN_FILENO);
dup2(ds, STDOUT_FILENO);
close(ds);
close(cs);
setsid();
syslog(LOG_INFO, "Executing: %s", exec);
execlp(_PATH_BSHELL, _PATH_BSHELL, "-c", exec, NULL);
syslog(LOG_ERR, "execlp failed: %m");
_exit(EX_OSFILE);
default:
wait(&ret);
errno = ret;
if (errno)
syslog(LOG_ERR, "Second fork failed: %m");
break;
}
}
int
main(int argc, char **argv)
{
char hostname[MAXHOSTNAMELEN];
char response[1024], *exec, rhook[NG_HOOKLEN + 1];
const char *prog, *provider, *acname;
struct ngm_connect ngc;
int ch, cs, ds, ret, optF, optd, sz, f;
prog = strrchr(argv[0], '/');
prog = prog ? prog + 1 : argv[0];
exec = NULL;
acname = NULL;
provider = "";
optF = optd = 0;
while ((ch = getopt(argc, argv, "a:Fde:p:")) != -1) {
switch (ch) {
case 'F':
optF = 1;
break;
case 'a':
acname = optarg;
break;
case 'd':
optd = 1;
break;
case 'e':
exec = optarg;
break;
case 'p':
provider = optarg;
break;
default:
return usage(prog);
}
}
if (optind >= argc || optind + 2 < argc)
return usage(prog);
if (exec == NULL) {
if (provider == NULL) {
fprintf(stderr, "%s: Either a provider or an exec command"
" must be given\n", prog);
return usage(prog);
}
exec = (char *)alloca(sizeof DEFAULT_EXEC_PREFIX + strlen(provider));
if (exec == NULL) {
fprintf(stderr, "%s: Cannot allocate %d bytes\n", prog,
(int)(sizeof DEFAULT_EXEC_PREFIX) + strlen(provider));
return EX_OSERR;
}
strcpy(exec, DEFAULT_EXEC_PREFIX);
strcpy(exec + sizeof DEFAULT_EXEC_PREFIX - 1, provider);
}
if (acname == NULL) {
char *dot;
if (gethostname(hostname, sizeof hostname))
strcpy(hostname, "localhost");
else if ((dot = strchr(hostname, '.')))
*dot = '\0';
acname = hostname;
}
#ifndef NOKLDLOAD
if (modfind("netgraph") == -1) {
fputs("Can't run without options NETGRAPH in the kernel\n", stderr);
return EX_UNAVAILABLE;
}
if (modfind("ng_socket") == -1 && kldload("ng_socket") == -1) {
perror("kldload: ng_socket");
return EX_UNAVAILABLE;
}
#endif
/* Create a socket node */
if (NgMkSockNode(NULL, &cs, &ds) == -1) {
perror("Cannot create netgraph socket node");
return EX_CANTCREAT;
}
/* Connect it up (and fill in `ngc') */
if ((ret = ConfigureNode(prog, argv[optind], provider, cs, ds,
optd, &ngc)) != 0) {
close(cs);
close(ds);
return ret;
}
if (!optF && daemon(1, 0) == -1) {
perror("daemon()");
return EX_OSERR;
}
openlog(prog, LOG_PID | (optF ? LOG_PERROR : 0), LOG_DAEMON);
signal(SIGHUP, Fairwell);
signal(SIGINT, Fairwell);
signal(SIGQUIT, Fairwell);
signal(SIGTERM, Fairwell);
while (1) {
if (*provider)
syslog(LOG_INFO, "Listening as provider %s", provider);
else
syslog(LOG_INFO, "Listening");
switch (sz = NgRecvData(ds, response, sizeof response, rhook)) {
case -1:
syslog(LOG_INFO, "NgRecvData: %m");
break;
case 0:
syslog(LOG_INFO, "NgRecvData: socket closed");
break;
default:
if (optd) {
char *dbuf, *ptr;
ptr = dbuf = alloca(sz * 2 + 1);
for (f = 0; f < sz; f++, ptr += 2)
sprintf(ptr, "%02x", (u_char)response[f]);
*ptr = '\0';
syslog(LOG_INFO, "Got %d bytes of data: %s", sz, dbuf);
}
}
if (sz <= 0) {
ret = EX_UNAVAILABLE;
break;
}
Spawn(prog, acname, exec, ngc, cs, ds, response, sz, optd);
}
return ret;
}