params.c revision 50c83d09652262aba75a6182b3203c80b48b092b
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <sys/fcntl.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <poll.h>
#include <errno.h>
#include <sys/dlpi.h>
#include <sys/stropts.h>
#include <syslog.h>
#include <net/if.h>
#include "rpld.h"
#include "dluser.h"
#define MAXIFS 256
extern char configFile[];
extern int debugLevel;
extern int debugDest;
extern int maxClients;
extern int backGround;
extern char logFile[];
extern unsigned long delayGran;
extern unsigned long startDelay;
extern int frameSize;
extern char ifName[];
extern char debugmsg[];
extern FILE *log_str;
extern int ifUnit;
extern int ppanum;
extern int need_llc;
void usage(void);
void getdevice(uchar_t *, uchar_t *);
int
parseargs(int argc, char *argv[], char *envp[])
{
int c;
int err = 0;
int ip_fd;
int numifs;
struct ifreq *reqbuf;
struct ifreq *ifr;
char *device;
int unit;
struct ifconf ifconf;
unsigned bufsize;
char devbuf[MAXPATHLEN];
int aflag = 0;
int curr_ppa = 0;
extern char *optarg;
extern int optind;
extern int opterr;
int n;
int more = 0;
char _ifName[MAXPATHLEN] = "";
int _ifUnit = 0;
int _need_llc;
opterr = 0;
/*
* Expects at least 2 arguments: program name and the network
* interface name, or the program name with -a option.
*/
if (argc < 2) {
usage();
return (-1);
}
debugLevel = MSG_ERROR_1; /* at least when it fails, we know */
debugDest = DEST_CONSOLE; /* why at the console */
/*
* Check if either the -a option or a /dev/deviceN path is specified.
* At the same time look for an alternate config file.
* After this checking, optind is positioned to the next thing on
* the command line after all the options.
*/
optind = 1;
while ((c = getopt(argc, argv, "af:d:D:M:b:l:s:g:z:")) != -1) {
switch (c) {
case 'a':
aflag = 1;
break;
case 'f': /* alternate config file */
if (strlcpy(configFile, optarg,
MAXPATHLEN) >= MAXPATHLEN) {
printf("Alternate config file too long\n");
return (-1);
}
break;
case '?':
usage();
return (-1);
}
}
if (!aflag) {
if ((argv[optind] == NULL) || (argv[optind][0] == '\0')) {
/* neither -a nor device name is specified */
usage();
return (-1);
} else {
/* potentially a correct device name is specified */
int i;
if (strlcpy(ifName, argv[optind],
MAXPATHLEN) >= MAXPATHLEN) {
printf("Network interface name too long\n");
return (-1);
}
/* must have a PPA number */
i = strlen(ifName) - 1;
if ((ifName[i] < '0') || (ifName[i] > '9')) {
usage();
return (-1);
}
/* extract the PPA number and device path */
while (i) {
if ((ifName[i] < '0') || (ifName[i] > '9'))
break;
else
i--;
}
if (i) {
ifUnit = atoi(&ifName[i+1]);
ifName[i+1] = '\0';
} else {
usage();
return (-1);
}
}
} else {
/* cannot have both -a and device */
if (argv[optind] && argv[optind][0]) {
usage();
return (-1);
}
}
/* Read the configuration file now */
#define NOTRUNNING 0
readconfig(NOTRUNNING);
/* Now override any parameters with command line options */
optind = 1;
while ((c = getopt(argc, argv, "af:d:D:M:b:l:s:g:z:")) != -1) {
switch (c) {
case 'd': /* debug level */
debugLevel = atoi(optarg);
break;
case 'D': /* debug output destination */
debugDest = atoi(optarg);
break;
case 'M': /* max clients */
maxClients = atoi(optarg);
break;
case 'b': /* background mode */
backGround = atoi(optarg);
break;
case 'l': /* alt. log file name */
if (strlcpy(logFile, optarg,
MAXPATHLEN) >= MAXPATHLEN) {
printf("Alternate log file too long\n");
return (-1);
}
break;
case 's': /* start delay count */
startDelay = atol(optarg);
break;
case 'g': /* granularity */
delayGran = atol(optarg);
break;
case 'z': /* frame size */
frameSize = atoi(optarg);
break;
case '?':
err++;
}
}
if (debugLevel && (open_debug_dest() < 0)) {
printf("Cannot open specified debug destination\n");
return (-1);
}
/* debugLevel and debugDest finally has meaning now */
if (debugLevel < MSG_NONE || debugLevel > MSG_ALWAYS) {
sprintf(debugmsg,
"Debug level out of range. Legal range is 0-9.\n");
senddebug(MSG_FATAL);
err++;
}
if (backGround && debugDest == DEST_CONSOLE) {
if (debugLevel >= MSG_FATAL) {
sprintf(debugmsg, "Cannot run in background while "
"sending debug information to console.\n");
senddebug(MSG_FATAL);
}
err++;
}
if ((long)delayGran < 0) {
if (debugLevel >= MSG_FATAL) {
sprintf(debugmsg,
"Cannot have negative delay granularity.\n");
senddebug(MSG_FATAL);
}
err++;
}
if (err) {
usage();
return (-1);
}
if (aflag) {
/*
* Find all the network interfaces and start a server on
* each of them.
*/
if ((ip_fd = open("/dev/ip", 0)) < 0) {
if (debugLevel >= MSG_FATAL) {
sprintf(debugmsg,
"Failed to open IP, -a option failed.\n");
senddebug(MSG_FATAL);
}
return (-1);
}
/* ask IP for the list of configured interfaces */
#ifdef SIOCGIFNUM
if (_ioctl(ip_fd, SIOCGIFNUM, (char *)&numifs) < 0) {
if (debugLevel >= MSG_FATAL) {
sprintf(debugmsg,
"Failed _ioctl(SIOCGIFNUM)\n");
senddebug(MSG_FATAL);
}
close(ip_fd);
return (-1);
}
#else
numifs = MAXIFS;
#endif
bufsize = numifs*sizeof (struct ifreq);
reqbuf = (struct ifreq *)malloc(bufsize);
if (reqbuf == NULL) {
if (debugLevel >= MSG_FATAL) {
sprintf(debugmsg, "Failed to allocate memory "
"to look up configured interfaces in IP.\n");
senddebug(MSG_FATAL);
sprintf(debugmsg, "-a option failed.\n");
senddebug(MSG_FATAL);
}
close(ip_fd);
return (-1);
}
ifconf.ifc_len = bufsize;
ifconf.ifc_buf = (caddr_t)reqbuf;
if (_ioctl(ip_fd, SIOCGIFCONF, (char *)&ifconf) < 0) {
if (debugLevel >= MSG_FATAL) {
sprintf(debugmsg,
"SIOCGIFCONF failed, -a option failed.\n");
senddebug(MSG_FATAL);
}
close(ip_fd);
free(reqbuf);
return (-1);
}
/*
* Fork and pass discovered interface name through the
* global variables ifName and ifUnit
*/
for (ifr = ifconf.ifc_req; ifconf.ifc_len > 0;
ifr++, ifconf.ifc_len -= sizeof (struct ifreq)) {
if (ioctl(ip_fd, SIOCGIFFLAGS, (char *)ifr) < 0) {
if (debugLevel >= MSG_FATAL) {
sprintf(debugmsg,
"ioctl SIOCGIFFLAGS failed.\n");
senddebug(MSG_FATAL);
}
free(reqbuf);
close(ip_fd);
return (-1);
}
if ((ifr->ifr_flags & IFF_LOOPBACK) ||
!(ifr->ifr_flags & IFF_BROADCAST) ||
!(ifr->ifr_flags & IFF_UP) ||
(ifr->ifr_flags & IFF_NOARP) ||
(ifr->ifr_flags & IFF_POINTOPOINT))
continue;
if (strchr(ifr->ifr_name, ':') != NULL)
/*
* rpld only cares about DLPI devices and only
* the physical IP interfaces correspond to
* a DLPI device.
*/
continue;
/*
* check if this interface needs LLC and if so,
* remember which PPA in LLC to use. Assume that
* only llc1 is used.
*/
(void) strlcpy(devbuf, "/dev/", MAXPATHLEN);
getdevice((uchar_t *)ifr->ifr_name,
(uchar_t *)&devbuf[5]);
(void) strlcpy(ifName, devbuf, MAXPATHLEN);
ifUnit = unit = getunit(ifr->ifr_name);
if (debugLevel >= MSG_INFO_1) {
sprintf(debugmsg, "found interface = %s%d\n",
ifName, ifUnit);
senddebug(MSG_INFO_1);
}
if (llc_is_needed(ifName, ifUnit)) {
need_llc = 1;
} else
need_llc = 0;
if (more) {
switch (fork()) {
case -1: /* failure */
free(reqbuf);
close(ip_fd);
if (debugLevel >= MSG_FATAL) {
sprintf(debugmsg, "Fork() "
"failed to run rpld on "
"interface %s%d\n", ifName,
ifUnit);
senddebug(MSG_FATAL);
}
return (-1);
break;
case 0: /* child */
free(reqbuf);
close(ip_fd);
if (debugLevel >= MSG_INFO_2)
dumpparams();
return (0);
default: /* parent */
break;
}
/* prepare for next ppanum for LLC */
ppanum++;
}
/*
* retain the first found interface for the
* parent process
*/
if (!more) {
more = 1;
(void) strlcpy(_ifName, ifName, MAXPATHLEN);
_ifUnit = ifUnit;
_need_llc = need_llc;
}
} /* for */
free(reqbuf);
close(ip_fd);
} /* aflag */
/* parent always use ppanum of 0 */
ppanum = 0;
(void) strlcpy(ifName, _ifName, MAXPATHLEN);
ifUnit = _ifUnit;
need_llc = _need_llc;
if (debugLevel >= MSG_INFO_2)
dumpparams();
return (0);
}
/* returns 1 if need LLC, 0 if not, -1 if error occurs */
int
llc_is_needed(char *devname, int ifUnit)
{
int if_fd;
dl_info_t if_info;
struct strbuf ctl;
char resultbuf[MAXPRIMSZ];
union DL_primitives *dlp = (union DL_primitives *)resultbuf;
unsigned char node_address[6];
int flags = 0;
/* open this device */
if ((if_fd = dl_open(ifName, O_RDWR, NULL)) < 0) {
return (-1);
}
/* query information about this device */
if (dl_info(if_fd, &if_info) < 0) {
dl_close(if_fd);
return (-1);
}
/*
* Check if we need to use the LLC module. This is needed if:
* 1. a DL_INFO_REQ determines that the device is of type DL_ETHER
* 2. an attempt to do a DL_TEST_REQ to yourself does not return
* an error ack for non-DL_ETHER devices.
*/
if (if_info.mac_type == DLTYPE_ETHER) {
dl_close(if_fd);
return (1);
} else {
struct pollfd pfd;
int rc;
if (debugLevel >= MSG_INFO_1) {
sprintf(debugmsg,
"Need to determine if LLC driver is needed\n");
senddebug(MSG_INFO_1);
}
/* attach to ifUnit */
if (dl_attach(if_fd, ifUnit) < 0) {
dl_close(if_fd);
return (-1);
}
/* find out own node address for use in test */
if (dlpi_get_phys(if_fd, node_address) != 0) {
dl_close(if_fd);
return (-1);
}
dlp->test_req.dl_primitive = DL_TEST_REQ;
dlp->test_req.dl_flag = 0;
dlp->test_req.dl_dest_addr_length = 7;
dlp->test_req.dl_dest_addr_offset = DL_TEST_REQ_SIZE;
memcpy(&resultbuf[DL_TEST_REQ_SIZE], node_address, 6);
resultbuf[DL_TEST_REQ_SIZE+6] = (unsigned char)0xfc;
ctl.len = DL_TEST_REQ + 7;
ctl.buf = resultbuf;
if (putmsg(if_fd, &ctl, NULL, 0) < 0) {
if (debugLevel >= MSG_FATAL) {
sprintf(debugmsg,
"putmsg() failed in DL_TEST_REQ, errno = %d\n", errno);
senddebug(MSG_FATAL);
}
dl_close(if_fd);
return (-1);
}
pfd.fd = if_fd;
pfd.events = POLLIN | POLLPRI | POLLERR;
rc = poll(&pfd, 1, 10); /* 10 millisec timeout */
if (rc == 0) {
/* no reply message, assume LLC is available */
} else if (rc > 0) {
ctl.maxlen = MAXPRIMSZ;
ctl.buf = resultbuf;
if (getmsg(if_fd, &ctl, NULL, &flags) < 0) {
if (debugLevel >= MSG_FATAL) {
sprintf(debugmsg,
"getmsg() failed in DL_TEST_REQ, errno = %d\n", errno);
senddebug(MSG_FATAL);
}
dl_close(if_fd);
return (-1);
}
if (dlp->dl_primitive == DL_ERROR_ACK) {
dl_close(if_fd);
return (1);
}
} else {
/* error occurs */
dl_close(if_fd);
return (-1);
}
}
dl_close(if_fd);
return (0);
}
/*
* Read the configuration parameters in the config file.
* Different actions are required if the server is already running or not
*/
int
readconfig(int running)
{
FILE *fstr;
int i, n;
char line[80];
int lineno = 0;
int done = 0;
int debugDestChange;
unsigned int newDebugDest;
int logFileChange;
char newLogFile[MAXPATHLEN];
if ((fstr = fopen(configFile, "r")) == NULL) {
if (running) {
if (debugLevel >= MSG_ERROR_1) {
sprintf(debugmsg,
"Cannot open config file %s\n", configFile);
senddebug(MSG_ERROR_1);
}
} else {
printf("Cannot open config file %s.\n", configFile);
}
/*
* If already running, do not fall back to use the default
* config file.
*/
if (running)
return (0);
/* Try to use default config file if not already done so */
if (strcmp(configFile, DFT_CONFIGFILE) != 0) {
printf("Using the default config file %s\n",
DFT_CONFIGFILE);
(void) strlcpy(configFile, DFT_CONFIGFILE, MAXPATHLEN);
if ((fstr = fopen(configFile, "r")) == NULL) {
if (running && debugLevel >= MSG_ERROR_1) {
sprintf(debugmsg, "Cannot open "
"default config file. Using "
"default values.\n");
senddebug(MSG_ERROR_1);
} else {
printf("Cannot open default config "
"file. Using default values.\n");
}
return (0);
}
} else {
return (0);
}
}
if (running && (debugLevel >= MSG_INFO_1)) {
sprintf(debugmsg, "Reading config file %s\n", configFile);
senddebug(MSG_INFO_1);
}
do {
lineno++;
n = readline(fstr, line, 80);
if (line[0] == '\0' || line[0] == COMMENT_CHAR)
continue;
/*
* We now scan the input line and put a NULL at the
* end of the keyword token. This way, we can compare
* the keyword token by strcmp(line, "...")
*
* At the same time, we find the start of the value
* token so that we can retrieve the value by something
* like atoi(&line[i])
*/
i = 0;
while (line[i] != ' ' && line[i] != TAB)
i++;
line[i++] = '\0'; /* put NULL after keyword token */
while (line[i] < '0' || line[i] > 'z' ||
(line[i] > '9' && line[i] < 'A') ||
(line[i] > 'Z' && line[i] < 'a'))
i++;
if (strcmp(line, "DebugLevel") == 0)
debugLevel = atoi(&line[i]);
else if (strcmp(line, "DebugDest") == 0) {
if (!running) {
debugDest = atoi(&line[i]);
} else {
debugDestChange = 0;
newDebugDest = atoi(&line[i]);
if (debugDest == atoi(&line[i]))
continue;
/*
* Don't allow running in background to
* re-acquire the controlling terminal
*/
if (backGround &&
atoi(&line[i]) == DEST_CONSOLE)
continue;
/*
* We have determined that there is a
* legal change of the debug destination.
* However, we cannot do the actual change
* now because the log file may also change.
* So we set a flag and do it later.
*/
debugDestChange = 1;
}
} else if (strcmp(line, "MaxClients") == 0) {
/* can be -1 which means unlimited */
if (line[i-1] == '-')
i--;
maxClients = atoi(&line[i]);
} else if (strcmp(line, "BackGround") == 0) {
if (!running)
backGround = atoi(&line[i]);
} else if (strcmp(line, "LogFile") == 0) {
if (line[i-1] == '/')
i--;
if (!running) {
(void) strlcpy(logFile, &line[i], MAXPATHLEN);
} else {
logFileChange = 0;
(void) strlcpy(newLogFile, &line[i],
MAXPATHLEN);
if (strcmp(newLogFile, logFile) == 0)
continue;
else
logFileChange = 1;
}
} else if (strcmp(line, "DelayGran") == 0)
delayGran = atol(&line[i]);
else if (strcmp(line, "StartDelay") == 0)
startDelay = atol(&line[i]);
else if (strcmp(line, "FrameSize") == 0)
frameSize = atoi(&line[i]);
else if (strcmp(line, "end") == 0)
done = 1;
} while (!done);
fclose(fstr);
if (running) {
if (debugDestChange || logFileChange) {
switch (debugDest) {
case DEST_SYSLOGD:
closelog();
break;
case DEST_LOGFILE:
if (log_str != NULL)
fclose(log_str);
log_str = NULL;
break;
}
debugDest = newDebugDest;
switch (debugDest) {
case DEST_SYSLOGD:
openlog("rpld", LOG_PID, LOG_DAEMON);
break;
case DEST_LOGFILE:
log_str = fopen(newLogFile, "a+");
if (log_str == NULL) {
/* cannot open the new log file */
/* reopen the old one and use that */
log_str = fopen(logFile, "a+");
if (log_str == NULL) {
/* we are doomed */
;
}
setbuf(log_str, (char *)NULL);
if (debugLevel >= MSG_FATAL) {
sprintf(debugmsg, "Cannot "
"open new log file %s\n",
newLogFile);
senddebug(MSG_FATAL);
sprintf(debugmsg, "Re-using "
"old log file %s\n",
logFile);
senddebug(MSG_FATAL);
}
} else {
(void) strlcpy(logFile, newLogFile,
MAXPATHLEN);
setbuf(log_str, (char *)NULL);
}
break;
}
}
}
return (0);
}
int
readline(FILE *str, char *buf, int count)
{
int i = 0;
int done = 0;
int ch;
while (!done && i < count) {
ch = getc(str);
if (ch == LF || ch == CR || ch == EOF) {
done = 1;
} else {
buf[i++] = ch;
}
}
buf[i] = '\0';
return (i);
}
int
open_debug_dest(void)
{
if (debugDest == DEST_LOGFILE) {
if (log_str != NULL)
fclose(log_str);
log_str = fopen(logFile, "a+");
if (log_str == NULL) {
if (debugLevel >= MSG_FATAL) {
sprintf(debugmsg, "Cannot open log file %s\n",
logFile);
senddebug(MSG_FATAL);
}
return (-1);
}
setbuf(log_str, (char *)NULL);
}
if (debugDest == DEST_SYSLOGD) {
openlog("rpld", LOG_PID, LOG_DAEMON);
}
return (0);
}
void
usage(void)
{
printf("\n");
printf("Usage: rpld [options] <network_interface_name>\n");
printf(" rpld -a [options]\n");
printf("Options:\n");
printf(" -f file # set alternate config file\n");
printf(" -d num # set debug level (0-9), 0=nil, 9=most\n");
printf(" -D num # set debug destination, 0=console, "
"1=syslogd, 2=logfile\n");
printf(" -M num # set max simultaneous clients, "
"-1=unlimited\n");
printf(" -b num # set background mode, 1=background, "
"0=not\n");
printf(" -l file # set alternate log file\n");
printf(" -s num # set start delay count\n");
printf(" -g num # set delay granularity\n");
printf("\n");
}
/*
* Pick out leading alphabetic part of string 's'.
*/
void
getdevice(uchar_t *s, uchar_t *dev)
{
while (isalpha(*s))
*dev++ = *s++;
*dev = '\0';
}
/*
* Pick out trailing numeric part of string 's' and return int.
*/
int
getunit(char *s)
{
char intbuf[128];
char *p = intbuf;
while (isalpha(*s))
s++;
while (isdigit(*s))
*p++ = *s++;
*p = '\0';
return (atoi(intbuf));
}