/*
* Copyright 2000, International Business Machines Corporation and others.
* All Rights Reserved.
- *
+ *
* This software has been released under the terms of the IBM Public
* License. For details, see the LICENSE file in the top-level source
* directory or online at http://www.openafs.org/dl/license10.html
#include <afsconfig.h>
#include <afs/param.h>
-RCSID
- ("$Header$");
+#include <afs/procmgmt.h>
+
+#include <roken.h>
+#include <afs/opr.h>
+
+#ifdef IGNORE_SOME_GCC_WARNINGS
+# pragma GCC diagnostic warning "-Wimplicit-function-declaration"
+#endif
-#include <sys/types.h>
-#include <sys/stat.h>
#ifdef AFS_NT40_ENV
-#include <winsock2.h>
#include <WINNT/afsevent.h>
-#else
-#include <netinet/in.h>
-#include <sys/time.h>
#endif
-#include <afs/procmgmt.h>
+
+#include <ctype.h>
+
+#include <rx/rx.h>
+#include <rx/rx_globals.h>
+#include <rx/rxkad.h>
#include <rx/xdr.h>
+
#include <afs/afsint.h>
-#include <stdio.h>
-#ifdef AFS_PTHREAD_ENV
-#include <assert.h>
-#else
-#include <afs/assert.h>
-#endif
#include <afs/prs_fs.h>
#include <afs/nfs.h>
-#include <string.h>
#include <afs/vlserver.h>
#include <lwp.h>
#include <lock.h>
#include <afs/afsutil.h>
-#include <rx/rx.h>
-#include <rx/rx_globals.h>
-#include <afs/auth.h>
-#include <rx/rxkad.h>
#include <afs/cellconfig.h>
#include <afs/keys.h>
#include <afs/volser.h>
#include <ubik.h>
+#include <afs/audit.h>
#include <afs/com_err.h>
-#include <errno.h>
#include <afs/cmd.h>
#include <afs/tcdata.h>
#include <afs/bubasics.h>
-#include <ctype.h>
-#include "error_macros.h"
#include <afs/budb_errs.h>
-#include "afs/butx.h"
+#include <afs/budb_client.h>
+#include <afs/bucoord_prototypes.h>
+#include <afs/butx.h>
+#include <afs/kautils.h>
+#include <afs/bc.h>
+
+#include "error_macros.h"
+#define XBSA_TCMAIN
#include "butc_xbsa.h"
+#include "butc_prototypes.h"
+#include "butc_internal.h"
#define N_SECURITY_OBJECTS 3
#define ERRCODE_RANGE 8 /* from error_table.h */
#define TL_PREFIX "TL"
#define CFG_PREFIX "CFG"
-struct ubik_client *cstruct;
-extern void TC_ExecuteRequest();
-extern int dbWatcher();
+extern struct ubik_client *cstruct;
FILE *logIO, *ErrorlogIO, *centralLogIO, *lastLogIO;
char lFile[AFSDIR_PATH_MAX];
-char logFile[256];
-char ErrorlogFile[256];
-char lastLogFile[256];
+char logFile[AFSDIR_PATH_MAX + 256];
+char ErrorlogFile[AFSDIR_PATH_MAX + 256];
+char lastLogFile[AFSDIR_PATH_MAX + 256];
char eFile[AFSDIR_PATH_MAX];
char tapeConfigFile[AFSDIR_PATH_MAX];
char pFile[AFSDIR_PATH_MAX];
char *restoretofile;
int forcemultiple;
-/* XBSA Global Parameters */
-afs_int32 xbsaType;
-#ifdef xbsa
-struct butx_transactionInfo butxInfo;
-
-#define rpc_c_protect_level_default 0
-afs_uint32 dumpRestAuthnLevel = rpc_c_protect_level_default;
-char *xbsaObjectOwner;
-char *appObjectOwner;
-char *adsmServerName;
-char *xbsaSecToken;
-char *xbsalGName;
-#endif
-
int maxpass;
#define PASSESMIN 1
#define PASSESMAX 10
afs_int32 BufferSize; /* Size in B stored for data */
char *centralLogFile;
afs_int32 lastLog; /* Log last pass info */
+int rxBind = 0;
+struct afsconf_dir *butc_confdir;
+int allow_unauth = 0;
-/* dummy routine for the audit work. It should do nothing since audits */
-/* occur at the server level and bos is not a server. */
-osi_audit()
-{
- return 0;
-}
+#define ADDRSPERSITE 16 /* Same global is in rx/rx_user.c */
+afs_uint32 SHostAddrs[ADDRSPERSITE];
static afs_int32
-SafeATOL(anum)
- register char *anum;
+SafeATOL(char *anum)
{
- register afs_int32 total;
- register int tc;
+ afs_int32 total;
+ int tc;
total = 0;
- while (tc = *anum) {
+ while ((tc = *anum)) {
if (tc < '0' || tc > '9')
return -1;
total *= 10;
* should deal with signed numbers. Should signal error if no digits
* seen.
*/
-atocl(numstring, crunit, number)
- char *numstring;
- char crunit; /* Units to report number in */
- afs_int32 *number;
+int
+atocl(char *numstring, char crunit, afs_int32 *number)
{
float total;
afs_int32 runits;
}
count =
- sscanf(numstring, "%f%c%s", (unsigned char *)&total, &cunit, rest);
+ sscanf(numstring, "%f%c%s", &total, &cunit, rest);
if ((count > 2) || (count <= 0))
return -1;
if (count == 1)
total *= 1024.0;
total += 0.5; /* Round up */
- if ((total > 0x7fffffff) || (total < 0)) /* Don't go over 2G */
- total = 0x7fffffff;
+ if ((total >= 2147483648.0) || (total < 0)) /* Don't go over 2G */
+ *number = 2147483647;
+ else
+ *number = total;
- *number = total;
return (0);
}
-/* replace last two ocurrences of / by _ */
-static
-stringReplace(name)
- char *name;
-{
- char *pos;
- char buffer[256];
-
- pos = strrchr(name, '/');
- *pos = '_';
- strcpy(buffer, pos);
- pos = strrchr(name, '/');
- *pos = '\0';
- strcat(name, buffer);
- return 0;
-}
-
-static
-stringNowReplace(logFile, deviceName)
- char *logFile, *deviceName;
-
+static int
+stringNowReplace(char *logFile, char *deviceName)
{
char *pos = 0;
char storeDevice[256];
deviceName += devPrefLen;
mvFlag++;
}
- while (pos = strchr(deviceName, devPrefix[0])) /* look for / or \ */
+ while ((pos = strchr(deviceName, devPrefix[0]))) /* look for / or \ */
*pos = '_';
strcat(logFile, deviceName);
/* now put back deviceName to the way it was */
- if (mvFlag) {
- mvFlag = 0;
+ if (mvFlag)
deviceName -= devPrefLen;
- }
+
strcpy(deviceName, storeDevice);
return (0);
#define LINESIZE 256
static afs_int32
-GetDeviceConfig(filename, config, portOffset)
- char *filename;
- struct tapeConfig *config;
- afs_int32 portOffset;
+GetDeviceConfig(char *filename, struct tapeConfig *config, afs_int32 portOffset)
{
FILE *devFile = 0;
char line[LINESIZE];
char devName[LINESIZE], tcapacity[LINESIZE], tfmsize[LINESIZE],
trest[LINESIZE];
afs_int32 aport;
- afs_uint32 capacity;
- afs_uint32 fmSize;
+ afs_int32 capacity;
+ afs_int32 fmSize;
afs_int32 code = 0, count;
/* Initialize the config struct */
/* GetConfigParams
*/
static afs_int32
-GetConfigParams(filename, port)
- char *filename;
- afs_int32 port;
+GetConfigParams(char *filename, afs_int32 port)
{
- char paramFile[256];
+ char paramFile[AFSDIR_PATH_MAX + 257];
FILE *devFile = 0;
char line[LINESIZE], cmd[LINESIZE], value[LINESIZE];
afs_int32 code = 0;
groupId = 0; /* Group id for multiple dumps */
/* Try opening the CFG_<port> file */
- sprintf(paramFile, "%s_%d", filename, port);
+ snprintf(paramFile, sizeof(paramFile), "%s_%d", filename, port);
devFile = fopen(paramFile, "r");
if (devFile) {
/* Set log names to TL_<port>, TL_<port>.lp and TE_<port> */
- sprintf(logFile, "%s_%d", lFile, port);
- sprintf(lastLogFile, "%s_%d.lp", lFile, port);
- sprintf(ErrorlogFile, "%s_%d", eFile, port);
+ snprintf(logFile, sizeof(logFile), "%s_%d", lFile, port);
+ snprintf(lastLogFile, sizeof(lastLogFile), "%s_%d.lp", lFile, port);
+ snprintf(ErrorlogFile, sizeof(ErrorlogFile), "%s_%d", eFile, port);
} else if (CONF_XBSA) {
/* If configured as XBSA, a configuration file CFG_<port> must exist */
printf("Cannot open configuration file %s", paramFile);
continue;
}
- opencallout = (char *)malloc(strlen(value) + 1);
- strcpy(opencallout, value);
+ opencallout = strdup(value);
printf("Tape mount callout routine is %s\n", opencallout);
}
continue;
}
- closecallout = (char *)malloc(strlen(value) + 1);
- strcpy(closecallout, value);
+ closecallout = strdup(value);
printf("Tape unmount callout routine is %s\n", closecallout);
}
else if (!strcmp(cmd, "BUFFERSIZE")) {
afs_int32 size;
- afs_int32 tapeblocks;
+ afs_int32 numTapeblocks;
if (!CONF_XBSA) {
if (atocl(value, 'K', &size)) {
/* A tapeblock is 16KB. Determine # of tapeblocks. Then
* determine BufferSize needed for that many tapeblocks.
*/
- tapeblocks = size / 16;
- if (tapeblocks <= 0)
- tapeblocks = 1;
- printf("BUFFERSIZE is %u KBytes\n", (tapeblocks * 16));
- BufferSize = tapeblocks * BUTM_BLOCKSIZE;
+ numTapeblocks = size / 16;
+ if (numTapeblocks <= 0)
+ numTapeblocks = 1;
+ printf("BUFFERSIZE is %u KBytes\n", (numTapeblocks * 16));
+ BufferSize = numTapeblocks * BUTM_BLOCKSIZE;
} else {
#ifdef xbsa
if (atocl(value, 'B', &size)) {
cmd);
continue;
}
- xbsaObjectOwner = malloc(strlen(value) + 1);
- strcpy(xbsaObjectOwner, value);
+ xbsaObjectOwner = strdup(value);
printf("XBSA node is %s\n", xbsaObjectOwner);
}
cmd);
continue;
}
- adsmServerName = malloc(strlen(value) + 1);
- strcpy(adsmServerName, value);
+ adsmServerName = strdup(value);
printf("XBSA server is %s\n", adsmServerName);
}
continue;
}
- xbsaSecToken = malloc(strlen(value) + 1);
- strcpy(xbsaSecToken, value);
+ xbsaSecToken = strdup(value);
printf("XBSA Password has been read\n");
}
cmd);
continue;
}
- xbsalGName = malloc(strlen(value) + 1);
- strcpy(xbsalGName, value);
+ xbsalGName = strdup(value);
printf("XBSA management class is %s\n", xbsalGName);
}
#endif
}
else if (!strcmp(cmd, "CENTRALLOG")) {
- centralLogFile = malloc(strlen(value) + 1);
- strcpy(centralLogFile, value);
+ centralLogFile = strdup(value);
printf("Central log file is %s\n", centralLogFile);
}
statusSize *= BufferSize;
if (statusSize < 0)
statusSize = 0x7fffffff; /*max size */
- printf("Status every %ld Bytes\n", statusSize);
+ printf("Status every %ld Bytes\n", afs_printable_int32_ld(statusSize));
}
error_exit:
return (code);
}
-static
-WorkerBee(as, arock)
- struct cmd_syndesc *as;
- char *arock;
+#ifdef xbsa
+static void
+xbsa_shutdown(int x)
{
- register afs_int32 code;
- struct rx_securityClass *(securityObjects[3]);
+ xbsa_Finalize(&butxInfo);
+ exit(0);
+}
+#endif
+
+static int
+tc_IsLocalRealmMatch(void *rock, char *name, char *inst, char *cell)
+{
+ struct afsconf_dir *dir = (struct afsconf_dir *)rock;
+ afs_int32 islocal = 0; /* default to no */
+ int code;
+
+ code = afsconf_IsLocalRealmMatch(dir, &islocal, name, inst, cell);
+ if (code) {
+ TLog(0, "Failed local realm check; code=%d, name=%s, inst=%s, cell=%s\n",
+ code, name, inst, cell);
+ }
+ return islocal;
+}
+
+static int
+WorkerBee(struct cmd_syndesc *as, void *arock)
+{
+ afs_int32 code, numClasses;
+ struct rx_securityClass *(nullObjects[1]), **secObjs, **allObjs;
struct rx_service *service;
- struct ktc_token ttoken;
+ time_t tokenExpires;
char cellName[64];
int localauth;
/*process arguments */
#else
PROCESS dbWatcherPid;
#endif
+ char hoststr[16];
+ afs_uint32 host = htonl(INADDR_ANY);
+ char *auditFileName = NULL;
+ char *auditInterface = NULL;
debugLevel = 0;
}
if (as->parms[6].items) { /* -restoretofile */
- int s = strlen(as->parms[6].items->data);
- restoretofile = malloc(s + 1);
- strncpy(restoretofile, as->parms[6].items->data, s + 1);
+ restoretofile = strdup(as->parms[6].items->data);
printf("Restore to file '%s'\n", restoretofile);
}
struct stat sbuf;
afs_int32 statcode;
#ifndef AFS_NT40_ENV
- char path[AFSDIR_PATH_MAX];
+ char *path;
#endif
statcode = stat(centralLogFile, &sbuf);
}
#ifndef AFS_NT40_ENV
/* Make sure it is not in AFS, has to have been created first */
- if (!realpath(centralLogFile, path)) {
+ path = malloc(AFSDIR_PATH_MAX);
+ if (path == NULL || !realpath(centralLogFile, path)) {
fprintf(stderr,
"Warning: can't determine real path of '%s' (%d)\n",
centralLogFile, errno);
exit(1);
}
}
+ free(path);
#endif
/* Write header if created it */
}
}
+ /* Open the configuration directory */
+ butc_confdir = afsconf_Open(AFSDIR_SERVER_ETC_DIRPATH);
+ if (butc_confdir == NULL) {
+ TLog(0, "Failed to open server configuration directory");
+ exit(1);
+ }
+
+ if (afsconf_CountKeys(butc_confdir) == 0) {
+ TLog(0, "WARNING: No encryption keys found! "
+ "All authenticated accesses will fail. "
+ "Run akeyconvert or asetkey to import encryption keys.\n");
+ }
+
+ /* Start auditing */
+ osi_audit_init();
+ if (as->parms[9].items) {
+ auditFileName = as->parms[9].items->data;
+ }
+ if (auditFileName != NULL)
+ osi_audit_file(auditFileName);
+ if (as->parms[10].items) {
+ auditInterface = as->parms[10].items->data;
+ if (osi_audit_interface(auditInterface)) {
+ TLog(0, "Invalid audit interface '%s'\n", auditInterface);
+ exit(1);
+ }
+ }
+ osi_audit(TC_StartEvent, 0, AUD_END);
+ osi_audit_set_user_check(butc_confdir, tc_IsLocalRealmMatch);
+
if (as->parms[1].items) {
debugLevel = SafeATOL(as->parms[1].items->data);
if (debugLevel == -1) {
rc = InitToServer(0 /*taskid */ , &butxInfo, adsmServerName);
if (rc != XBSA_SUCCESS)
return (1);
+ (void)signal(SIGINT, xbsa_shutdown);
+ (void)signal(SIGHUP, xbsa_shutdown);
}
#endif /*xbsa */
autoQuery = 0;
localauth = (as->parms[5].items ? 1 : 0);
+ rxBind = (as->parms[8].items ? 1 : 0);
+ allow_unauth = (as->parms[11].items ? 1 : 0);
+
+ if (!allow_unauth && !localauth) {
+ const char *errstr = "Neither -localauth nor -allow_unauthenticated was provided; refusing to start in unintended insecure configuration\n";
+ TLog(0, "%s", (char *)errstr);
+ exit(1);
+ }
+
+ if (rxBind) {
+ afs_int32 ccode;
+ if (AFSDIR_SERVER_NETRESTRICT_FILEPATH ||
+ AFSDIR_SERVER_NETINFO_FILEPATH) {
+ char reason[1024];
+ ccode = afsconf_ParseNetFiles(SHostAddrs, NULL, NULL,
+ ADDRSPERSITE, reason,
+ AFSDIR_SERVER_NETINFO_FILEPATH,
+ AFSDIR_SERVER_NETRESTRICT_FILEPATH);
+ } else
+ {
+ ccode = rx_getAllAddr(SHostAddrs, ADDRSPERSITE);
+ }
+ if (ccode == 1)
+ host = SHostAddrs[0];
+ }
- code = rx_Init(htons(BC_TAPEPORT + portOffset));
+ TLog(0, "butc binding rx to %s:%d\n",
+ afs_inet_ntoa_r(host, hoststr), BC_TAPEPORT + portOffset);
+ code = rx_InitHost(host, htons(BC_TAPEPORT + portOffset));
if (code) {
TapeLog(0, 0, code, 0, "rx init failed on port %u\n",
BC_TAPEPORT + portOffset);
rx_SetRxDeadTime(150);
/* Establish connection with the vldb server */
- code = vldbClientInit(0, localauth, cellName, &cstruct, &ttoken);
+ code = vldbClientInit(0, localauth, cellName, &cstruct, &tokenExpires);
if (code) {
TapeLog(0, 0, code, 0, "Can't access vldb\n");
return code;
/*initialize the dumpNode list */
InitNodeList(portOffset);
- deviceLatch =
- (struct deviceSyncNode *)(malloc(sizeof(struct deviceSyncNode)));
+ deviceLatch = malloc(sizeof(struct deviceSyncNode));
Lock_Init(&(deviceLatch->lock));
deviceLatch->flags = 0;
/* initialize database support, volume support, and logs */
- /* Create a single security object, in this case the null security
- * object, for unauthenticated connections, which will be used to control
- * security on connections made to this server
+ /*
+ * Create security objects for the Rx server functionality. Historically
+ * this was a single rxnull security object, since the tape controller was
+ * run by an operator that had local access to the tape device and some
+ * administrative privilege in the cell (to be able to perform volume-level
+ * accesses), but on a machine that was not necessarily trusted to hold the
+ * cell-wide key.
+ *
+ * Such a configuration is, of course, insecure because anyone can make
+ * inbound RPCs and manipulate the database, including creating bogus
+ * dumps and restoring them! Additionally, in modern usage, butc is
+ * frequently run with -localauth to authenticate its outbound connections
+ * to the volservers and budb with the cell-wide key, in which case the
+ * cell-wide key is present and could be used to authenticate incoming
+ * connections as well.
+ *
+ * If -localauth is in use, create the full barrage of server security
+ * objects, including rxkad, so that inbound connections can be verified
+ * to only be made by authenticated clients. Otherwise, only the rxnull
+ * class is in use with a single server security object. Note that butc
+ * will refuse to start in this configuration unless the
+ * "-allow_unauthenticated" flag is provided, indicating that the operator
+ * has ensured that incoming connections are appropriately restricted by
+ * firewall configuration or network topology.
*/
- securityObjects[0] = rxnull_NewServerSecurityObject();
- securityObjects[1] = (struct rx_securityClass *)0; /* don't bother with rxvab */
- if (!securityObjects[0]) {
- TLog(0, "rxnull_NewServerSecurityObject");
- exit(1);
+ if (allow_unauth) {
+ nullObjects[RX_SECIDX_NULL] = rxnull_NewServerSecurityObject();
+ if (!nullObjects[RX_SECIDX_NULL]) {
+ TLog(0, "rxnull_NewServerSecurityObject");
+ exit(1);
+ }
+ numClasses = 1;
+ secObjs = nullObjects;
+ } else {
+ /* Must be -localauth, so the cell keys are available. */
+ afsconf_BuildServerSecurityObjects(butc_confdir, &allObjs, &numClasses);
+ secObjs = allObjs;
}
service =
- rx_NewService(0, 1, "BUTC", securityObjects, 3, TC_ExecuteRequest);
+ rx_NewServiceHost(host, 0, 1, "BUTC", secObjs, numClasses, TC_ExecuteRequest);
if (!service) {
TLog(0, "rx_NewService");
exit(1);
exit(1);
}
/* This call is here to verify that we are authentiated.
- * The call does nothing and will return BUDB_NOTPERMITTED
+ * The call does nothing and will return BUDB_NOTPERMITTED
* if we don't belong.
*/
code = bcdb_deleteDump(0, 0, 0, 0);
TLog(0, "Starting Tape Coordinator: Port offset %u Debug level %u\n",
portOffset, debugLevel);
- TLog(0, "Token expires: %s\n", cTIME(&ttoken.endTime));
+ TLog(0, "Token expires: %s\n", cTIME(&tokenExpires));
rx_StartServer(1); /* Donate this process to the server process pool */
TLog(0, "Error: StartServer returned");
#include "AFS_component_version_number.c"
#endif
-main(argc, argv)
- int argc;
- char **argv;
+int
+main(int argc, char **argv)
{
- register struct cmd_syndesc *ts;
- register struct cmd_item *ti;
+ struct cmd_syndesc *ts;
+ struct cmd_item *ti;
#ifdef AFS_AIX32_ENV
/*
- * The following signal action for AIX is necessary so that in case of a
- * crash (i.e. core is generated) we can include the user's data section
+ * The following signal action for AIX is necessary so that in case of a
+ * crash (i.e. core is generated) we can include the user's data section
* in the core dump. Unfortunately, by default, only a partial core is
* generated which, in many cases, isn't too useful.
*/
setlinebuf(stdout);
- ts = cmd_CreateSyntax(NULL, WorkerBee, NULL, "tape coordinator");
+ ts = cmd_CreateSyntax(NULL, WorkerBee, NULL, 0, "tape coordinator");
cmd_AddParm(ts, "-port", CMD_SINGLE, CMD_OPTIONAL, "port offset");
cmd_AddParm(ts, "-debuglevel", CMD_SINGLE, CMD_OPTIONAL, "0 | 1 | 2");
cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell name");
"file to restore to");
cmd_AddParm(ts, "-xbsaforcemultiple", CMD_FLAG, (CMD_OPTIONAL | CMD_HIDE),
"Force multiple XBSA server support");
+ cmd_AddParm(ts, "-rxbind", CMD_FLAG, CMD_OPTIONAL,
+ "bind Rx socket");
+ cmd_AddParm(ts, "-auditlog", CMD_SINGLE, CMD_OPTIONAL, "location of audit log");
+ cmd_AddParm(ts, "-audit-interface", CMD_SINGLE, CMD_OPTIONAL,
+ "interface to use for audit logging");
+ cmd_AddParm(ts, "-allow_unauthenticated", CMD_FLAG, CMD_OPTIONAL,
+ "allow unauthenticated inbound RPCs (requires firewalling)");
/* Initialize dirpaths */
if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) {
/* setup the file paths */
strcompose(eFile, AFSDIR_PATH_MAX, AFSDIR_SERVER_BACKUP_DIRPATH, "/",
- TE_PREFIX, NULL);
+ TE_PREFIX, (char *)NULL);
strcompose(lFile, AFSDIR_PATH_MAX, AFSDIR_SERVER_BACKUP_DIRPATH, "/",
- TL_PREFIX, NULL);
+ TL_PREFIX, (char *)NULL);
strcompose(pFile, AFSDIR_PATH_MAX, AFSDIR_SERVER_BACKUP_DIRPATH, "/",
- CFG_PREFIX, NULL);
+ CFG_PREFIX, (char *)NULL);
strcpy(tapeConfigFile, AFSDIR_SERVER_TAPECONFIG_FILEPATH);
/* special case "no args" case since cmd_dispatch gives help message
* instead
*/
if (argc == 1) {
- ts = (struct cmd_syndesc *)malloc(sizeof(struct cmd_syndesc));
- memset(ts, 0, sizeof(*ts));
+ ts = calloc(1, sizeof(struct cmd_syndesc));
- ti = (struct cmd_item *)malloc(sizeof(struct cmd_item));
+ ti = malloc(sizeof(struct cmd_item));
ti->next = 0;
ti->data = "0";
ts->parms[0].items = ti;
- ti = (struct cmd_item *)malloc(sizeof(struct cmd_item));
+ ti = malloc(sizeof(struct cmd_item));
ti->next = 0;
ti->data = "0";
ts->parms[1].items = ti;
- ts->parms[2].items = (struct cmd_item *)NULL;
- ts->parms[3].items = (struct cmd_item *)NULL;
- ts->parms[4].items = (struct cmd_item *)NULL;
- ts->parms[5].items = (struct cmd_item *)NULL;
+ ts->parms[2].items = NULL;
+ ts->parms[3].items = NULL;
+ ts->parms[4].items = NULL;
+ ts->parms[5].items = NULL;
return WorkerBee(ts, NULL);
} else
return cmd_Dispatch(argc, argv);