xref: /illumos-gate/usr/src/cmd/bnu/sysfiles.c (revision 486c6fda)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 #include "uucp.h"
30 
31 #include <unistd.h>
32 #include "sysfiles.h"
33 #include <sys/stropts.h>
34 
35 /*
36  * manage systems files (Systems, Devices, and Dialcodes families).
37  *
38  * also manage new file Devconfig, allows per-device setup.
39  * present use is to specify what streams modules to push/pop for
40  * AT&T TLI/streams network.
41  *
42  * TODO:
43  *    call bsfix()?
44  *    combine the 3 versions of everything (sys, dev, and dial) into one.
45  *    allow arbitrary classes of service.
46  *    need verifysys() for uucheck.
47  *    nameserver interface?
48  *    pass sysname (or 0) to getsysline().  (might want reg. exp. or NS processing
49  */
50 
51 /* private variables */
52 static void tokenize(), nameparse(), setfile(), setioctl(),
53 	scansys(), scancfg(), setconfig();
54 static int namematch(), nextdialers(), nextdevices(), nextsystems(), getaline();
55 
56 /* pointer arrays might be dynamically allocated */
57 static char *Systems[64] = {0};	/* list of Systems files */
58 static char *Devices[64] = {0};	/* list of Devices files */
59 static char *Dialers[64] = {0};	/* list of Dialers files */
60 static char *Pops[64] = {0};	/* list of STREAMS modules to be popped */
61 static char *Pushes[64] = {0};	/* list of STREAMS modules to be pushed */
62 
63 static int nsystems;		/* index into list of Systems files */
64 static int ndevices;		/* index into list of Devices files */
65 static int ndialers;		/* index into list of Dialers files */
66 static int npops;		/* index into list of STREAMS modules */
67 							/*to be popped */
68 static int npushes;		/* index into list of STREAMS modules */
69 							/*to be pushed */
70 
71 GLOBAL unsigned connecttime = CONNECTTIME;
72 GLOBAL unsigned expecttime = EXPECTTIME;
73 GLOBAL unsigned msgtime = MSGTIME;
74 
75 static FILE *fsystems;
76 static FILE *fdevices;
77 static FILE *fdialers;
78 
79 static char errformat[BUFSIZ];
80 
81 /* this might be dynamically allocated */
82 #define NTOKENS 16
83 static char *tokens[NTOKENS], **tokptr;
84 
85 /* export these */
86 EXTERN void sysreset(), devreset(), dialreset(), setdevcfg(), setservice();
87 EXTERN char *strsave();
88 
89 /* import these */
90 extern char *strcpy(), *strtok(), *strchr(), *strsave();
91 EXTERN int eaccess();
92 
93 /*
94  * setservice init's Systems, Devices, Dialers lists from Sysfiles
95  */
96 GLOBAL void
97 setservice(service)
98 char *service;
99 {
100 	char	*prev = _uu_setlocale(LC_ALL, "C");
101 
102 	setconfig();
103 	scansys(service);
104 	(void) _uu_resetlocale(LC_ALL, prev);
105 	return;
106 }
107 
108 /*
109  * setdevcfg init's Pops, Pushes lists from Devconfig
110  */
111 
112 GLOBAL void
113 setdevcfg(service, device)
114 char *service, *device;
115 {
116 	char	*prev = _uu_setlocale(LC_ALL, "C");
117 
118 	scancfg(service, device);
119 	(void) _uu_resetlocale(LC_ALL, prev);
120 	return;
121 }
122 
123 /*	administrative files access */
124 GLOBAL int
125 sysaccess(type)
126 int type;
127 {
128 	switch (type) {
129 
130 	case ACCESS_SYSTEMS:
131 		return(access(Systems[nsystems], R_OK));
132 	case ACCESS_DEVICES:
133 		return(access(Devices[ndevices], R_OK));
134 	case ACCESS_DIALERS:
135 		return(access(Dialers[ndialers], R_OK));
136 	case EACCESS_SYSTEMS:
137 		return(eaccess(Systems[nsystems], R_OK));
138 	case EACCESS_DEVICES:
139 		return(eaccess(Devices[ndevices], R_OK));
140 	case EACCESS_DIALERS:
141 		return(eaccess(Dialers[ndialers], R_OK));
142 	default:
143 		(void)sprintf(errformat, "bad access type %d", type);
144 		logent(errformat, "sysaccess");
145 		return(FAIL);
146 	}
147 }
148 
149 
150 /*
151  * read Sysfiles, set up lists of Systems/Devices/Dialers file names.
152  * allow multiple entries for a given service, allow a service
153  * type to describe resources more than once, e.g., systems=foo:baz systems=bar.
154  */
155 static void
156 scansys(service)
157 char *service;
158 {	FILE *f;
159 	char *tok, buf[BUFSIZ];
160 
161 	Systems[0] = Devices[0] = Dialers[0] = NULL;
162 	if ((f = fopen(SYSFILES, "r")) != 0) {
163 		while (getaline(f, buf) > 0) {
164 			/* got a (logical) line from Sysfiles */
165 			/* strtok's of this buf continue in tokenize() */
166 			tok = strtok(buf, " \t");
167 			if (namematch("service=", tok, service)) {
168 				tokenize();
169 				nameparse();
170 			}
171 		}
172 		(void) fclose(f);
173 	}
174 
175 	/* if didn't find entries in Sysfiles, use defaults */
176 	if (Systems[0] == NULL) {
177 		Systems[0] = strsave(SYSTEMS);
178 		ASSERT(Systems[0] != NULL, Ct_ALLOCATE, "scansys: Systems", 0);
179 		Systems[1] = NULL;
180 	}
181 	if (Devices[0] == NULL) {
182 		Devices[0] = strsave(DEVICES);
183 		ASSERT(Devices[0] != NULL, Ct_ALLOCATE, "scansys: Devices", 0);
184 		Devices[1] = NULL;
185 	}
186 	if (Dialers[0] == NULL) {
187 		Dialers[0] = strsave(DIALERS);
188 		ASSERT(Dialers[0] != NULL, Ct_ALLOCATE, "scansys: Dialers", 0);
189 		Dialers[1] = NULL;
190 	}
191 	return;
192 }
193 
194 
195 /*
196  * read Devconfig.  allow multiple entries for a given service, allow a service
197  * type to describe resources more than once, e.g., push=foo:baz push=bar.
198  */
199 static void
200 scancfg(service, device)
201 char *service, *device;
202 {	FILE *f;
203 	char *tok, buf[BUFSIZ];
204 
205 	/* (re)initialize device-specific information */
206 
207 	npops = npushes = 0;
208 	Pops[0] = Pushes[0] = NULL;
209 	connecttime = CONNECTTIME;
210 	expecttime = EXPECTTIME;
211 	msgtime = MSGTIME;
212 
213 	if ((f = fopen(DEVCONFIG, "r")) != 0) {
214 		while (getaline(f, buf) > 0) {
215 			/* got a (logical) line from Devconfig */
216 			/* strtok's of this buf continue in tokenize() */
217 			tok = strtok(buf, " \t");
218 			if (namematch("service=", tok, service)) {
219 				tok = strtok((char *)0, " \t");
220 				if ( namematch("device=", tok, device)) {
221 					tokenize();
222 					nameparse();
223 				}
224 			}
225 		}
226 		(void) fclose(f);
227 	}
228 
229 	return;
230 
231 }
232 
233 /*
234  *  given a file pointer and buffer, construct logical line in buffer
235  *  (i.e., concatenate lines ending in '\').  return length of line
236  *  ASSUMES that buffer is BUFSIZ long!
237  */
238 
239 static int
240 getaline(f, line)
241 FILE *f;
242 char *line;
243 {	char *lptr, *lend;
244 
245 	lptr = line;
246 	while (fgets(lptr, (line + BUFSIZ) - lptr, f) != NULL) {
247 		lend = lptr + strlen(lptr);
248 		if (lend == lptr || lend[-1] != '\n')
249 			/* empty buf or line too long! */
250 			break;
251 		*--lend = '\0'; /* lop off ending '\n' */
252 		if ( lend == line ) /* empty line - ignore */
253 			continue;
254 		lptr = lend;
255 		if (lend[-1] != '\\')
256 			break;
257 		/* continuation */
258 		lend[-1] = ' ';
259 	}
260 	return(lptr - line);
261 }
262 
263 /*
264  * given a label (e.g., "service=", "device="), a name ("cu", "uucico"),
265  *  and a line:  if line begins with the label and if the name appears
266  * in a colon-separated list of names following the label, return true;
267  * else return false
268  */
269 static int
270 namematch(label, line, name)
271 char *label, *line, *name;
272 {	char *lend;
273 
274 	if (strncmp(label, line, strlen(label)) != SAME) {
275 		return(FALSE);	/* probably a comment line */
276 	}
277 	line += strlen(label);
278 	if (*line == '\0')
279 		return(FALSE);
280 	/*
281 	 * can't use strtok() in the following because scansys(),
282 	 * scancfg() do an initializing call to strtok() before
283 	 * coming here and then CONTINUE calling strtok() in tokenize(),
284 	 * after returning from namematch().
285 	 */
286 	while ((lend = strchr(line, ':')) != NULL) {
287 		*lend = '\0';
288 		if (strcmp(line, name) == SAME)
289 			return(TRUE);
290 		line = lend+1;
291 	}
292 	return(strcmp(line, name) == SAME);
293 }
294 
295 /*
296  * tokenize() continues pulling tokens out of a buffer -- the
297  * initializing call to strtok must have been made before calling
298  * tokenize() -- and starts stuffing 'em into tokptr.
299  */
300 static void
301 tokenize()
302 {	char *tok;
303 
304 	tokptr = tokens;
305 	while ((tok = strtok((char *) NULL, " \t")) != NULL) {
306 		*tokptr++ = tok;
307 		if (tokptr - tokens >= NTOKENS)
308 			break;
309 	}
310 	*tokptr = NULL;
311 	return;
312 }
313 
314 /*
315  * look at top token in array: should be line of the form
316  *	name=item1:item2:item3...
317  * if name is one we recognize, then call set[file|ioctl] to set up
318  * corresponding list.  otherwise, log bad name.
319  */
320 static void
321 nameparse()
322 {	char **line, *equals;
323 	int temp;
324 
325 #define setuint(a,b,c) a = ( ((temp = atoi(b)) <= 0) ? (c) : temp )
326 
327 	for (line = tokens; (line - tokens) < NTOKENS && *line; line++) {
328 		equals = strchr(*line, '=');
329 		if (equals == NULL)
330 			continue;	/* may be meaningful someday? */
331 		*equals = '\0';
332 		/* ignore entry with empty rhs */
333 		if (*++equals == '\0')
334 			continue;
335 		if (strcmp(*line, "systems") == SAME)
336 			setfile(Systems, equals);
337 		else if (strcmp(*line, "devices") == SAME)
338 			setfile(Devices, equals);
339 		else if (strcmp(*line, "dialers") == SAME)
340 			setfile(Dialers, equals);
341 		else if (strcmp(*line, "pop") == SAME)
342 			setioctl(Pops, equals);
343 		else if (strcmp(*line, "push") == SAME)
344 			setioctl(Pushes, equals);
345 		else if (strcmp(*line, "connecttime") == SAME)
346 			setuint(connecttime, equals, CONNECTTIME);
347 		else if (strcmp(*line, "expecttime") == SAME)
348 			setuint(expecttime, equals, EXPECTTIME);
349 		else if (strcmp(*line, "msgtime") == SAME)
350 			setuint(msgtime, equals, MSGTIME);
351 		else {
352 			(void)sprintf(errformat,"unrecognized label %s",*line);
353 			logent(errformat, "Sysfiles|Devconfig");
354 		}
355 	}
356 	return;
357 }
358 
359 /*
360  * given the list for a particular type (systems, devices,...)
361  * and a line of colon-separated files, add 'em to list
362  */
363 
364 static void
365 setfile(type, line)
366 char **type, *line;
367 {	char **tptr, *tok;
368 	char expandpath[BUFSIZ];
369 
370 	if (*line == 0)
371 		return;
372 	tptr = type;
373 	while (*tptr)		/* skip over existing entries to*/
374 		tptr++;		/* concatenate multiple entries */
375 
376 	for (tok = strtok(line, ":"); tok != NULL;
377 	tok = strtok((char *) NULL, ":")) {
378 		expandpath[0] = '\0';
379 		if ( *tok != '/' )
380 			/* by default, file names are relative to SYSDIR */
381 			sprintf(expandpath, "%s/", SYSDIR);
382 		strcat(expandpath, tok);
383 		if (eaccess(expandpath, R_OK) != 0)
384 			/* if we can't read it, no point in adding to list */
385 			continue;
386 		*tptr = strsave(expandpath);
387 		ASSERT(*tptr != NULL, Ct_ALLOCATE, "setfile: tptr", 0);
388 		tptr++;
389 	}
390 	return;
391 }
392 
393 /*
394  * given the list for a particular ioctl (push, pop)
395  * and a line of colon-separated modules, add 'em to list
396  */
397 
398 static void
399 setioctl(type, line)
400 char **type, *line;
401 {	char **tptr, *tok;
402 
403 	if (*line == 0)
404 		return;
405 	tptr = type;
406 	while (*tptr)		/* skip over existing entries to*/
407 		tptr++;		/* concatenate multiple entries */
408 	for (tok = strtok(line, ":"); tok != NULL;
409 	tok = strtok((char *) NULL, ":")) {
410 		*tptr = strsave(tok);
411 		ASSERT(*tptr != NULL, Ct_ALLOCATE, "setioctl: tptr", 0);
412 		tptr++;
413 	}
414 	return;
415 }
416 
417 /*
418  * reset Systems files
419  */
420 GLOBAL void
421 sysreset()
422 {
423 	if (fsystems)
424 		fclose(fsystems);
425 	fsystems = NULL;
426 	nsystems = 0;
427 	devreset();
428 	return;
429 }
430 
431 /*
432  * reset Devices files
433  */
434 GLOBAL void
435 devreset()
436 {
437 	if (fdevices)
438 		fclose(fdevices);
439 	fdevices = NULL;
440 	ndevices = 0;
441 	dialreset();
442 	return;
443 }
444 
445 /*
446  * reset Dialers files
447  */
448 GLOBAL void
449 dialreset()
450 {
451 	if (fdialers)
452 		fclose(fdialers);
453 	fdialers = NULL;
454 	ndialers = 0;
455 	return;
456 }
457 
458 /*
459  * get next line from Systems file
460  * return TRUE if successful, FALSE if not
461  */
462 GLOBAL int
463 getsysline(char *buf, int len)
464 {
465 	char	*prev = _uu_setlocale(LC_ALL, "C");
466 
467 	if (Systems[0] == NULL)
468 		/* not initialized via setservice() - use default */
469 		setservice("uucico");
470 
471 	/* initialize devices and dialers whenever a new line is read */
472 	/* from systems */
473 	devreset();
474 	if (fsystems == NULL)
475 		if (nextsystems() == FALSE) {
476 			(void) _uu_resetlocale(LC_ALL, prev);
477 			return(FALSE);
478 		}
479 
480 	ASSERT(len >= BUFSIZ, "BUFFER TOO SMALL", "getsysline", 0);
481 	for(;;) {
482 		while (getaline(fsystems, buf) != 0)
483 		    if ((*buf != '#') && (*buf != ' ') &&
484 			(*buf != '\t') && (*buf != '\n')) {
485 			(void) _uu_resetlocale(LC_ALL, prev);
486 			return(TRUE);
487 		}
488 		if (nextsystems() == FALSE) {
489 			(void) _uu_resetlocale(LC_ALL, prev);
490 			return(FALSE);
491 		}
492 	}
493 }
494 
495 /*
496  * move to next systems file.  return TRUE if successful, FALSE if not
497  */
498 static int
499 nextsystems()
500 {
501 	devreset();
502 
503 	if (fsystems != NULL) {
504 		(void) fclose(fsystems);
505 		nsystems++;
506 	} else {
507 		nsystems = 0;
508 	}
509 	for ( ; Systems[nsystems] != NULL; nsystems++)
510 		if ((fsystems = fopen(Systems[nsystems], "r")) != NULL)
511 			return(TRUE);
512 	return(FALSE);
513 }
514 
515 /*
516  * get next line from Devices file
517  * return TRUE if successful, FALSE if not
518  */
519 GLOBAL int
520 getdevline(char *buf, int len)
521 {
522 	char	*prev = _uu_setlocale(LC_ALL, "C");
523 
524 	if (Devices[0] == NULL)
525 		/* not initialized via setservice() - use default */
526 		setservice("uucico");
527 
528 	if (fdevices == NULL)
529 		if (nextdevices() == FALSE) {
530 			(void) _uu_resetlocale(LC_ALL, prev);
531 			return(FALSE);
532 		}
533 	for(;;) {
534 		if (fgets(buf, len, fdevices) != NULL) {
535 			(void) _uu_resetlocale(LC_ALL, prev);
536 			return(TRUE);
537 		}
538 		if (nextdevices() == FALSE) {
539 			(void) _uu_resetlocale(LC_ALL, prev);
540 			return(FALSE);
541 		}
542 	}
543 }
544 
545 /*
546  * move to next devices file.  return TRUE if successful, FALSE if not
547  */
548 static int
549 nextdevices()
550 {
551 	if (fdevices != NULL) {
552 		(void) fclose(fdevices);
553 		ndevices++;
554 	} else {
555 		ndevices = 0;
556 	}
557 	for ( ; Devices[ndevices] != NULL; ndevices++)
558 		if ((fdevices = fopen(Devices[ndevices], "r")) != NULL)
559 			return(TRUE);
560 	return(FALSE);
561 }
562 
563 
564 /*
565  * get next line from Dialers file
566  * return TRUE if successful, FALSE if not
567  */
568 
569 GLOBAL int
570 getdialline(char *buf, int len)
571 {
572 	char	*prev = _uu_setlocale(LC_ALL, "C");
573 
574 	if (Dialers[0] == NULL)
575 		/* not initialized via setservice() - use default */
576 		setservice("uucico");
577 
578 	if (fdialers == NULL)
579 		if (nextdialers() == FALSE) {
580 			(void) _uu_resetlocale(LC_ALL, prev);
581 			return(FALSE);
582 		}
583 	for(;;) {
584 		if (fgets(buf, len, fdialers) != NULL) {
585 			(void) _uu_resetlocale(LC_ALL, prev);
586 			return(TRUE);
587 		}
588 		if (nextdialers() == FALSE) {
589 			(void) _uu_resetlocale(LC_ALL, prev);
590 			return(FALSE);
591 		}
592 	}
593 }
594 
595 /*
596  * move to next dialers file.  return TRUE if successful, FALSE if not
597  */
598 static int
599 nextdialers()
600 {
601 	if (fdialers) {
602 		(void) fclose(fdialers);
603 		ndialers++;
604 	} else {
605 		ndialers = 0;
606 	}
607 
608 	for ( ; Dialers[ndialers] != NULL; ndialers++)
609 		if ((fdialers = fopen(Dialers[ndialers], "r")) != NULL)
610 			return(TRUE);
611 	return(FALSE);
612 }
613 
614 /*
615  * get next module to be popped
616  * return TRUE if successful, FALSE if not
617  */
618 static int
619 getpop(buf, len, optional)
620 char *buf;
621 int len, *optional;
622 {
623 	int slen;
624 
625 	if ( Pops[0] == NULL || Pops[npops] == NULL )
626 		return(FALSE);
627 
628 	/*	if the module name is enclosed in parentheses,	*/
629 	/*	is optional. set flag & strip parens		*/
630 	slen = strlen(Pops[npops]) - 1;
631 	if ( Pops[npops][0] == '('  && Pops[npops][slen] == ')' ) {
632 		*optional = 1;
633 		len = ( slen < len ? slen : len );
634 		strncpy(buf, &(Pops[npops++][1]), len);
635 	} else {
636 		*optional = 0;
637 		strncpy(buf, Pops[npops++], len);
638 	}
639 	buf[len-1] = '\0';
640 	return(TRUE);
641 }
642 
643 /*
644  * get next module to be pushed
645  * return TRUE if successful, FALSE if not
646  */
647 static int
648 getpush(buf, len)
649 char *buf;
650 int len;
651 {
652 	if ( Pushes[0] == NULL || Pushes[npushes] == NULL )
653 		return(FALSE);
654 	strncpy(buf, Pushes[npushes++], len);
655 	return(TRUE);
656 }
657 
658 /*
659  * pop/push requested modules
660  * return TRUE if successful, FALSE if not
661  */
662 GLOBAL int
663 pop_push(fd)
664 int fd;
665 {
666     char	strmod[FMNAMESZ], onstream[FMNAMESZ];
667     int		optional;
668     char	*prev = _uu_setlocale(LC_ALL, "C");
669 
670     /*	check for streams modules to pop	*/
671     while ( getpop(strmod, sizeof(strmod), &optional) ) {
672 	DEBUG(5, (optional ? "pop_push: optionally POPing %s\n"
673 			   : "pop_push: POPing %s\n" ), strmod);
674 	if ( ioctl(fd, I_LOOK, onstream) == -1 ) {
675 	    DEBUG(5, "pop_push: I_LOOK on fd %d failed ", fd);
676 	    DEBUG(5, "errno %d\n", errno);
677 	    (void) _uu_resetlocale(LC_ALL, prev);
678 	    return(FALSE);
679 	}
680 	if ( strcmp(strmod, onstream) != SAME ) {
681 	    if ( optional )
682 		continue;
683 	    DEBUG(5, "pop_push: I_POP: %s not there\n", strmod);
684 	    (void) _uu_resetlocale(LC_ALL, prev);
685 	    return(FALSE);
686 	}
687 	if ( ioctl(fd, I_POP, 0) == -1 ) {
688 	    DEBUG(5, "pop_push: I_POP on fd %d failed ", fd);
689 	    DEBUG(5, "errno %d\n", errno);
690 	    (void) _uu_resetlocale(LC_ALL, prev);
691 	    return(FALSE);
692 	}
693     }
694 
695     /*	check for streams modules to push	*/
696     while ( getpush(strmod, sizeof(strmod)) ) {
697 	DEBUG(5, "pop_push: PUSHing %s\n", strmod);
698 	if ( ioctl(fd, I_PUSH, strmod) == -1 ) {
699 	    DEBUG(5, "pop_push: I_PUSH on fd %d failed ", fd);
700 	    DEBUG(5, "errno %d\n", errno);
701 	    (void) _uu_resetlocale(LC_ALL, prev);
702 	    return(FALSE);
703 	}
704     }
705     (void) _uu_resetlocale(LC_ALL, prev);
706     return(TRUE);
707 }
708 
709 /*
710  * 	return name of currently open Systems file
711  */
712 GLOBAL char *
713 currsys()
714 {
715 	return(Systems[nsystems]);
716 }
717 
718 /*
719  * 	return name of currently open Devices file
720  */
721 GLOBAL char *
722 currdev()
723 {
724 	return(Devices[ndevices]);
725 }
726 
727 /*
728  * 	return name of currently open Dialers file
729  */
730 GLOBAL char *
731 currdial()
732 {
733 	return(Dialers[ndialers]);
734 }
735 
736 /*
737  * set configuration parameters provided in Config file
738  */
739 static void
740 setconfig()
741 {
742     FILE *f;
743     char buf[BUFSIZ];
744     char *tok;
745     extern char _ProtoCfg[];
746 
747     if ((f = fopen(CONFIG, "r")) != 0) {
748 	while (getaline(f, buf) > 0) {
749 	    /* got a (logical) line from Config file */
750 	    tok = strtok(buf, " \t");
751 	    if ( (tok != NULL) && (*tok != '#') ) {
752 		/* got a token */
753 
754 		/* this probably should be table driven when
755 		 * the list of configurable parameters grows.
756 		 */
757 		if (strncmp("Protocol=", tok, strlen("Protocol=")) == SAME) {
758 		    tok += strlen("Protocol=");
759 		    if ( *tok != '\0' ) {
760 			if ( _ProtoCfg[0] != '\0' ) {
761 			    DEBUG(7, "Protocol string %s ", tok);
762 			    DEBUG(7, "overrides %s\n", _ProtoCfg);
763 		        }
764 		        strcpy(_ProtoCfg, tok);
765 		    }
766 	        } else {
767 		    DEBUG(7, "Unknown configuration parameter %s\n", tok);
768 	        }
769 	    }
770 	}
771     }
772 }
773