1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate * CDDL HEADER START
3*0Sstevel@tonic-gate *
4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance
7*0Sstevel@tonic-gate * with the License.
8*0Sstevel@tonic-gate *
9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate * and limitations under the License.
13*0Sstevel@tonic-gate *
14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate *
20*0Sstevel@tonic-gate * CDDL HEADER END
21*0Sstevel@tonic-gate */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate * Copyright (c) 1999 by Sun Microsystems, Inc.
24*0Sstevel@tonic-gate * All rights reserved.
25*0Sstevel@tonic-gate */
26*0Sstevel@tonic-gate
27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
28*0Sstevel@tonic-gate
29*0Sstevel@tonic-gate #include <sys/types.h>
30*0Sstevel@tonic-gate #include <sys/stat.h>
31*0Sstevel@tonic-gate #include <errno.h>
32*0Sstevel@tonic-gate #include <syslog.h>
33*0Sstevel@tonic-gate #include <libintl.h>
34*0Sstevel@tonic-gate #include <unistd.h>
35*0Sstevel@tonic-gate #include <strings.h>
36*0Sstevel@tonic-gate #include <string.h>
37*0Sstevel@tonic-gate #include <assert.h>
38*0Sstevel@tonic-gate #include <stdlib.h>
39*0Sstevel@tonic-gate #include "nfslogd.h"
40*0Sstevel@tonic-gate #include "../lib/nfslogtab.h"
41*0Sstevel@tonic-gate #include "buffer_list.h"
42*0Sstevel@tonic-gate
43*0Sstevel@tonic-gate static int buildbuffer_list(struct buffer_ent **, timestruc_t *);
44*0Sstevel@tonic-gate static void free_buffer_ent(struct buffer_ent *);
45*0Sstevel@tonic-gate static struct buffer_ent *findbuffer(struct buffer_ent *, char *);
46*0Sstevel@tonic-gate static void free_sharepnt_list(struct sharepnt_ent *);
47*0Sstevel@tonic-gate static void free_sharepnt_ent(struct sharepnt_ent *);
48*0Sstevel@tonic-gate #ifdef DEBUG
49*0Sstevel@tonic-gate static void print_sharepnt_list(struct sharepnt_ent *);
50*0Sstevel@tonic-gate #endif
51*0Sstevel@tonic-gate static struct sharepnt_ent *findsharepnt(struct sharepnt_ent *, char *,
52*0Sstevel@tonic-gate struct sharepnt_ent **);
53*0Sstevel@tonic-gate
54*0Sstevel@tonic-gate /*
55*0Sstevel@tonic-gate * Builds the buffer list from NFSLOGTAB and returns it in *listpp.
56*0Sstevel@tonic-gate * Returns 0 on success, non-zero error code otherwise.
57*0Sstevel@tonic-gate */
58*0Sstevel@tonic-gate int
getbuffer_list(struct buffer_ent ** listpp,timestruc_t * lu)59*0Sstevel@tonic-gate getbuffer_list(struct buffer_ent **listpp, timestruc_t *lu)
60*0Sstevel@tonic-gate {
61*0Sstevel@tonic-gate *listpp = NULL;
62*0Sstevel@tonic-gate return (buildbuffer_list(listpp, lu));
63*0Sstevel@tonic-gate }
64*0Sstevel@tonic-gate
65*0Sstevel@tonic-gate /*
66*0Sstevel@tonic-gate * If NFSLOGTAB has not been modified since the last time we read it,
67*0Sstevel@tonic-gate * it simply returns the same buffer list, otherwise it re-reads NFSLOGTAB
68*0Sstevel@tonic-gate * and rebuilds the list.
69*0Sstevel@tonic-gate * No NFSLOGTAB is not treated as an error.
70*0Sstevel@tonic-gate * Returns 0 on success, non-zero error code otherwise
71*0Sstevel@tonic-gate */
72*0Sstevel@tonic-gate int
checkbuffer_list(struct buffer_ent ** listpp,timestruc_t * lu)73*0Sstevel@tonic-gate checkbuffer_list(struct buffer_ent **listpp, timestruc_t *lu)
74*0Sstevel@tonic-gate {
75*0Sstevel@tonic-gate struct stat st;
76*0Sstevel@tonic-gate int error = 0;
77*0Sstevel@tonic-gate
78*0Sstevel@tonic-gate if (stat(NFSLOGTAB, &st) == -1) {
79*0Sstevel@tonic-gate error = errno;
80*0Sstevel@tonic-gate if (error != ENOENT) {
81*0Sstevel@tonic-gate syslog(LOG_ERR, gettext("Can't stat %s - %s"),
82*0Sstevel@tonic-gate NFSLOGTAB, strerror(error));
83*0Sstevel@tonic-gate error = 0;
84*0Sstevel@tonic-gate }
85*0Sstevel@tonic-gate return (error);
86*0Sstevel@tonic-gate }
87*0Sstevel@tonic-gate
88*0Sstevel@tonic-gate if (lu->tv_sec == st.st_mtim.tv_sec &&
89*0Sstevel@tonic-gate lu->tv_nsec == st.st_mtim.tv_nsec)
90*0Sstevel@tonic-gate return (0);
91*0Sstevel@tonic-gate
92*0Sstevel@tonic-gate free_buffer_list(listpp); /* free existing list first */
93*0Sstevel@tonic-gate return (buildbuffer_list(listpp, lu));
94*0Sstevel@tonic-gate }
95*0Sstevel@tonic-gate
96*0Sstevel@tonic-gate /*
97*0Sstevel@tonic-gate * Does the actual work of reading NFSLOGTAB, and building the
98*0Sstevel@tonic-gate * buffer list. If *be_head already contains entries, it will
99*0Sstevel@tonic-gate * update the list with new information.
100*0Sstevel@tonic-gate * Returns 0 on success, non-zero error code otherwise.
101*0Sstevel@tonic-gate */
102*0Sstevel@tonic-gate static int
buildbuffer_list(struct buffer_ent ** be_head,timestruc_t * lu)103*0Sstevel@tonic-gate buildbuffer_list(struct buffer_ent **be_head, timestruc_t *lu)
104*0Sstevel@tonic-gate {
105*0Sstevel@tonic-gate FILE *fd;
106*0Sstevel@tonic-gate struct buffer_ent *be_tail = NULL, *bep;
107*0Sstevel@tonic-gate struct sharepnt_ent *se_tail = NULL, *sep;
108*0Sstevel@tonic-gate struct logtab_ent *lep;
109*0Sstevel@tonic-gate struct stat st;
110*0Sstevel@tonic-gate int error = 0, res;
111*0Sstevel@tonic-gate
112*0Sstevel@tonic-gate if ((fd = fopen(NFSLOGTAB, "r+")) == NULL) {
113*0Sstevel@tonic-gate error = errno;
114*0Sstevel@tonic-gate if (error != ENOENT) {
115*0Sstevel@tonic-gate syslog(LOG_ERR, gettext("%s - %s\n"), NFSLOGTAB,
116*0Sstevel@tonic-gate strerror(error));
117*0Sstevel@tonic-gate error = 0;
118*0Sstevel@tonic-gate }
119*0Sstevel@tonic-gate return (error);
120*0Sstevel@tonic-gate }
121*0Sstevel@tonic-gate
122*0Sstevel@tonic-gate if (lockf(fileno(fd), F_LOCK, 0L) < 0) {
123*0Sstevel@tonic-gate error = errno;
124*0Sstevel@tonic-gate syslog(LOG_ERR, gettext("cannot lock %s - %s\n"), NFSLOGTAB,
125*0Sstevel@tonic-gate strerror(error));
126*0Sstevel@tonic-gate (void) fclose(fd);
127*0Sstevel@tonic-gate return (error);
128*0Sstevel@tonic-gate }
129*0Sstevel@tonic-gate
130*0Sstevel@tonic-gate assert(*be_head == NULL);
131*0Sstevel@tonic-gate while ((res = logtab_getent(fd, &lep)) > 0) {
132*0Sstevel@tonic-gate if (bep = findbuffer(*be_head, lep->le_buffer)) {
133*0Sstevel@tonic-gate /*
134*0Sstevel@tonic-gate * Add sharepnt to buffer list
135*0Sstevel@tonic-gate */
136*0Sstevel@tonic-gate if (sep = findsharepnt(bep->be_sharepnt,
137*0Sstevel@tonic-gate lep->le_path, &se_tail)) {
138*0Sstevel@tonic-gate /*
139*0Sstevel@tonic-gate * Sharepoint already in list,
140*0Sstevel@tonic-gate * update its state.
141*0Sstevel@tonic-gate */
142*0Sstevel@tonic-gate sep->se_state = lep->le_state;
143*0Sstevel@tonic-gate } else {
144*0Sstevel@tonic-gate /*
145*0Sstevel@tonic-gate * Need to add to sharepoint list
146*0Sstevel@tonic-gate */
147*0Sstevel@tonic-gate sep = (struct sharepnt_ent *)
148*0Sstevel@tonic-gate malloc(sizeof (*sep));
149*0Sstevel@tonic-gate if (sep == NULL) {
150*0Sstevel@tonic-gate error = ENOMEM;
151*0Sstevel@tonic-gate goto errout;
152*0Sstevel@tonic-gate }
153*0Sstevel@tonic-gate (void) memset(sep, 0, sizeof (*sep));
154*0Sstevel@tonic-gate
155*0Sstevel@tonic-gate sep->se_name = strdup(lep->le_path);
156*0Sstevel@tonic-gate if (sep->se_name == NULL) {
157*0Sstevel@tonic-gate error = ENOMEM;
158*0Sstevel@tonic-gate goto errout;
159*0Sstevel@tonic-gate }
160*0Sstevel@tonic-gate sep->se_state = lep->le_state;
161*0Sstevel@tonic-gate
162*0Sstevel@tonic-gate assert(se_tail != NULL);
163*0Sstevel@tonic-gate assert(se_tail->se_next == NULL);
164*0Sstevel@tonic-gate se_tail->se_next = sep;
165*0Sstevel@tonic-gate }
166*0Sstevel@tonic-gate } else {
167*0Sstevel@tonic-gate /*
168*0Sstevel@tonic-gate * Add new buffer to list
169*0Sstevel@tonic-gate */
170*0Sstevel@tonic-gate bep = (struct buffer_ent *)malloc(sizeof (*bep));
171*0Sstevel@tonic-gate if (bep == NULL) {
172*0Sstevel@tonic-gate error = ENOMEM;
173*0Sstevel@tonic-gate goto errout;
174*0Sstevel@tonic-gate }
175*0Sstevel@tonic-gate (void) memset(bep, 0, sizeof (*bep));
176*0Sstevel@tonic-gate
177*0Sstevel@tonic-gate bep->be_name = strdup(lep->le_buffer);
178*0Sstevel@tonic-gate if (bep->be_name == NULL) {
179*0Sstevel@tonic-gate error = ENOMEM;
180*0Sstevel@tonic-gate goto errout;
181*0Sstevel@tonic-gate }
182*0Sstevel@tonic-gate
183*0Sstevel@tonic-gate if (*be_head == NULL)
184*0Sstevel@tonic-gate *be_head = bep;
185*0Sstevel@tonic-gate else
186*0Sstevel@tonic-gate be_tail->be_next = bep;
187*0Sstevel@tonic-gate be_tail = bep;
188*0Sstevel@tonic-gate
189*0Sstevel@tonic-gate bep->be_sharepnt = (struct sharepnt_ent *)
190*0Sstevel@tonic-gate malloc(sizeof (*(bep->be_sharepnt)));
191*0Sstevel@tonic-gate (void) memset(bep->be_sharepnt, 0,
192*0Sstevel@tonic-gate sizeof (*(bep->be_sharepnt)));
193*0Sstevel@tonic-gate
194*0Sstevel@tonic-gate if (bep->be_sharepnt == NULL) {
195*0Sstevel@tonic-gate error = ENOMEM;
196*0Sstevel@tonic-gate goto errout;
197*0Sstevel@tonic-gate }
198*0Sstevel@tonic-gate bep->be_sharepnt->se_name = strdup(lep->le_path);
199*0Sstevel@tonic-gate if (bep->be_sharepnt->se_name == NULL) {
200*0Sstevel@tonic-gate error = ENOMEM;
201*0Sstevel@tonic-gate goto errout;
202*0Sstevel@tonic-gate }
203*0Sstevel@tonic-gate bep->be_sharepnt->se_state = lep->le_state;
204*0Sstevel@tonic-gate }
205*0Sstevel@tonic-gate }
206*0Sstevel@tonic-gate
207*0Sstevel@tonic-gate if (res < 0) {
208*0Sstevel@tonic-gate error = EIO;
209*0Sstevel@tonic-gate goto errout;
210*0Sstevel@tonic-gate }
211*0Sstevel@tonic-gate
212*0Sstevel@tonic-gate /*
213*0Sstevel@tonic-gate * Get modification time while we have the file locked.
214*0Sstevel@tonic-gate */
215*0Sstevel@tonic-gate if (lu) {
216*0Sstevel@tonic-gate if ((error = fstat(fileno(fd), &st)) == -1) {
217*0Sstevel@tonic-gate syslog(LOG_ERR, gettext("Can't stat %s"), NFSLOGTAB);
218*0Sstevel@tonic-gate goto errout;
219*0Sstevel@tonic-gate }
220*0Sstevel@tonic-gate *lu = st.st_mtim;
221*0Sstevel@tonic-gate }
222*0Sstevel@tonic-gate
223*0Sstevel@tonic-gate (void) fclose(fd);
224*0Sstevel@tonic-gate return (error);
225*0Sstevel@tonic-gate
226*0Sstevel@tonic-gate errout:
227*0Sstevel@tonic-gate (void) fclose(fd);
228*0Sstevel@tonic-gate if (lep)
229*0Sstevel@tonic-gate logtab_ent_free(lep);
230*0Sstevel@tonic-gate free_buffer_list(be_head);
231*0Sstevel@tonic-gate assert(*be_head == NULL);
232*0Sstevel@tonic-gate syslog(LOG_ERR, gettext("cannot read %s: %s\n"), NFSLOGTAB,
233*0Sstevel@tonic-gate strerror(error));
234*0Sstevel@tonic-gate
235*0Sstevel@tonic-gate return (error);
236*0Sstevel@tonic-gate }
237*0Sstevel@tonic-gate
238*0Sstevel@tonic-gate /*
239*0Sstevel@tonic-gate * Removes the entry from the buffer list and frees it.
240*0Sstevel@tonic-gate */
241*0Sstevel@tonic-gate void
remove_buffer_ent(struct buffer_ent ** be_listpp,struct buffer_ent * bep)242*0Sstevel@tonic-gate remove_buffer_ent(struct buffer_ent **be_listpp, struct buffer_ent *bep)
243*0Sstevel@tonic-gate {
244*0Sstevel@tonic-gate struct buffer_ent *p, *prev;
245*0Sstevel@tonic-gate
246*0Sstevel@tonic-gate for (p = prev = *be_listpp; p != NULL; p = p->be_next) {
247*0Sstevel@tonic-gate if (p == bep) {
248*0Sstevel@tonic-gate if (p == *be_listpp)
249*0Sstevel@tonic-gate *be_listpp = (*be_listpp)->be_next;
250*0Sstevel@tonic-gate else
251*0Sstevel@tonic-gate prev->be_next = bep->be_next;
252*0Sstevel@tonic-gate free_buffer_ent(bep);
253*0Sstevel@tonic-gate break;
254*0Sstevel@tonic-gate }
255*0Sstevel@tonic-gate prev = p;
256*0Sstevel@tonic-gate }
257*0Sstevel@tonic-gate }
258*0Sstevel@tonic-gate
259*0Sstevel@tonic-gate /*
260*0Sstevel@tonic-gate * Frees the buffer list.
261*0Sstevel@tonic-gate */
262*0Sstevel@tonic-gate void
free_buffer_list(struct buffer_ent ** be_listpp)263*0Sstevel@tonic-gate free_buffer_list(struct buffer_ent **be_listpp)
264*0Sstevel@tonic-gate {
265*0Sstevel@tonic-gate struct buffer_ent *bep, *nextp;
266*0Sstevel@tonic-gate
267*0Sstevel@tonic-gate for (bep = *be_listpp; bep != NULL; bep = nextp) {
268*0Sstevel@tonic-gate nextp = bep->be_next;
269*0Sstevel@tonic-gate free_buffer_ent(bep);
270*0Sstevel@tonic-gate }
271*0Sstevel@tonic-gate *be_listpp = NULL;
272*0Sstevel@tonic-gate }
273*0Sstevel@tonic-gate
274*0Sstevel@tonic-gate static void
free_buffer_ent(struct buffer_ent * bep)275*0Sstevel@tonic-gate free_buffer_ent(struct buffer_ent *bep)
276*0Sstevel@tonic-gate {
277*0Sstevel@tonic-gate assert(bep != NULL);
278*0Sstevel@tonic-gate if (debug)
279*0Sstevel@tonic-gate (void) printf("freeing %s\n", bep->be_name);
280*0Sstevel@tonic-gate if (bep->be_name != NULL)
281*0Sstevel@tonic-gate free(bep->be_name);
282*0Sstevel@tonic-gate if (bep->be_sharepnt != NULL)
283*0Sstevel@tonic-gate free_sharepnt_list(bep->be_sharepnt);
284*0Sstevel@tonic-gate free(bep);
285*0Sstevel@tonic-gate }
286*0Sstevel@tonic-gate
287*0Sstevel@tonic-gate static void
free_sharepnt_list(struct sharepnt_ent * sep_listp)288*0Sstevel@tonic-gate free_sharepnt_list(struct sharepnt_ent *sep_listp)
289*0Sstevel@tonic-gate {
290*0Sstevel@tonic-gate struct sharepnt_ent *nextp;
291*0Sstevel@tonic-gate
292*0Sstevel@tonic-gate for (; sep_listp != NULL; sep_listp = nextp) {
293*0Sstevel@tonic-gate nextp = sep_listp->se_next;
294*0Sstevel@tonic-gate free_sharepnt_ent(sep_listp);
295*0Sstevel@tonic-gate }
296*0Sstevel@tonic-gate free(sep_listp);
297*0Sstevel@tonic-gate }
298*0Sstevel@tonic-gate
299*0Sstevel@tonic-gate /*
300*0Sstevel@tonic-gate * Removes the entry from the sharepnt list and frees it.
301*0Sstevel@tonic-gate */
302*0Sstevel@tonic-gate void
remove_sharepnt_ent(struct sharepnt_ent ** se_listpp,struct sharepnt_ent * sep)303*0Sstevel@tonic-gate remove_sharepnt_ent(struct sharepnt_ent **se_listpp, struct sharepnt_ent *sep)
304*0Sstevel@tonic-gate {
305*0Sstevel@tonic-gate struct sharepnt_ent *p, *prev;
306*0Sstevel@tonic-gate
307*0Sstevel@tonic-gate for (p = prev = *se_listpp; p != NULL; p = p->se_next) {
308*0Sstevel@tonic-gate if (p == sep) {
309*0Sstevel@tonic-gate if (p == *se_listpp)
310*0Sstevel@tonic-gate *se_listpp = (*se_listpp)->se_next;
311*0Sstevel@tonic-gate else
312*0Sstevel@tonic-gate prev->se_next = sep->se_next;
313*0Sstevel@tonic-gate free_sharepnt_ent(sep);
314*0Sstevel@tonic-gate break;
315*0Sstevel@tonic-gate }
316*0Sstevel@tonic-gate prev = p;
317*0Sstevel@tonic-gate }
318*0Sstevel@tonic-gate }
319*0Sstevel@tonic-gate
320*0Sstevel@tonic-gate static void
free_sharepnt_ent(struct sharepnt_ent * sep)321*0Sstevel@tonic-gate free_sharepnt_ent(struct sharepnt_ent *sep)
322*0Sstevel@tonic-gate {
323*0Sstevel@tonic-gate assert(sep != NULL);
324*0Sstevel@tonic-gate if (debug)
325*0Sstevel@tonic-gate (void) printf("freeing %s\n", sep->se_name);
326*0Sstevel@tonic-gate if (sep->se_name != NULL)
327*0Sstevel@tonic-gate free(sep->se_name);
328*0Sstevel@tonic-gate free(sep);
329*0Sstevel@tonic-gate }
330*0Sstevel@tonic-gate
331*0Sstevel@tonic-gate #ifdef DEBUG
332*0Sstevel@tonic-gate void
printbuffer_list(struct buffer_ent * bep)333*0Sstevel@tonic-gate printbuffer_list(struct buffer_ent *bep)
334*0Sstevel@tonic-gate {
335*0Sstevel@tonic-gate for (; bep != NULL; bep = bep->be_next) {
336*0Sstevel@tonic-gate (void) printf("%s\n", bep->be_name);
337*0Sstevel@tonic-gate if (bep->be_sharepnt != NULL)
338*0Sstevel@tonic-gate print_sharepnt_list(bep->be_sharepnt);
339*0Sstevel@tonic-gate }
340*0Sstevel@tonic-gate }
341*0Sstevel@tonic-gate
342*0Sstevel@tonic-gate static void
print_sharepnt_list(struct sharepnt_ent * sep)343*0Sstevel@tonic-gate print_sharepnt_list(struct sharepnt_ent *sep)
344*0Sstevel@tonic-gate {
345*0Sstevel@tonic-gate for (; sep != NULL; sep = sep->se_next)
346*0Sstevel@tonic-gate (void) printf("\t(%d) %s\n", sep->se_state, sep->se_name);
347*0Sstevel@tonic-gate }
348*0Sstevel@tonic-gate #endif
349*0Sstevel@tonic-gate
350*0Sstevel@tonic-gate /*
351*0Sstevel@tonic-gate * Returns a pointer to the buffer matching 'name', NULL otherwise.
352*0Sstevel@tonic-gate */
353*0Sstevel@tonic-gate static struct buffer_ent *
findbuffer(struct buffer_ent * bep,char * name)354*0Sstevel@tonic-gate findbuffer(struct buffer_ent *bep, char *name)
355*0Sstevel@tonic-gate {
356*0Sstevel@tonic-gate for (; bep != NULL; bep = bep->be_next) {
357*0Sstevel@tonic-gate if (strcmp(bep->be_name, name) == 0)
358*0Sstevel@tonic-gate return (bep);
359*0Sstevel@tonic-gate }
360*0Sstevel@tonic-gate return (NULL);
361*0Sstevel@tonic-gate }
362*0Sstevel@tonic-gate
363*0Sstevel@tonic-gate /*
364*0Sstevel@tonic-gate * Returns a pointer the sharepoint entry matching 'name'.
365*0Sstevel@tonic-gate * Otherwise, it sets '*se_tail' to the last element of the list
366*0Sstevel@tonic-gate * to make insertion of new element easier, and returns NULL.
367*0Sstevel@tonic-gate */
368*0Sstevel@tonic-gate static struct sharepnt_ent *
findsharepnt(struct sharepnt_ent * sep,char * name,struct sharepnt_ent ** se_tail)369*0Sstevel@tonic-gate findsharepnt(
370*0Sstevel@tonic-gate struct sharepnt_ent *sep,
371*0Sstevel@tonic-gate char *name,
372*0Sstevel@tonic-gate struct sharepnt_ent **se_tail)
373*0Sstevel@tonic-gate {
374*0Sstevel@tonic-gate struct sharepnt_ent *tail;
375*0Sstevel@tonic-gate
376*0Sstevel@tonic-gate for (; sep != NULL; sep = sep->se_next) {
377*0Sstevel@tonic-gate if (strcmp(sep->se_name, name) == 0)
378*0Sstevel@tonic-gate return (sep);
379*0Sstevel@tonic-gate tail = sep;
380*0Sstevel@tonic-gate }
381*0Sstevel@tonic-gate *se_tail = tail;
382*0Sstevel@tonic-gate return (NULL);
383*0Sstevel@tonic-gate }
384