4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
24 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
28 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
29 /* All Rights Reserved */
33 * data base routines for the network listener process
36 /* system include files */
42 #include <sys/param.h>
43 #include <sys/types.h>
44 #include <sys/tiuser.h>
45 #include <sys/stropts.h>
47 /* listener include files */
49 #include "lsparam.h" /* listener parameters */
50 #include "listen.h" /* listener includes */
51 #include "lsfiles.h" /* listener files info */
52 #include "lserror.h" /* listener error codes */
53 #include "lsdbf.h" /* data base file stuff */
54 /* #include "nsaddr.h" nls includes */
56 #define SUPPRESS 1 /* suppress messages during scan*/
57 #define NOSUPPRESS 0 /* don't suppress messages */
61 static char *dbfopenmsg
= "Trouble opening data base file";
62 static char *dbfrderror
= "Error reading data base file: line %d";
63 static char *dbfbadlmsg
= "Data base file: Error on line %d";
64 static char *dbfdupcmsg
= "Data base file: Duplicate service code: <%s>";
65 static char *dbfunknown
= "Unknown error reading data base file: line %d";
66 static char *dbfsvccmsg
= "Data base file: Illegal service code: <%s>";
67 static char *dbfcorrupt
= "Data base file has been corrupted";
69 static int Dbflineno
; /* current line number in dbf */
70 static unsigned Dbfentries
; /* number of non-comment lines */
71 extern char *Server_cmd_lines
; /* contains svc_code, cmd_line, mod_list */
72 extern char *New_cmd_lines
; /* svc_code, cmd_line, mod_list (on reread)*/
74 /* public variables */
80 * read the data base file into internal structures
82 * all data base routines under read_dbf log there own errors and return -1
83 * in case of an error.
85 * if 're_read' is non-zero, this stuff is being called to read a new
86 * data base file after the listener's initialization.
91 int re_read
; /* zero means first time */
93 register unsigned size
;
94 int exit_flag
= EXIT
| NOCORE
;
95 register dbf_t
*dbf_p
;
98 extern dbf_t
*Dbfhead
; /* Dbfentries (when allocated) */
99 extern dbf_t
*Newdbf
; /* Dbfentries (on re-read) */
100 extern char *calloc();
102 DEBUG((9,"in read_dbf"));
105 error(E_BADVER
, EXIT
| NOCORE
);
107 if (re_read
) { /* not first time */
108 exit_flag
= CONTINUE
;
112 * note: data base routines log their own error messages
116 DEBUG((9,"read_dbf: open file here: %s", DBFNAME
));
117 if ( (size
= scan_dbf(DBFNAME
)) == (unsigned)(-1) )
118 error( E_SCAN_DBF
, exit_flag
| NO_MSG
);
120 DEBUG((5,"read_dbf: scan complete: non-commented lines: %u, size: %u",
124 logmessage("No database? 0 entries?");
129 * allocate enough space for Dbfentries of 'size' bytes (total)
130 * The +1 is to insure a NULL last entry!
133 if (!(dbf_p
= (dbf_t
*)calloc(Dbfentries
+1,sizeof(dbf_t
)))
134 || !(cmd_p
= calloc(size
, 1))) {
135 DEBUG((1,"cannot calloc %u + %u bytes", size
,
136 (Dbfentries
+1)*(unsigned)sizeof(dbf_t
)));
137 error( E_DBF_ALLOC
, exit_flag
); /* if init, exit */
139 /* if still here, this is a re-read */
148 if (get_dbf(dbf_p
, cmd_p
)) {
149 DEBUG((9, "get_dbf FAILED"));
150 error(E_DBF_IO
, exit_flag
| NO_MSG
);
152 /* if still here, this is a re_read */
160 New_cmd_lines
= cmd_p
;
162 DEBUG((7,"read_dbf: NEW data base dump..."));
164 for (dbf_p
= Newdbf
; dbf_p
->dbf_svc_code
; ++dbf_p
)
165 DEBUG((7, "svc code <%s>; id: %s; private address: %s; modules: %s; cmd line: %s; sflags: %x, prognum: %d version: %d",
166 dbf_p
->dbf_svc_code
, dbf_p
->dbf_id
, dbf_p
->dbf_prv_adr
, dbf_p
->dbf_modules
, dbf_p
->dbf_cmd_line
, dbf_p
->dbf_sflags
, dbf_p
->dbf_prognum
, dbf_p
->dbf_version
));
167 #endif /* DEBUGMODE */
171 Server_cmd_lines
= cmd_p
;
173 DEBUG((7,"read_dbf: data base dump..."));
175 for (dbf_p
= Dbfhead
; dbf_p
->dbf_svc_code
; ++dbf_p
)
176 DEBUG((7, "svc code <%s>; id: %s; r1: %s; r2: %s; r3: %s; private address: %s; modules: %s; cmd line: %s; sflags: %x, prognum: %d version: %d",
177 dbf_p
->dbf_svc_code
, dbf_p
->dbf_id
, dbf_p
->dbf_res1
, dbf_p
->dbf_res2
, dbf_p
->dbf_res3
, dbf_p
->dbf_prv_adr
, dbf_p
->dbf_modules
, dbf_p
->dbf_cmd_line
, dbf_p
->dbf_sflags
, dbf_p
->dbf_prognum
, dbf_p
->dbf_version
));
178 #endif /* DEBUGMODE */
186 * get_dbf: read the file and fill the structures
187 * checking for duplicate entries as we go
191 get_dbf(dbf_p
, cmd_p
)
192 register dbf_t
*dbf_p
;
193 register char *cmd_p
;
195 dbf_t
*dbfhead
= dbf_p
;
198 register char *p
= buf
;
213 register dbf_t
*tdbf_p
;
215 extern int Dbf_entries
;
216 extern int NLPS_proc
;
220 Dbf_entries
= 0; /* number of private addresses in dbf file */
222 DEBUG((9,"in get_dbf: "));
223 if (!(dbfilep
= fopen(DBFNAME
,"r"))) {
224 logmessage(dbfopenmsg
);
225 error(E_DBF_IO
, EXIT
| NOCORE
| NO_MSG
);
228 while (n
= rd_dbf_line(dbfilep
,p
,&svc_code_p
,&flags
,&id_p
,&res1_p
,&res2_p
,&res3_p
,&private_p
,&prognum
,&vernum
,&module_p
,&sflags
,&cmd_line_p
,NOSUPPRESS
)) {
230 if (n
== -1) { /* read error */
235 /* make sure service code is legal */
237 i
= strlen(svc_code_p
);
238 if ( (i
== 0) || (i
>= SVC_CODE_SZ
) )
241 /* check for duplicate service code */
243 while (tdbf_p
->dbf_svc_code
) { /* duplicate svc code? */
244 if (!strcmp(svc_code_p
, tdbf_p
->dbf_svc_code
)) {
245 sprintf(scratch
, dbfdupcmsg
, svc_code_p
);
252 /* NLPS_proc is set by the nlps_server, which also uses these
253 * routines. The actual listener child shouldn't ever need
254 * to read a database, so it will never be here
256 if (!NLPS_proc
&& (strlen(private_p
) == 0) && !(sflags
& DFLAG
))
257 continue; /* ignore entries with no address */
260 * child doesn't care about private addresses
264 i
= strlen(private_p
);
265 if (i
>= PRV_ADR_SZ
) {
272 * legal, non-duplicate entry: copy it into internal data base
275 dbf_p
->dbf_fd
= -1; /* set to actual fd in add_prvaddr */
276 dbf_p
->dbf_flags
= flags
;
277 dbf_p
->dbf_sflags
= sflags
;
278 dbf_p
->dbf_prognum
= prognum
;
279 dbf_p
->dbf_version
= vernum
;
280 strcpy(cmd_p
, svc_code_p
);
281 dbf_p
->dbf_svc_code
= cmd_p
;
282 cmd_p
+= strlen(svc_code_p
) + 1; /* +1 for null */
283 strcpy(cmd_p
, cmd_line_p
);
284 dbf_p
->dbf_cmd_line
= cmd_p
;
285 cmd_p
+= strlen(cmd_line_p
) + 1;
287 dbf_p
->dbf_id
= cmd_p
;
288 cmd_p
+= strlen(id_p
) + 1;
289 strcpy(cmd_p
, res1_p
);
290 dbf_p
->dbf_res1
= cmd_p
;
291 cmd_p
+= strlen(res1_p
) + 1;
292 strcpy(cmd_p
, res2_p
);
293 dbf_p
->dbf_res2
= cmd_p
;
294 cmd_p
+= strlen(res2_p
) + 1;
295 strcpy(cmd_p
, res3_p
);
296 dbf_p
->dbf_res3
= cmd_p
;
297 cmd_p
+= strlen(res3_p
) + 1;
298 if (strlen(private_p
) != 0) {
299 strcpy(cmd_p
, private_p
);
300 dbf_p
->dbf_prv_adr
= cmd_p
;
301 cmd_p
+= strlen(private_p
) + 1;
304 dbf_p
->dbf_prv_adr
= NULL
;
305 strcpy(cmd_p
, module_p
);
306 dbf_p
->dbf_modules
= cmd_p
;
307 cmd_p
+= strlen(module_p
) + 1; /* cmd line + null char */
315 DEBUG((9, "svc_code <%s> failed validation test", svc_code_p
));
316 sprintf(scratch
, dbfsvccmsg
, svc_code_p
);
320 DEBUG((9,"private address <%s> failed validation test", private_p
));
321 sprintf(scratch
, "Invalid private address ignored: \\x%x", private_p
);
328 * scan_dbf: Take a quick pass through the data base file to figure out
329 * approx. how many items in the file we'll need to
330 * allocate storage for. Essentially, returns the number
331 * of non-null, non-comment lines in the data base file.
333 * return: -1 == error.
334 * other == number of non-comment characters.
342 register unsigned int size
= 0;
344 register FILE *dbfilep
;
346 register char *p
= buf
;
361 DEBUG((9, "In scan_dbf. Scanning data base file %s.", path
));
363 if (!(dbfilep
= fopen(path
,"r"))) {
364 DEBUG((9,"errorno = %d", errno
));
365 logmessage(dbfopenmsg
);
370 n
= rd_dbf_line(dbfilep
,p
,&svc_code_p
,&flags
,&id_p
,&res1_p
,&res2_p
,&res3_p
,&private_p
,&prognum
,&vernum
,&module_p
,&sflags
,&cmd_line_p
,SUPPRESS
);
386 * rd_dbf_line: Returns the next non-comment line into the
387 * given buffer (up to DBFLINESZ bytes).
388 * Skips 'ignore' lines.
390 * Returns: 0 = done, -1 = error,
391 * other = cmd line size incl. terminating null.
392 * *svc_code_p = service code;
393 * *id_p = user id string;
394 * *res1_p = reserved string;
395 * *res2_p = reserved string;
396 * *res3_p = reserved string;
397 * *private_p = contains private address;
398 * *flags_p = decoded flags;
399 * prognum = RPC program #;
400 * vernum = RPC version $;
401 * cnd_line_p points to null terminated cmd line;
403 * When logging errors, the extern Dbflineno is used.
407 rd_dbf_line(fp
, bp
, svc_code_p
, flags_p
, id_p
, res1_p
, res2_p
, res3_p
, private_p
, prognum
, vernum
, module_p
, sflags
, cmd_line_p
, mflag
)
431 if (!fgets(bp
, DBFLINESZ
, fp
)) {
436 sprintf(bp
,dbfrderror
,Dbflineno
);
440 sprintf(bp
,dbfunknown
,Dbflineno
);
442 return(-1); /* Unknown error (?) */
445 if (*(bp
+strlen(bp
)-1) != '\n') { /* terminating newline? */
446 sprintf(bp
, dbfbadlmsg
, Dbflineno
);
451 *(bp
+ strlen(bp
) -1) = '\0'; /* delete newline */
453 if (strlen(bp
) && (p
= strchr(bp
, DBFCOMMENT
)))
454 *p
= '\0'; /* delete comments */
458 p
= bp
+ strlen(bp
) - 1; /* bp->start; p->end */
459 while ((p
!= bp
) && (isspace(*p
))) {
460 *p
= '\0'; /* delete terminating spaces */
464 while (*bp
) /* del beginning white space*/
470 if (strlen(bp
)) { /* anything left? */
472 if (!(length
=scan_line(bp
,svc_code_p
,flags_p
,id_p
,res1_p
,res2_p
,res3_p
,private_p
,prognum
,vernum
,module_p
,sflags
,cmd_line_p
,mflag
))) {
474 DEBUG((1, "rd_dbf_line line %d, error while scanning entry",
476 sprintf(bp
, dbfbadlmsg
, Dbflineno
);
482 } while (!length
); /* until we have something */
484 DEBUG((5,"rd_dbf_line: line: %d,cmd line len: %d",Dbflineno
, length
+1));
486 return(length
+1); /* +1 for the trailing NULL */
492 * scan a non-white space line
494 * other = length of cmd line;
496 * non-null lines have the following format:
498 * service_code: flags: id: res1: res2: res3: private address: rpcdata: sflags: modules: cmd_line # comments
500 * mflag is set to suppress messages (scan_line is called both for parsing
501 * and counting, messages should only be output once)
505 scan_line(bp
, svc_code_p
, flags_p
, id_p
, res1_p
, res2_p
, res3_p
, private_p
, prognum
, vernum
, module_p
, sflags
, cmd_line_p
, mflag
)
508 register int *flags_p
;
518 register char **cmd_line_p
;
522 register char *nexttok
;
525 char scratch
[BUFSIZ
];
529 if (!(p
= strchr(bp
, DBFTOKSEP
))) { /* look for service code string */
530 DEBUG((9,"scan_line failed svc_code strchr"));
537 if (!(p
= strchr(nexttok
, DBFTOKSEP
))) {
538 DEBUG((9,"scan_line failed flags strchr"));
545 case 'x': /* service is turned off */
549 case 'u': /* create utmp entry */
550 *flags_p
|= DBF_UTMP
;
553 DEBUG((1,"scan_line: unknown flag char: 0x%x",*nexttok
));
554 *flags_p
= DBF_UNKNOWN
;
561 if (!(p
= strchr(nexttok
, DBFTOKSEP
))) {
562 DEBUG((9,"scan_line failed id strchr"));
569 if (!(p
= strchr(nexttok
, DBFTOKSEP
))) {
570 DEBUG((9,"scan_line failed res1 strchr"));
577 if (!(p
= strchr(nexttok
, DBFTOKSEP
))) {
578 DEBUG((9,"scan_line failed res2 strchr"));
585 if (!(p
= strchr(nexttok
, DBFTOKSEP
))) {
586 DEBUG((9,"scan_line failed res3 strchr"));
593 if (!(p
= strchr(nexttok
, DBFTOKSEP
))) {
594 DEBUG((9,"scan_line failed private strchr"));
598 *private_p
= nexttok
;
601 if (!(p
= strchr(nexttok
, DBFTOKSEP
))) {
602 DEBUG((9,"scan_line failed rpc strchr"));
610 /* there is rpc info */
611 for (ptr
= nexttok
; *ptr
; ++ptr
) {
612 if ((*ptr
== ',') && !sawsep
) {
614 * skip separator - note that if
615 * separator has been seen, it's not
616 * a digit so it will fail below
621 if (!isdigit(*ptr
)) {
622 sprintf(scratch
, "service code <%s> specifies non-integer rpc info", *svc_code_p
);
627 ptr
= strchr(nexttok
, ',');
629 if ((*prognum
= atoi(nexttok
)) < 0) {
631 /* messages aren't suppressed */
632 sprintf(scratch
, "service code <%s> specifies negative program number", *svc_code_p
);
637 if ((*vernum
= atoi(ptr
+ 1)) < 0) {
639 sprintf(scratch
, "service code <%s> specifies negative version number", *svc_code_p
);
647 sprintf(scratch
, "service code <%s> - invalid rpcinfo", *svc_code_p
);
655 if (!(p
= strchr(nexttok
, DBFTOKSEP
))) {
656 DEBUG((9,"scan_line failed sflags strchr"));
664 case 'c': /* dbf_cmd_line is a command */
667 case 'd': /* dynamic address */
668 if ((int) strlen(*private_p
) > 0) {
670 sprintf(scratch
, "service code <%s> specifies static and dynamic address", *svc_code_p
);
672 logmessage(" address info ignored");
674 /* don't set DFLAG and wipe out private addr */
681 case 'p': /* dbf_cmd_line is a pipe */
686 sprintf(scratch
, "service code <%s> unknown flag <%c> ignored", *svc_code_p
, *nexttok
);
695 if (!(p
= strchr(nexttok
, DBFTOKSEP
))) {
696 DEBUG((9,"scan_line failed module strchr"));
703 *cmd_line_p
= nexttok
;
705 DEBUG((9,"scan_line: modules: %s; line: %s; len: %d", *module_p
, *cmd_line_p
, strlen(*svc_code_p
)+strlen(*id_p
)+strlen(*res1_p
)+strlen(*res2_p
)+strlen(*res3_p
)+strlen(*private_p
)+strlen(*module_p
)+strlen(*cmd_line_p
)+9));
707 * return the length of the buffer. Add 9 for the NULLs after each
710 return(strlen(*svc_code_p
)+strlen(*id_p
)+strlen(*res1_p
)+strlen(*res2_p
)+strlen(*res3_p
)+strlen(*private_p
)+strlen(*module_p
)+strlen(*cmd_line_p
)+9);
715 #define VERSIONSTR "# VERSION="
721 char *line
, *p
, *tmp
;
724 if ((fp
= fopen(DBFNAME
, "r")) == NULL
) {
725 logmessage(dbfopenmsg
);
726 error(E_DBF_IO
, EXIT
| NOCORE
| NO_MSG
);
728 if ((line
= (char *) malloc(DBFLINESZ
)) == NULL
)
729 error(E_DBF_ALLOC
, EXIT
| NOCORE
);
731 while (fgets(p
, DBFLINESZ
, fp
)) {
732 if (!strncmp(p
, VERSIONSTR
, strlen(VERSIONSTR
))) {
733 /* pitch the newline */
734 tmp
= strchr(p
, '\n');
738 logmessage(dbfcorrupt
);
739 error(E_DBF_CORRUPT
, EXIT
| NOCORE
);
741 p
+= strlen(VERSIONSTR
);
745 logmessage(dbfcorrupt
);
746 error(E_DBF_CORRUPT
, EXIT
| NOCORE
);
750 if (version
!= VERSION
)
751 return(1); /* wrong version */
753 return(0); /* version ok */
757 logmessage(dbfcorrupt
);
758 error(E_DBF_CORRUPT
, EXIT
| NOCORE
);
764 * mkdbfargv: Given a pointer to a dbf_t, construct argv
765 * for an exec system call.
766 * Warning: returns a pointer to static data which are
767 * overritten by each call.
769 * There is a practical limit of 50 arguments (including argv[0])
771 * Warning: calling mkdbfargv destroys the data (by writing null
772 * characters via strtok) pointed to by dbp->dbf_cmd_line.
775 static char *dbfargv
[50];
776 static char *delim
= " \t'\""; /* delimiters */
782 register char **argvp
= dbfargv
;
783 register char *p
= dbp
->dbf_cmd_line
;
785 register char *savep
;
787 char scratch
[BUFSIZ
];
796 if (p
= strpbrk(p
, delim
)) {
803 DEBUG((9, "argv[%d] = %s", i
++, savep
));
804 /* zap trailing white space */
812 delch
= *p
; /* remember the delimiter */
816 * We work the string in place, embedded instances of the string delimiter,
817 * i.e. \" must have the '\' removed. Since we'd have to do a compare to
818 * decide if a copy were needed, it's less work to just do the copy, even
819 * though it is most likely unnecessary.
825 sprintf(scratch
, "invalid command line, non-terminated string for service code %s", dbp
->dbf_svc_code
);
827 exit(2); /* server, don't log */
830 if (*(tp
- 1) == '\\') { /* \delim */
834 else { /* end of string */
837 DEBUG((9, "argv[%d] = %s", i
++, savep
));
839 /* zap trailing white space */
852 logmessage("Internal error in parse routine");
853 exit(2); /* server, don't log */
858 DEBUG((9, "argv[%d] = %s", i
++, savep
));
869 * getentry: Given a fd, this routine will return a
870 * dbf entry. If the entry doesn't exist it will
878 extern dbf_t
*Dbfhead
; /* Dbfentries (when allocated) */
879 register dbf_t
*dbp
= Dbfhead
;
881 if (!Dbfhead
) { /* no private address in database file */
882 DEBUG((9, "getdbfentry - nothing in Dbfhead = %s ",Dbfhead
));
886 for (dbp
= Dbfhead
; dbp
->dbf_prv_adr
; ++dbp
)
887 if (fd
== dbp
->dbf_fd
) {
891 return((dbf_t
*)0); /* requested private address not in list */
897 * pushmod: push modules if defined in the data base entry.
899 * WARNING: This routine writes into the in-memory copy
900 * of the database file. Therefore, after it is called,
901 * the incore copy of the database file will no longer be valid.
910 register char *bufp
= mp
;
914 DEBUG((9,"in pushmod:"));
915 if (!mp
|| *mp
== '\0') {
916 DEBUG((9,"NULL list: exiting pushmod"));
919 /* pop timod if it is on the stack */
920 if (ioctl(fd
, I_LOOK
, name
) >= 0) {
921 if (strcmp(name
, "timod") == 0) {
922 if (ioctl(fd
, I_POP
, 0) < 0)
923 DEBUG((9,"pushmod: I_POP failed"));
926 while ((cp
= strtok(bufp
, ",")) != NULL
) {
928 DEBUG((9,"pushmod: about to push %s", cp
));
929 if (ioctl(fd
, I_PUSH
, cp
) < 0) {
930 DEBUG((9,"pushmod: ioctl failed, errno = %d",errno
));
934 DEBUG((9,"exiting pushmod:"));