nl7c.c revision 8348:4137e18bfaf0
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 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*
27 * NL7C (Network Layer 7 Cache) as part of SOCKFS provides an in-kernel
28 * gateway cache for the request/response message based L7 protocol HTTP
29 * (Hypertext Transfer Protocol, see HTTP/1.1 RFC2616) in a semantically
30 * transparent manner.
31 *
32 * Neither the requesting user agent (client, e.g. web browser) nor the
33 * origin server (e.g. webserver) that provided the response cached by
34 * NL7C are impacted in any way.
35 *
36 * Note, currently NL7C only processes HTTP messages via the embedded
37 * URI of scheme http (not https nor any other), additional scheme are
38 * intended to be supported as is practical such that much of the NL7C
39 * framework may appear more general purpose then would be needed just
40 * for an HTTP gateway cache.
41 *
42 * NL7C replaces NCA (Network Cache and Accelerator) and in the future
43 * NCAS (NCA/SSL).
44 *
45 * Further, NL7C uses all NCA configuration files, see "/etc/nca/", the
46 * NCA socket API, "AF_NCA", and "ndd /dev/nca" for backwards compatibility.
47 */
48
49#include <sys/systm.h>
50#include <sys/strsun.h>
51#include <sys/strsubr.h>
52#include <inet/common.h>
53#include <inet/led.h>
54#include <inet/mi.h>
55#include <netinet/in.h>
56#include <fs/sockfs/nl7c.h>
57#include <fs/sockfs/nl7curi.h>
58#include <fs/sockfs/socktpi.h>
59
60#include <inet/nca/ncadoorhdr.h>
61#include <inet/nca/ncalogd.h>
62#include <inet/nca/ncandd.h>
63
64#include <sys/promif.h>
65
66/*
67 * NL7C, NCA, NL7C logger enabled:
68 */
69
70boolean_t	nl7c_enabled = B_FALSE;
71
72boolean_t	nl7c_logd_enabled = B_FALSE;
73boolean_t	nl7c_logd_started = B_FALSE;
74boolean_t	nl7c_logd_cycle = B_TRUE;
75
76/*
77 * Some externs:
78 */
79
80extern int	inet_pton(int, char *, void *);
81
82extern void	nl7c_uri_init(void);
83extern boolean_t nl7c_logd_init(int, caddr_t *);
84extern void	nl7c_nca_init(void);
85
86/*
87 * nl7c_addr_t - a singly linked grounded list, pointed to by *nl7caddrs,
88 * constructed at init time by parsing "/etc/nca/ncaport.conf".
89 *
90 * This list is searched at bind(3SOCKET) time when an application doesn't
91 * explicitly set AF_NCA but instead uses AF_INET, if a match is found then
92 * the underlying socket is marked sti_nl7c_flags NL7C_ENABLED.
93 */
94
95typedef struct nl7c_addr_s {
96	struct nl7c_addr_s *next;	/* next entry */
97	sa_family_t	family;		/* addr type, only INET and INET6 */
98	uint16_t	port;		/* port */
99	union {
100		ipaddr_t	v4;	/* IPv4 address */
101		in6_addr_t	v6;	/* IPv6 address */
102		void		*align;	/* foce alignment */
103	}		addr;		/* address */
104
105	struct sonode	*listener;	/* listen()er's sonode */
106	boolean_t	temp;		/* temporary addr via add_addr() ? */
107} nl7c_addr_t;
108
109nl7c_addr_t	*nl7caddrs = NULL;
110
111/*
112 * Called for an NL7C_ENABLED listen()er socket for the nl7c_addr_t
113 * previously returned by nl7c_lookup_addr().
114 */
115
116void
117nl7c_listener_addr(void *arg, struct sonode *so)
118{
119	nl7c_addr_t		*p = (nl7c_addr_t *)arg;
120
121	if (p->listener == NULL)
122		p->listener = so;
123	SOTOTPI(so)->sti_nl7c_addr = arg;
124}
125
126struct sonode *
127nl7c_addr2portso(void *arg)
128{
129	nl7c_addr_t		*p = (nl7c_addr_t *)arg;
130
131	return (p->listener);
132}
133
134void *
135nl7c_lookup_addr(void *addr, t_uscalar_t addrlen)
136{
137	struct sockaddr		*sap = addr;
138	struct sockaddr_in	*v4p = addr;
139	nl7c_addr_t		*p = nl7caddrs;
140
141	if (sap->sa_family != AF_INET || addrlen != sizeof (*v4p)) {
142		/* Only support IPv4 */
143		return (B_FALSE);
144	}
145	while (p) {
146		if (sap->sa_family == p->family &&
147		    v4p->sin_port == p->port &&
148		    (v4p->sin_addr.s_addr == p->addr.v4 ||
149		    p->addr.v4 == INADDR_ANY)) {
150			/* Match */
151			return (p);
152		}
153		p = p->next;
154	}
155	return (NULL);
156}
157
158void *
159nl7c_add_addr(void *addr, t_uscalar_t addrlen)
160{
161	struct sockaddr		*sap = addr;
162	struct sockaddr_in	*v4p = addr;
163	nl7c_addr_t		*new = NULL;
164	nl7c_addr_t		*old;
165	nl7c_addr_t		*p;
166	boolean_t		alloced;
167
168	if (sap->sa_family != AF_INET || addrlen != sizeof (*v4p)) {
169		/* Only support IPv4 */
170		return (NULL);
171	}
172again:
173	p = nl7caddrs;
174	while (p) {
175		if (new == NULL && p->port == 0)
176			new = p;
177		if (sap->sa_family == p->family &&
178		    v4p->sin_port == p->port &&
179		    (v4p->sin_addr.s_addr == p->addr.v4 ||
180		    p->addr.v4 == INADDR_ANY)) {
181			/* Match */
182			return (p);
183		}
184		p = p->next;
185	}
186	if (new == NULL) {
187		new = kmem_zalloc(sizeof (*new), KM_SLEEP);
188		alloced = B_TRUE;
189	} else
190		alloced = B_FALSE;
191
192	new->family = sap->sa_family;
193	new->port = v4p->sin_port;
194	new->addr.v4 = v4p->sin_addr.s_addr;
195	new->temp = B_TRUE;
196
197	if (alloced) {
198		old = nl7caddrs;
199		new->next = old;
200		if (atomic_cas_ptr(&nl7caddrs, old, new) != old) {
201			kmem_free(new, sizeof (*new));
202			goto again;
203		}
204	}
205
206	return (new);
207}
208
209boolean_t
210nl7c_close_addr(struct sonode *so)
211{
212	nl7c_addr_t	*p = nl7caddrs;
213
214	while (p) {
215		if (p->listener == so) {
216			if (p->temp)
217				p->port = (uint16_t)-1;
218			p->listener = NULL;
219			return (B_TRUE);
220		}
221		p = p->next;
222	}
223	return (B_FALSE);
224}
225
226static void
227nl7c_addr_add(nl7c_addr_t *p)
228{
229	p->next = nl7caddrs;
230	nl7caddrs = p;
231}
232
233void
234nl7c_mi_report_addr(mblk_t *mp)
235{
236	ipaddr_t	ip;
237	uint16_t	port;
238	nl7c_addr_t	*p = nl7caddrs;
239	struct sonode	*so;
240	char		addr[32];
241
242	(void) mi_mpprintf(mp, "Door  Up-Call-Queue IPaddr:TCPport Listenning");
243	while (p) {
244		if (p->port != (uint16_t)-1) {
245			/* Don't report freed slots */
246			ip = ntohl(p->addr.v4);
247			port = ntohs(p->port);
248
249			if (ip == INADDR_ANY) {
250				(void) strcpy(addr, "*");
251			} else {
252				int a1 = (ip >> 24) & 0xFF;
253				int a2 = (ip >> 16) & 0xFF;
254				int a3 = (ip >> 8) & 0xFF;
255				int a4 = ip & 0xFF;
256
257				(void) mi_sprintf(addr, "%d.%d.%d.%d",
258				    a1, a2, a3, a4);
259			}
260			so = p->listener;
261			(void) mi_mpprintf(mp, "%p  %s:%d  %d",
262			    so ? (void *)strvp2wq(SOTOV(so)) : NULL,
263			    addr, port, p->listener ? 1 : 0);
264		}
265		p = p->next;
266	}
267}
268
269/*
270 * ASCII to unsigned.
271 *
272 * Note, it's assumed that *p is a valid zero byte terminated string.
273 */
274
275static unsigned
276atou(const char *p)
277{
278	int c;
279	int v = 0;
280
281	/* Shift and add digit by digit */
282	while ((c = *p++) != NULL && isdigit(c)) {
283		v *= 10;
284		v += c - '0';
285	}
286	return (v);
287}
288
289/*
290 * strdup(), yet another strdup() in the kernel.
291 */
292
293static char *
294strdup(char *s)
295{
296	int	len = strlen(s) + 1;
297	char	*ret = kmem_alloc(len, KM_SLEEP);
298
299	bcopy(s, ret, len);
300
301	return (ret);
302}
303
304/*
305 * Inet ASCII to binary.
306 *
307 * Note, it's assumed that *s is a valid zero byte terminated string, and
308 * that *p is a zero initialized struct (this is important as the value of
309 * INADDR_ANY and IN6ADDR_ANY is zero).
310 */
311
312static int
313inet_atob(char *s, nl7c_addr_t *p)
314{
315	if (strcmp(s, "*") == 0) {
316		/* INADDR_ANY */
317		p->family = AF_INET;
318		return (0);
319	}
320	if (strcmp(s, "::") == 0) {
321		/* IN6ADDR_ANY */
322		p->family = AF_INET6;
323		return (0);
324	}
325	/* IPv4 address ? */
326	if (inet_pton(AF_INET, s, &p->addr.v4) != 1) {
327		/* Nop, IPv6 address ? */
328		if (inet_pton(AF_INET6, s, &p->addr.v6) != 1) {
329			/* Nop, return error */
330			return (1);
331		}
332		p->family = AF_INET6;
333	} else {
334		p->family = AF_INET;
335		p->addr.v4 = ntohl(p->addr.v4);
336	}
337	return (0);
338}
339
340/*
341 * Open and read each line from "/etc/nca/ncaport.conf", the syntax of a
342 * ncaport.conf file line is:
343 *
344 *	ncaport=IPaddr/Port[/Proxy]
345 *
346 * Where:
347 *
348 * ncaport - the only token recognized.
349 *
350 *  IPaddr - an IPv4 numeric dot address (e.g. 192.168.84.71) or '*' for
351 *           INADDR_ANY, or an IPv6 numeric address or "::" for IN6ADDR_ANY.
352 *
353 *       / - IPaddr/Port separator.
354 *
355 *    Port - a TCP decimal port number.
356 *
357 * Note, all other lines will be ignored.
358 */
359
360static void
361ncaportconf_read(void)
362{
363	int	ret;
364	struct vnode *vp;
365	char	c;
366	ssize_t resid;
367	char	buf[1024];
368	char	*ebp = &buf[sizeof (buf)];
369	char	*bp = ebp;
370	offset_t off = 0;
371	enum parse_e {START, TOK, ADDR, PORT, EOL} parse = START;
372	nl7c_addr_t *addrp = NULL;
373	char	*ncaport = "ncaport";
374	char	string[] = "XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX";
375	char	*stringp;
376	char	*tok;
377	char	*portconf = "/etc/nca/ncaport.conf";
378
379	ret = vn_open(portconf, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0);
380	if (ret == ENOENT) {
381		/* No portconf file, nothing to do */
382		return;
383	}
384	if (ret != 0) {
385		/* Error of some sort, tell'm about it */
386		cmn_err(CE_WARN, "%s: open error %d", portconf, ret);
387		return;
388	}
389	/*
390	 * Read portconf one buf[] at a time, parse one char at a time.
391	 */
392	for (;;) {
393		if (bp == ebp) {
394			/* Nothing left in buf[], read another */
395			ret = vn_rdwr(UIO_READ, vp, buf, sizeof (buf), off,
396			    UIO_SYSSPACE, 0, (rlim64_t)0, CRED(), &resid);
397			if (ret != 0) {
398				/* Error of some sort, tell'm about it */
399				cmn_err(CE_WARN, "%s: read error %d",
400				    portconf, ret);
401				break;
402			}
403			if (resid == sizeof (buf)) {
404				/* EOF, done */
405				break;
406			}
407			/* Initilize per buf[] state */
408			bp = buf;
409			ebp = &buf[sizeof (buf) - resid];
410			off += sizeof (buf) - resid;
411		}
412		c = *bp++;
413		switch (parse) {
414		case START:
415			/* Initilize all per file line state */
416			if (addrp == NULL) {
417				addrp = kmem_zalloc(sizeof (*addrp),
418				    KM_NOSLEEP);
419			}
420			tok = ncaport;
421			stringp = string;
422			parse = TOK;
423			/*FALLTHROUGH*/
424		case TOK:
425			if (c == '#') {
426				/* Comment through end of line */
427				parse = EOL;
428				break;
429			}
430			if (isalpha(c)) {
431				if (c != *tok++) {
432					/* Only know one token, skip */
433					parse = EOL;
434				}
435			} else if (c == '=') {
436				if (*tok != NULL) {
437					/* Only know one token, skip */
438					parse = EOL;
439					break;
440				}
441				parse = ADDR;
442			} else if (c == '\n') {
443				/* Found EOL, empty line, next line */
444				parse = START;
445			} else {
446				/* Unexpected char, skip */
447				parse = EOL;
448			}
449			break;
450
451		case ADDR:
452			if (c == '/') {
453				/* addr/port separator, end of addr */
454				*stringp = NULL;
455				if (inet_atob(string, addrp)) {
456					/* Bad addr, skip */
457					parse = EOL;
458				} else {
459					stringp = string;
460					parse = PORT;
461				}
462			} else {
463				/* Save char to string */
464				if (stringp ==
465				    &string[sizeof (string) - 1]) {
466					/* Would overflow, skip */
467					parse = EOL;
468				} else {
469					/* Copy IP addr char */
470					*stringp++ = c;
471				}
472			}
473			break;
474
475		case PORT:
476			if (isdigit(c)) {
477				/* Save char to string */
478				if (stringp ==
479				    &string[sizeof (string) - 1]) {
480					/* Would overflow, skip */
481					parse = EOL;
482				} else {
483					/* Copy port digit char */
484					*stringp++ = c;
485				}
486				break;
487			} else if (c == '#' || isspace(c)) {
488				/* End of port number, convert */
489				*stringp = NULL;
490				addrp->port = ntohs(atou(string));
491
492				/* End of parse, add entry */
493				nl7c_addr_add(addrp);
494				addrp = NULL;
495				parse = EOL;
496			} else {
497				/* Unrecognized char, skip */
498				parse = EOL;
499				break;
500			}
501			if (c == '\n') {
502				/* Found EOL, start on next line */
503				parse = START;
504			}
505			break;
506
507		case EOL:
508			if (c == '\n') {
509				/* Found EOL, start on next line */
510				parse = START;
511			}
512			break;
513		}
514
515	}
516	if (addrp != NULL) {
517		kmem_free(addrp, sizeof (*addrp));
518	}
519	(void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL);
520	VN_RELE(vp);
521}
522
523/*
524 * Open and read each line from "/etc/nca/ncakmod.conf" and parse looking
525 * for the NCA enabled, the syntax is: status=enabled, all other lines will
526 * be ignored.
527 */
528
529static void
530ncakmodconf_read(void)
531{
532	int	ret;
533	struct vnode *vp;
534	char	c;
535	ssize_t resid;
536	char	buf[1024];
537	char	*ebp = &buf[sizeof (buf)];
538	char	*bp = ebp;
539	offset_t off = 0;
540	enum parse_e {START, TOK, EOL} parse = START;
541	char	*status = "status=enabled";
542	char	*tok;
543	char	*ncakmod = "/etc/nca/ncakmod.conf";
544
545	ret = vn_open(ncakmod, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0);
546	if (ret == ENOENT) {
547		/* No ncakmod file, nothing to do */
548		return;
549	}
550	if (ret != 0) {
551		/* Error of some sort, tell'm about it */
552		cmn_err(CE_WARN, "%s: open error %d", status, ret);
553		return;
554	}
555	/*
556	 * Read ncakmod one buf[] at a time, parse one char at a time.
557	 */
558	for (;;) {
559		if (bp == ebp) {
560			/* Nothing left in buf[], read another */
561			ret = vn_rdwr(UIO_READ, vp, buf, sizeof (buf), off,
562			    UIO_SYSSPACE, 0, (rlim64_t)0, CRED(), &resid);
563			if (ret != 0) {
564				/* Error of some sort, tell'm about it */
565				cmn_err(CE_WARN, "%s: read error %d",
566				    status, ret);
567				break;
568			}
569			if (resid == sizeof (buf)) {
570				/* EOF, done */
571				break;
572			}
573			/* Initilize per buf[] state */
574			bp = buf;
575			ebp = &buf[sizeof (buf) - resid];
576			off += sizeof (buf) - resid;
577		}
578		c = *bp++;
579		switch (parse) {
580		case START:
581			/* Initilize all per file line state */
582			tok = status;
583			parse = TOK;
584			/*FALLTHROUGH*/
585		case TOK:
586			if (c == '#') {
587				/* Comment through end of line */
588				parse = EOL;
589				break;
590			}
591			if (isalpha(c) || c == '=') {
592				if (c != *tok++) {
593					/* Only know one token, skip */
594					parse = EOL;
595				}
596			} else if (c == '\n') {
597				/*
598				 * Found EOL, if tok found done,
599				 * else start on next-line.
600				 */
601				if (*tok == NULL) {
602					nl7c_enabled = B_TRUE;
603					goto done;
604				}
605				parse = START;
606			} else {
607				/* Unexpected char, skip */
608				parse = EOL;
609			}
610			break;
611
612		case EOL:
613			if (c == '\n') {
614				/* Found EOL, start on next line */
615				parse = START;
616			}
617			break;
618		}
619
620	}
621done:
622	(void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL);
623	VN_RELE(vp);
624}
625
626/*
627 * Open and read each line from "/etc/nca/ncalogd.conf" and parse for
628 * the tokens and token text (i.e. key and value ncalogd.conf(4)):
629 *
630 *	status=enabled
631 *
632 *	logd_file_size=[0-9]+
633 *
634 *	logd_file_name=["]filename( filename)*["]
635 */
636
637static int	file_size = 1000000;
638static caddr_t	fnv[NCA_FIOV_SZ];
639
640static void
641ncalogdconf_read(void)
642{
643	int	ret;
644	struct vnode *vp;
645	char	c;
646	int	sz;
647	ssize_t resid;
648	char	buf[1024];
649	char	*ebp = &buf[sizeof (buf)];
650	char	*bp = ebp;
651	offset_t off = 0;
652	enum parse_e {START, TOK, TEXT, EOL} parse = START;
653	char	*tokstatus = "status\0enabled";
654	char	*toksize = "logd_file_size";
655	char	*tokfile = "logd_path_name";
656	char	*tokstatusp;
657	char	*toksizep;
658	char	*tokfilep;
659	char	*tok;
660	int	tokdelim = 0;
661	char	*ncalogd = "/etc/nca/ncalogd.conf";
662	char	*ncadeflog = "/var/nca/log";
663	char	file[TYPICALMAXPATHLEN] = {0};
664	char	*fp = file;
665	caddr_t	*fnvp = fnv;
666
667	ret = vn_open(ncalogd, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0);
668	if (ret == ENOENT) {
669		/* No ncalogd file, nothing to do */
670		return;
671	}
672	if (ret != 0) {
673		/* Error of some sort, tell'm about it */
674		cmn_err(CE_WARN, "ncalogdconf_read: %s: open error(%d).",
675		    ncalogd, ret);
676		return;
677	}
678	/*
679	 * Read ncalogd.conf one buf[] at a time, parse one char at a time.
680	 */
681	for (;;) {
682		if (bp == ebp) {
683			/* Nothing left in buf[], read another */
684			ret = vn_rdwr(UIO_READ, vp, buf, sizeof (buf), off,
685			    UIO_SYSSPACE, 0, (rlim64_t)0, CRED(), &resid);
686			if (ret != 0) {
687				/* Error of some sort, tell'm about it */
688				cmn_err(CE_WARN, "%s: read error %d",
689				    ncalogd, ret);
690				break;
691			}
692			if (resid == sizeof (buf)) {
693				/* EOF, done */
694				break;
695			}
696			/* Initilize per buf[] state */
697			bp = buf;
698			ebp = &buf[sizeof (buf) - resid];
699			off += sizeof (buf) - resid;
700		}
701		c = *bp++;
702		switch (parse) {
703		case START:
704			/* Initilize all per file line state */
705			tokstatusp = tokstatus;
706			toksizep = toksize;
707			tokfilep = tokfile;
708			tok = NULL;
709			parse = TOK;
710			sz = 0;
711			/*FALLTHROUGH*/
712		case TOK:
713			if (isalpha(c) || c == '_') {
714				/*
715				 * Found a valid tok char, if matches
716				 * any of the tokens continue else NULL
717				 * then string pointer.
718				 */
719				if (tokstatusp != NULL && c != *tokstatusp++)
720					tokstatusp = NULL;
721				if (toksizep != NULL && c != *toksizep++)
722					toksizep = NULL;
723				if (tokfilep != NULL && c != *tokfilep++)
724					tokfilep = NULL;
725
726				if (tokstatusp == NULL &&
727				    toksizep == NULL &&
728				    tokfilep == NULL) {
729					/*
730					 * All tok string pointers are NULL
731					 * so skip rest of line.
732					 */
733					parse = EOL;
734				}
735			} else if (c == '=') {
736				/*
737				 * Found tok separator, if tok found get
738				 * tok text, else skip rest of line.
739				 */
740				if (tokstatusp != NULL && *tokstatusp == NULL)
741					tok = tokstatus;
742				else if (toksizep != NULL && *toksizep == NULL)
743					tok = toksize;
744				else if (tokfilep != NULL && *tokfilep == NULL)
745					tok = tokfile;
746				if (tok != NULL)
747					parse = TEXT;
748				else
749					parse = EOL;
750			} else if (c == '\n') {
751				/* Found EOL, start on next line */
752				parse = START;
753			} else {
754				/* Comment or unknown char, skip rest of line */
755				parse = EOL;
756			}
757			break;
758		case TEXT:
759			if (c == '\n') {
760				/*
761				 * Found EOL, finish up tok text processing
762				 * (if any) and start on next line.
763				 */
764				if (tok == tokstatus) {
765					if (*++tokstatusp == NULL)
766						nl7c_logd_enabled = B_TRUE;
767				} else if (tok == toksize) {
768					file_size = sz;
769				} else if (tok == tokfile) {
770					if (tokdelim == 0) {
771						/* Non delimited path name */
772						*fnvp++ = strdup(file);
773					} else if (fp != file) {
774						/* No closing delimiter */
775						/*EMPTY*/;
776					}
777				}
778				parse = START;
779			} else if (tok == tokstatus) {
780				if (! isalpha(c) || *++tokstatusp == NULL ||
781				    c != *tokstatusp) {
782					/* Not enabled, skip line */
783					parse = EOL;
784				}
785			} else if (tok == toksize) {
786				if (isdigit(c)) {
787					sz *= 10;
788					sz += c - '0';
789				} else {
790					/* Not a decimal digit, skip line */
791					parse = EOL;
792				}
793			} else {
794				/* File name */
795				if (c == '"' && tokdelim++ == 0) {
796					/* Opening delimiter, skip */
797					/*EMPTY*/;
798				} else if (c == '"' || c == ' ') {
799					/* List delim or filename separator */
800					*fnvp++ = strdup(file);
801					fp = file;
802				} else if (fp < &file[sizeof (file) - 1]) {
803					/* Filename char */
804					*fp++ = c;
805				} else {
806					/* Filename to long, skip line */
807					parse = EOL;
808				}
809			}
810			break;
811
812		case EOL:
813			if (c == '\n') {
814				/* Found EOL, start on next line */
815				parse = START;
816			}
817			break;
818		}
819
820	}
821done:
822	(void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL);
823	VN_RELE(vp);
824
825	if (nl7c_logd_enabled) {
826		if (fnvp == fnv) {
827			/*
828			 * No logfile was specified and found so
829			 * so use defualt NCA log file path.
830			 */
831			*fnvp++ = strdup(ncadeflog);
832		}
833		if (fnvp < &fnv[NCA_FIOV_SZ]) {
834			/* NULL terminate list */
835			*fnvp = NULL;
836		}
837	}
838}
839
840void
841nl7clogd_startup(void)
842{
843	static kmutex_t startup;
844
845	/*
846	 * Called on the first log() attempt, have to wait until then to
847	 * initialize logd as at logdconf_read() the root fs is read-only.
848	 */
849	mutex_enter(&startup);
850	if (nl7c_logd_started) {
851		/* Lost the race, nothing todo */
852		mutex_exit(&startup);
853		return;
854	}
855	nl7c_logd_started = B_TRUE;
856	if (! nl7c_logd_init(file_size, fnv)) {
857		/* Failure, disable logging */
858		nl7c_logd_enabled = B_FALSE;
859		cmn_err(CE_WARN, "nl7clogd_startup: failed, disabling loggin");
860		mutex_exit(&startup);
861		return;
862	}
863	mutex_exit(&startup);
864}
865
866
867void
868nl7c_startup()
869{
870	/*
871	 * Open, read, and parse the NCA logd configuration file,
872	 * then initialize URI processing and NCA compat.
873	 */
874	ncalogdconf_read();
875	nl7c_uri_init();
876	nl7c_nca_init();
877}
878
879void
880nl7c_init()
881{
882	/* Open, read, and parse the NCA kmod configuration file */
883	ncakmodconf_read();
884
885	if (nl7c_enabled) {
886		/*
887		 * NL7C is enabled so open, read, and parse
888		 * the NCA address/port configuration file
889		 * and call startup() to finish config/init.
890		 */
891		ncaportconf_read();
892		nl7c_startup();
893	}
894}
895
896/*
897 * The main processing function called by accept() on a newly created
898 * socket prior to returning it to the caller of accept().
899 *
900 * Here data is read from the socket until a completed L7 request parse
901 * is completed. Data will be read in the context of the user thread
902 * which called accept(), when parse has been completed either B_TRUE
903 * or B_FALSE will be returned.
904 *
905 * If NL7C successfully process the L7 protocol request, i.e. generates
906 * a response, B_TRUE will be returned.
907 *
908 * Else, B_FALSE will be returned if NL7C can't process the request:
909 *
910 * 1) Couldn't locate a URI within the request.
911 *
912 * 2) URI scheme not reqcognized.
913 *
914 * 3) A request which can't be processed.
915 *
916 * 4) A request which could be processed but NL7C dosen't currently have
917 *    the response data. In which case NL7C will parse the returned response
918 *    from the application for possible caching for subsequent request(s).
919 */
920
921volatile uint64_t nl7c_proc_cnt = 0;
922volatile uint64_t nl7c_proc_error = 0;
923volatile uint64_t nl7c_proc_ETIME = 0;
924volatile uint64_t nl7c_proc_again = 0;
925volatile uint64_t nl7c_proc_next = 0;
926volatile uint64_t nl7c_proc_rcv = 0;
927volatile uint64_t nl7c_proc_noLRI = 0;
928volatile uint64_t nl7c_proc_nodata = 0;
929volatile uint64_t nl7c_proc_parse = 0;
930
931boolean_t
932nl7c_process(struct sonode *so, boolean_t nonblocking)
933{
934	vnode_t	*vp = SOTOV(so);
935	sotpi_info_t *sti = SOTOTPI(so);
936	mblk_t	*rmp = sti->sti_nl7c_rcv_mp;
937	clock_t	timout;
938	rval_t	rval;
939	uchar_t pri;
940	int 	pflag;
941	int	error;
942	boolean_t more;
943	boolean_t ret = B_FALSE;
944	boolean_t first = B_TRUE;
945	boolean_t pollin = (sti->sti_nl7c_flags & NL7C_POLLIN);
946
947	nl7c_proc_cnt++;
948
949	/* Caller has so_lock enter()ed */
950	error = so_lock_read_intr(so, nonblocking ? FNDELAY|FNONBLOCK : 0);
951	if (error) {
952		/* Couldn't read lock, pass on this socket */
953		sti->sti_nl7c_flags = 0;
954		nl7c_proc_noLRI++;
955		return (B_FALSE);
956	}
957	/* Exit so_lock for now, will be reenter()ed prior to return */
958	mutex_exit(&so->so_lock);
959
960	if (pollin)
961		sti->sti_nl7c_flags &= ~NL7C_POLLIN;
962
963	/* Initialize some kstrgetmsg() constants */
964	pflag = MSG_ANY | MSG_DELAYERROR;
965	pri = 0;
966	if (nonblocking) {
967		/* Non blocking so don't block */
968		timout = 0;
969	} else if (sti->sti_nl7c_flags & NL7C_SOPERSIST) {
970		/* 2nd or more time(s) here so use keep-alive value */
971		timout = nca_http_keep_alive_timeout;
972	} else {
973		/* 1st time here so use connection value */
974		timout = nca_http_timeout;
975	}
976
977	rval.r_vals = 0;
978	do {
979		/*
980		 * First time through, if no data left over from a previous
981		 * kstrgetmsg() then try to get some, else just process it.
982		 *
983		 * Thereafter, rmp = NULL after the successful kstrgetmsg()
984		 * so try to get some new data and append to list (i.e. until
985		 * enough fragments are collected for a successful parse).
986		 */
987		if (rmp == NULL) {
988
989			error = kstrgetmsg(vp, &rmp, NULL, &pri, &pflag,
990			    timout, &rval);
991			if (error) {
992				if (error == ETIME) {
993					/* Timeout */
994					nl7c_proc_ETIME++;
995				} else if (error != EWOULDBLOCK) {
996					/* Error of some sort */
997					nl7c_proc_error++;
998					rval.r_v.r_v2 = error;
999					sti->sti_nl7c_flags = 0;
1000					break;
1001				}
1002				error = 0;
1003			}
1004			if (rmp != NULL) {
1005				mblk_t	*mp = sti->sti_nl7c_rcv_mp;
1006
1007
1008				if (mp == NULL) {
1009					/* Just new data, common case */
1010					sti->sti_nl7c_rcv_mp = rmp;
1011				} else {
1012					/* Add new data to tail */
1013					while (mp->b_cont != NULL)
1014						mp = mp->b_cont;
1015					mp->b_cont = rmp;
1016				}
1017			}
1018			if (sti->sti_nl7c_rcv_mp == NULL) {
1019				/* No data */
1020				nl7c_proc_nodata++;
1021				if (timout > 0 || (first && pollin)) {
1022					/* Expected data so EOF */
1023					ret = B_TRUE;
1024				} else if (sti->sti_nl7c_flags &
1025				    NL7C_SOPERSIST) {
1026					/* Persistent so just checking */
1027					ret = B_FALSE;
1028				}
1029				break;
1030			}
1031			rmp = NULL;
1032		}
1033		first = B_FALSE;
1034	again:
1035		nl7c_proc_parse++;
1036
1037		more = nl7c_parse(so, nonblocking, &ret);
1038
1039		if (ret == B_TRUE && (sti->sti_nl7c_flags & NL7C_SOPERSIST)) {
1040			/*
1041			 * Parse complete, cache hit, response on its way,
1042			 * socket is persistent so try to process the next
1043			 * request.
1044			 */
1045			if (nonblocking) {
1046				ret = B_FALSE;
1047				break;
1048			}
1049			if (sti->sti_nl7c_rcv_mp) {
1050				/* More recv-side data, pipelined */
1051				nl7c_proc_again++;
1052				goto again;
1053			}
1054			nl7c_proc_next++;
1055			if (nonblocking)
1056				timout = 0;
1057			else
1058				timout = nca_http_keep_alive_timeout;
1059
1060			more = B_TRUE;
1061		}
1062
1063	} while (more);
1064
1065	if (sti->sti_nl7c_rcv_mp) {
1066		nl7c_proc_rcv++;
1067	}
1068	sti->sti_nl7c_rcv_rval = rval.r_vals;
1069	/* Renter so_lock, caller called with it enter()ed */
1070	mutex_enter(&so->so_lock);
1071	so_unlock_read(so);
1072
1073	return (ret);
1074}
1075