xref: /titanic_44/usr/src/cmd/fs.d/nfs/nfslog/nfslog_elf.c (revision 8eea8e29cc4374d1ee24c25a07f45af132db3499)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
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.
13  *
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]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * nfs log - read buffer file and print structs in user-readable form
31  */
32 
33 #define	_REENTRANT
34 
35 #include <ctype.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <stddef.h>
39 #include <string.h>
40 #include <strings.h>
41 #include <time.h>
42 #include <fcntl.h>
43 #include <unistd.h>
44 #include <signal.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <sys/param.h>
48 #include <sys/utsname.h>
49 #include <errno.h>
50 #include <time.h>
51 #include <limits.h>
52 #include <libintl.h>
53 #include <pwd.h>
54 #include <netdb.h>
55 #include <syslog.h>
56 #include <rpc/rpc.h>
57 #include <netconfig.h>
58 #include <netdir.h>
59 #include <nfs/nfs_sec.h>
60 #include <nfs/export.h>
61 #include <rpc/auth.h>
62 #include <rpc/svc.h>
63 #include <rpc/xdr.h>
64 #include <rpc/clnt.h>
65 #include <nfs/nfs.h>
66 #include <nfs/nfs_log.h>
67 #include "fhtab.h"
68 #include "nfslogd.h"
69 
70 static char	empty_name[4] = "-";
71 
72 static char ftype3_names[NF3FIFO + 1][20] = {
73 	"\"none\"", "\"file\"", "\"dir\"", "\"blk device\"",
74 	"\"chr device\"", "\"link\"", "\"socket\"", "\"fifo\""
75 };
76 
77 #define	NFSL_FTYPE3(ftype)						\
78 	((((ftype) >= 0) && ((ftype) <= NF3FIFO)) ?			\
79 	ftype3_names[ftype] : empty_name)
80 
81 static char createmode3_names[EXCLUSIVE + 1][20] = {
82 	"\"unchecked", "\"guarded\"", "\"exclusive\""
83 };
84 
85 #define	NFSL_CREATEMODE3(createmode)					\
86 	((((createmode) >= 0) && ((createmode) <= EXCLUSIVE)) ?		\
87 	createmode3_names[createmode] : empty_name)
88 
89 static char	auth_flavor_name[RPCSEC_GSS + 1][20] = {
90 	"\"auth_null\"", "\"auth_unix\"", "\"auth_short\"", "\"auth_des\"",
91 	"\"auth_kerb\"", "\"none\"", "\"rpcsec_gss\""
92 };
93 
94 #define	NFSL_AUTH_FLAVOR_PRINT(auth_flavor)				\
95 	(((auth_flavor) <= RPCSEC_GSS) ?				\
96 	auth_flavor_name[auth_flavor] : empty_name)
97 
98 #define	NFSL_ERR_CNT		31	/* Actual err numbers */
99 
100 /*
101  * Two arrays - one short ints containing err codes, the other the strings
102  * (merged codes for both v2 and v3
103  */
104 static char	nfsl_status_name[NFSL_ERR_CNT][30] = {
105 	"\"ok\"", "\"perm\"", "\"noent\"", "\"io\"",
106 	"\"nxio\"", "\"access\"", "\"exist\"", "\"xdev\"",
107 	"\"nodev\"", "\"notdir\"", "\"isdir\"", "\"inval\"",
108 	"\"fbig\"", "\"nospc\"", "\"rofs\"", "\"mlink\"",
109 	"\"notsupp\"", "\"nametoolong\"", "\"notempty\"", "\"dquot\"",
110 	"\"stale\"", "\"remote\"", "\"wflush\"", "\"badhandle\"",
111 	"\"not_sync\"", "\"bad_cookie\"", "\"notsupp\"", "\"toosmall\"",
112 	"\"serverfault\"", "\"badtype\"", "\"jukebox\"",
113 };
114 
115 static short	nfsl_status[NFSL_ERR_CNT] = {
116 	0, 1, 2, 5, 6, 13, 17, 18,
117 	19, 20, 21, 22, 27, 28, 30, 31,
118 	45, 63, 66, 69, 70, 71, 99, 10001,
119 	10002, 10003, 10004, 10005, 10006, 10007, 10008
120 };
121 
122 /* list of open elf files */
123 static struct nfsl_log_file	*elf_file_list = NULL;
124 
125 /* Imported functions */
126 extern void bcopy(const void *s1, void *s2, size_t n);
127 
128 /* Static functions */
129 static void nfsl_log_file_free(struct nfsl_log_file *elfrec);
130 static void nfsl_log_file_add(struct nfsl_log_file *elfrec,
131 	struct nfsl_log_file **elf_listp);
132 static struct nfsl_log_file *nfsl_log_file_find(struct nfsl_log_file *elfrec,
133 	struct nfsl_log_file *elf_list);
134 static struct nfsl_log_file *nfsl_log_file_del(struct nfsl_log_file *elfrec,
135 	struct nfsl_log_file **elf_listp);
136 
137 static char *nfsl_get_time(time_t tt);
138 static char *nfsl_get_date(time_t tt);
139 static char *nfsl_get_date_nq(time_t tt);
140 static int nfsl_write_elfbuf(struct nfsl_log_file *elfrec);
141 static void nfsl_ipaddr_print(struct nfsl_log_file *, struct netbuf *);
142 static void nfsl_elf_record_header_print(struct nfsl_log_file *,
143 		nfslog_record_header *, char *, char *,
144 		struct nfsl_proc_disp *, char *);
145 static void nfsl_elf_buffer_header_print(struct nfsl_log_file *,
146 		nfslog_buffer_header *);
147 static struct nfsl_proc_disp *nfsl_find_elf_dispatch(
148 		nfslog_request_record *, char **);
149 static void nfsl_elf_rpc_print(struct nfsl_log_file *,
150 		nfslog_request_record *, struct nfsl_proc_disp *,
151 		char *, char *, char *);
152 static void nfslog_size3_print(struct nfsl_log_file *, set_size3 *);
153 
154 static void nfslog_null_args(struct nfsl_log_file *, caddr_t *);
155 static void nfslog_null_res(struct nfsl_log_file *, caddr_t *);
156 
157 
158 /*
159  * NFS VERSION 2
160  */
161 
162 /* Functions for elf print of the arguments */
163 static void nfslog_fhandle_print(struct nfsl_log_file *, fhandle_t *);
164 static void nfslog_diropargs_print(struct nfsl_log_file *, nfslog_diropargs *);
165 static void nfslog_setattrargs_print(struct nfsl_log_file *,
166 	nfslog_setattrargs *);
167 static void nfslog_sattr_print(struct nfsl_log_file *,
168 	nfslog_sattr *);
169 static void nfslog_nfsreadargs_print(struct nfsl_log_file *,
170 	nfslog_nfsreadargs *);
171 static void nfslog_writeargs_print(struct nfsl_log_file *,
172 	nfslog_writeargs *);
173 static void nfslog_writeresult_print(struct nfsl_log_file *,
174 	nfslog_writeresult *, bool_t);
175 static void nfslog_creatargs_print(struct nfsl_log_file *,
176 	nfslog_createargs *);
177 static void nfslog_rddirargs_print(struct nfsl_log_file *, nfslog_rddirargs *);
178 static void nfslog_linkargs_print(struct nfsl_log_file *, nfslog_linkargs *);
179 static void nfslog_rnmargs_print(struct nfsl_log_file *, nfslog_rnmargs *);
180 static void nfslog_symlinkargs_print(struct nfsl_log_file *,
181 	nfslog_symlinkargs *);
182 
183 static void nfslog_sharefsargs_print(struct nfsl_log_file *,
184 	nfslog_sharefsargs *);
185 static void nfslog_getfhargs_print(struct nfsl_log_file *,
186 	nfslog_getfhargs *);
187 
188 /* Functions for elf print of the response */
189 static void nfslog_nfsstat_print(struct nfsl_log_file *, enum nfsstat *,
190 	bool_t);
191 static void nfslog_diropres_print(struct nfsl_log_file *, nfslog_diropres *,
192 	bool_t);
193 static void nfslog_rdlnres_print(struct nfsl_log_file *, nfslog_rdlnres *,
194 	bool_t);
195 static void nfslog_rdresult_print(struct nfsl_log_file *,
196 	nfslog_rdresult *, bool_t);
197 static void nfslog_rddirres_print(struct nfsl_log_file *, nfslog_rddirres *,
198 	bool_t);
199 
200 /*
201  * NFS VERSION 3
202  */
203 
204 /* Functions for elf print of the arguments */
205 static void nfslog_fh3_print(struct nfsl_log_file *, nfs_fh3 *);
206 static void nfslog_diropargs3_print(struct nfsl_log_file *,
207 	nfslog_diropargs3 *);
208 static void nfslog_SETATTR3args_print(struct nfsl_log_file *,
209 	nfslog_SETATTR3args *);
210 static void nfslog_READ3args_print(struct nfsl_log_file *, nfslog_READ3args *);
211 static void nfslog_WRITE3args_print(struct nfsl_log_file *,
212 	nfslog_WRITE3args *);
213 static void nfslog_CREATE3args_print(struct nfsl_log_file *,
214 	nfslog_CREATE3args *);
215 static void nfslog_MKDIR3args_print(struct nfsl_log_file *,
216 	nfslog_MKDIR3args *);
217 static void nfslog_SYMLINK3args_print(struct nfsl_log_file *,
218 	nfslog_SYMLINK3args *);
219 static void nfslog_MKNOD3args_print(struct nfsl_log_file *,
220 	nfslog_MKNOD3args *);
221 static void nfslog_REMOVE3args_print(struct nfsl_log_file *,
222 	nfslog_REMOVE3args *);
223 static void nfslog_RMDIR3args_print(struct nfsl_log_file *,
224 	nfslog_RMDIR3args *);
225 static void nfslog_RENAME3args_print(struct nfsl_log_file *,
226 	nfslog_RENAME3args *);
227 static void nfslog_LINK3args_print(struct nfsl_log_file *,
228 	nfslog_LINK3args *);
229 static void nfslog_COMMIT3args_print(struct nfsl_log_file *,
230 	nfslog_COMMIT3args *);
231 static void nfslog_READDIRPLUS3args_print(struct nfsl_log_file *,
232 	nfslog_READDIRPLUS3args *);
233 
234 /* Functions for elf print of the response */
235 static void nfslog_nfsstat3_print(struct nfsl_log_file *,
236 	nfsstat3 *, bool_t);
237 static void nfslog_LOOKUP3res_print(struct nfsl_log_file *,
238 	nfslog_LOOKUP3res *, bool_t);
239 static void nfslog_READLINK3res_print(struct nfsl_log_file *,
240 	nfslog_READLINK3res *, bool_t);
241 static void nfslog_READ3res_print(struct nfsl_log_file *,
242 	nfslog_READ3res *, bool_t);
243 static void nfslog_WRITE3res_print(struct nfsl_log_file *,
244 	nfslog_WRITE3res *, bool_t);
245 static void nfslog_CREATE3res_print(struct nfsl_log_file *,
246 	nfslog_CREATE3res *, bool_t);
247 static void nfslog_MKDIR3res_print(struct nfsl_log_file *,
248 	nfslog_MKDIR3res *, bool_t);
249 static void nfslog_SYMLINK3res_print(struct nfsl_log_file *,
250 	nfslog_SYMLINK3res *, bool_t);
251 static void nfslog_MKNOD3res_print(struct nfsl_log_file *,
252 	nfslog_MKNOD3res *, bool_t);
253 static void nfslog_READDIRPLUS3res_print(struct nfsl_log_file *,
254 	nfslog_READDIRPLUS3res *, bool_t);
255 
256 extern int debug;
257 static bool_t nfsl_print_fh = FALSE;		/* print file handles? */
258 
259 #define	DFLT_BUFFERSIZE		8192
260 #define	DFLT_OVFSIZE		3072	/* Maximum logged or buffered size */
261 
262 static char hostname[MAXHOSTNAMELEN];	/* name of host */
263 
264 
265 /*
266  * Define the actions taken per prog/vers/proc:
267  *
268  * In some cases, the nl types are the same as the nfs types and a simple
269  * bcopy should suffice. Rather that define tens of identical procedures,
270  * simply define these to bcopy. Similarly this takes care of different
271  * procs that use same parameter struct.
272  */
273 
274 static struct nfsl_proc_disp nfsl_elf_proc_v2[] = {
275 	/*
276 	 * NFS VERSION 2
277 	 */
278 
279 	/* RFS_NULL = 0 */
280 	{nfslog_null_args, nfslog_null_res, "\"null\""},
281 
282 	/* RFS_GETATTR = 1 */
283 	{nfslog_fhandle_print, nfslog_nfsstat_print, "\"getattr\""},
284 
285 	/* RFS_SETATTR = 2 */
286 	{nfslog_setattrargs_print, nfslog_nfsstat_print, "\"setattr\""},
287 
288 	/* RFS_ROOT = 3 *** NO LONGER SUPPORTED *** */
289 	{nfslog_null_args, nfslog_null_res, "\"root\""},
290 
291 	/* RFS_LOOKUP = 4 */
292 	{nfslog_diropargs_print, nfslog_diropres_print, "\"lookup\""},
293 
294 	/* RFS_READLINK = 5 */
295 	{nfslog_fhandle_print, nfslog_rdlnres_print, "\"readlink\""},
296 
297 	/* RFS_READ = 6 */
298 	{nfslog_nfsreadargs_print, nfslog_rdresult_print, "\"read\""},
299 
300 	/* RFS_WRITECACHE = 7 *** NO LONGER SUPPORTED *** */
301 	{nfslog_null_args, nfslog_null_res, "\"writecache\""},
302 
303 	/* RFS_WRITE = 8 */
304 	{nfslog_writeargs_print, nfslog_writeresult_print, "\"write\""},
305 
306 	/* RFS_CREATE = 9 */
307 	{nfslog_creatargs_print, nfslog_diropres_print, "\"create\""},
308 
309 	/* RFS_REMOVE = 10 */
310 	{nfslog_diropargs_print, nfslog_nfsstat_print, "\"remove\""},
311 
312 	/* RFS_RENAME = 11 */
313 	{nfslog_rnmargs_print, nfslog_nfsstat_print, "\"rename\""},
314 
315 	/* RFS_LINK = 12 */
316 	{nfslog_linkargs_print, nfslog_nfsstat_print, "\"link\""},
317 
318 	/* RFS_SYMLINK = 13 */
319 	{nfslog_symlinkargs_print, nfslog_nfsstat_print, "\"symlink\""},
320 
321 	/* RFS_MKDIR = 14 */
322 	{nfslog_creatargs_print, nfslog_diropres_print, "\"mkdir\""},
323 
324 	/* RFS_RMDIR = 15 */
325 	{nfslog_diropargs_print, nfslog_nfsstat_print, "\"rmdir\""},
326 
327 	/* RFS_READDIR = 16 */
328 	{nfslog_rddirargs_print, nfslog_rddirres_print, "\"readdir\""},
329 
330 	/* RFS_STATFS = 17 */
331 	{nfslog_fhandle_print, nfslog_nfsstat_print, "\"statfs\""},
332 };
333 
334 
335 /*
336  * NFS VERSION 3
337  */
338 
339 static struct nfsl_proc_disp nfsl_elf_proc_v3[] = {
340 
341 	/* NFSPROC3_NULL = 0 */
342 	{nfslog_null_args, nfslog_null_res, "\"null\""},
343 
344 	/* NFSPROC3_GETATTR = 1 */
345 	{nfslog_fh3_print, nfslog_nfsstat3_print, "\"getattr\""},
346 
347 	/* NFSPROC3_SETATTR = 2 */
348 	{nfslog_SETATTR3args_print, nfslog_nfsstat3_print, "\"setattr\""},
349 
350 	/* NFSPROC3_LOOKUP = 3 */
351 	{nfslog_diropargs3_print, nfslog_LOOKUP3res_print, "\"lookup\""},
352 
353 	/* NFSPROC3_ACCESS = 4 */
354 	{nfslog_fh3_print, nfslog_nfsstat3_print, "\"access\""},
355 
356 	/* NFSPROC3_READLINK = 5 */
357 	{nfslog_fh3_print, nfslog_READLINK3res_print, "\"readlink\""},
358 
359 	/* NFSPROC3_READ = 6 */
360 	{nfslog_READ3args_print, nfslog_READ3res_print, "\"read\""},
361 
362 	/* NFSPROC3_WRITE = 7 */
363 	{nfslog_WRITE3args_print, nfslog_WRITE3res_print, "\"write\""},
364 
365 	/* NFSPROC3_CREATE = 8 */
366 	{nfslog_CREATE3args_print, nfslog_CREATE3res_print, "\"create\""},
367 
368 	/* NFSPROC3_MKDIR = 9 */
369 	{nfslog_MKDIR3args_print, nfslog_MKDIR3res_print, "\"mkdir\""},
370 
371 	/* NFSPROC3_SYMLINK = 10 */
372 	{nfslog_SYMLINK3args_print, nfslog_SYMLINK3res_print, "\"symlink\""},
373 
374 	/* NFSPROC3_MKNOD = 11 */
375 	{nfslog_MKNOD3args_print, nfslog_MKNOD3res_print, "\"mknod\""},
376 
377 	/* NFSPROC3_REMOVE = 12 */
378 	{nfslog_REMOVE3args_print, nfslog_nfsstat3_print, "\"remove\""},
379 
380 	/* NFSPROC3_RMDIR = 13 */
381 	{nfslog_RMDIR3args_print, nfslog_nfsstat3_print, "\"rmdir\""},
382 
383 	/* NFSPROC3_RENAME = 14 */
384 	{nfslog_RENAME3args_print, nfslog_nfsstat3_print, "\"rename\""},
385 
386 	/* NFSPROC3_LINK = 15 */
387 	{nfslog_LINK3args_print, nfslog_nfsstat3_print, "\"link\""},
388 
389 	/* NFSPROC3_READDIR = 16 */
390 	{nfslog_fh3_print, nfslog_nfsstat3_print, "\"readdir\""},
391 
392 	/* NFSPROC3_READDIRPLUS = 17 */
393 	{nfslog_READDIRPLUS3args_print, nfslog_READDIRPLUS3res_print,
394 		"\"readdirplus\""},
395 
396 	/* NFSPROC3_FSSTAT = 18 */
397 	{nfslog_fh3_print, nfslog_nfsstat3_print, "\"fsstat\""},
398 
399 	/* NFSPROC3_FSINFO = 19 */
400 	{nfslog_fh3_print, nfslog_nfsstat3_print, "\"fsinfo\""},
401 
402 	/* NFSPROC3_PATHCONF = 20 */
403 	{nfslog_fh3_print, nfslog_nfsstat3_print, "\"pathconf\""},
404 
405 	/* NFSPROC3_COMMIT = 21 */
406 	{nfslog_COMMIT3args_print, nfslog_nfsstat3_print, "\"commit\""},
407 };
408 
409 /*
410  * NFSLOG VERSION 1
411  */
412 
413 static struct nfsl_proc_disp nfsl_log_elf_proc_v1[] = {
414 
415 	/* NFSLOG_NULL = 0 */
416 	{nfslog_null_args, nfslog_null_res, "\"null\""},
417 
418 	/* NFSLOG_SHARE = 1 */
419 	{nfslog_sharefsargs_print, nfslog_nfsstat_print, "\"log_share\""},
420 
421 	/* NFSLOG_UNSHARE = 2 */
422 	{nfslog_sharefsargs_print, nfslog_nfsstat_print, "\"log_unshare\""},
423 
424 	/* NFSLOG_LOOKUP = 3 */
425 	{nfslog_diropargs3_print, nfslog_LOOKUP3res_print, "\"lookup\""},
426 
427 	/* NFSLOG_GETFH = 4 */
428 	{nfslog_getfhargs_print, nfslog_nfsstat_print, "\"log_getfh\""},
429 };
430 
431 static struct nfsl_vers_disp nfsl_elf_vers_disptable[] = {
432 	{sizeof (nfsl_elf_proc_v2) / sizeof (nfsl_elf_proc_v2[0]),
433 	    nfsl_elf_proc_v2},
434 	{sizeof (nfsl_elf_proc_v3) / sizeof (nfsl_elf_proc_v3[0]),
435 	    nfsl_elf_proc_v3},
436 };
437 
438 static struct nfsl_vers_disp nfsl_log_elf_vers_disptable[] = {
439 	{sizeof (nfsl_log_elf_proc_v1) / sizeof (nfsl_log_elf_proc_v1[0]),
440 	    nfsl_log_elf_proc_v1},
441 };
442 
443 static struct nfsl_prog_disp nfsl_elf_dispatch_table[] = {
444 	{NFS_PROGRAM,
445 	    NFS_VERSMIN,
446 	    sizeof (nfsl_elf_vers_disptable) /
447 		sizeof (nfsl_elf_vers_disptable[0]),
448 	    nfsl_elf_vers_disptable, "nfs"},
449 	{NFSLOG_PROGRAM,
450 	    NFSLOG_VERSMIN,
451 	    sizeof (nfsl_log_elf_vers_disptable) /
452 		sizeof (nfsl_log_elf_vers_disptable[0]),
453 	    nfsl_log_elf_vers_disptable, "nfslog"},
454 };
455 
456 static int	nfsl_elf_dispatch_table_arglen =
457 			sizeof (nfsl_elf_dispatch_table) /
458 			sizeof (nfsl_elf_dispatch_table[0]);
459 
460 static char *
461 nfslog_get_status(short status)
462 {
463 	int	low, mid, high;
464 	short	errstat;
465 
466 	/* Usually status is 0... */
467 	if (status == 0)
468 		return (nfsl_status_name[0]);
469 
470 	low = 0;
471 	high = NFSL_ERR_CNT;
472 	mid = NFSL_ERR_CNT / 2;
473 	/* binary search for status string */
474 	while (((errstat = nfsl_status[mid]) != status) && (low < mid) &&
475 		(mid < high)) {
476 		if (errstat > status) {	/* search bottom half */
477 			high = mid;
478 		} else {		/* search upper half */
479 			low = mid;
480 		}
481 		mid = low + ((high - low) / 2);
482 	}
483 	if (errstat == status) {	/* found it */
484 		return (nfsl_status_name[mid]);
485 	}
486 	return (NULL);
487 }
488 
489 /* nfsl_get_time - return string with time formatted as hh:mm:ss */
490 static char *
491 nfsl_get_time(time_t tt)
492 {
493 	static char	timestr[20];
494 	static time_t	lasttime;
495 	struct tm	tmst;
496 
497 	if (tt == lasttime)
498 		return (timestr);
499 	if (localtime_r(&tt, &tmst) == NULL) {
500 		return (empty_name);
501 	}
502 	(void) sprintf(timestr, "%02d:%02d:%02d",
503 		tmst.tm_hour, tmst.tm_min, tmst.tm_sec);
504 	lasttime = tt;
505 	return (timestr);
506 }
507 
508 /* nfsl_get_date - return date string formatted as "yyyy-mm-dd hh:mm:ss" */
509 static char *
510 nfsl_get_date(time_t tt)
511 {
512 	static char	timestr[30];
513 	static time_t	lasttime;
514 	struct tm	tmst;
515 
516 	if (tt == lasttime)
517 		return (timestr);
518 	if (localtime_r(&tt, &tmst) == NULL) {
519 		return (empty_name);
520 	}
521 	(void) sprintf(timestr, "\"%04d-%02d-%02d %02d:%02d:%02d\"",
522 		tmst.tm_year + 1900, tmst.tm_mon + 1, tmst.tm_mday,
523 		tmst.tm_hour, tmst.tm_min, tmst.tm_sec);
524 	lasttime = tt;
525 	return (timestr);
526 }
527 
528 /*
529  * nfsl_get_date_nq - return date string formatted as yyyy-mm-dd hh:mm:ss
530  * (no quotes)
531  */
532 static char *
533 nfsl_get_date_nq(time_t tt)
534 {
535 	static char	timestr[30];
536 	static time_t	lasttime;
537 	struct tm	tmst;
538 
539 	if (tt == lasttime)
540 		return (timestr);
541 	if (localtime_r(&tt, &tmst) == NULL) {
542 		return (empty_name);
543 	}
544 	(void) sprintf(timestr, "%04d-%02d-%02d %02d:%02d:%02d",
545 		tmst.tm_year + 1900, tmst.tm_mon + 1, tmst.tm_mday,
546 		tmst.tm_hour, tmst.tm_min, tmst.tm_sec);
547 	return (timestr);
548 }
549 
550 /* write log buffer out to file */
551 static int
552 nfsl_write_elfbuf(struct nfsl_log_file *elfrec)
553 {
554 	int	rc;
555 	char	*elfbuf = elfrec->buf;
556 	int	elfbufoffset = elfrec->bufoffset;
557 
558 	if (debug > 1)
559 		(void) printf("nfsl_write_elfbuf: bufoffset %d\n",
560 			elfbufoffset);
561 	if (elfbufoffset <= 0)
562 		return (0);
563 	elfbuf[elfbufoffset] = '\0';
564 	if ((rc = fputs(elfbuf, elfrec->fp)) < 0) {
565 		syslog(LOG_ERR, gettext("Write to %s failed: %s\n"),
566 			elfrec->path, strerror(errno));
567 		return (-1);
568 	}
569 	if (rc != elfbufoffset) {
570 		syslog(LOG_ERR, gettext("Write %d bytes to %s returned %d\n"),
571 			elfbufoffset, elfrec->path, rc);
572 		return (-1);
573 	}
574 	elfrec->bufoffset = 0;
575 	return (0);
576 }
577 
578 /*ARGSUSED*/
579 static void
580 nfslog_null_args(struct nfsl_log_file *elfrec, caddr_t *nfsl_args)
581 {
582 }
583 
584 /*ARGSUSED*/
585 static void
586 nfslog_null_res(struct nfsl_log_file *elfrec, caddr_t *nfsl_res)
587 {
588 }
589 
590 static void
591 nfslog_fh3_print(struct nfsl_log_file *elfrec, nfs_fh3 *fh3)
592 {
593 	if (!nfsl_print_fh)
594 		return;
595 	if (fh3->fh3_length == sizeof (fh3->fh3_u.nfs_fh3_i.fh3_i)) {
596 		nfslog_fhandle_print(elfrec, &fh3->fh3_u.nfs_fh3_i.fh3_i);
597 	} else {
598 		nfslog_opaque_print_buf(fh3->fh3_u.data, fh3->fh3_length,
599 			elfrec->buf, &elfrec->bufoffset,
600 			DFLT_BUFFERSIZE + DFLT_OVFSIZE);
601 	}
602 }
603 
604 /*
605  * NFS VERSION 2
606  */
607 
608 
609 /* Functions that elf print the arguments */
610 
611 static void
612 nfslog_fhandle_print(struct nfsl_log_file *elfrec, fhandle_t *args)
613 {
614 	if (!nfsl_print_fh)
615 		return;
616 	nfslog_opaque_print_buf(args, sizeof (*args),
617 			elfrec->buf, &elfrec->bufoffset,
618 			DFLT_BUFFERSIZE + DFLT_OVFSIZE);
619 }
620 
621 static void
622 nfslog_diropargs_print(struct nfsl_log_file *elfrec, nfslog_diropargs *args)
623 {
624 	char	*elfbuf = elfrec->buf;
625 	int	elfbufoffset = elfrec->bufoffset;
626 
627 	if (nfsl_print_fh) {
628 		nfslog_fhandle_print(elfrec, &args->da_fhandle);
629 		elfbufoffset = elfrec->bufoffset;
630 		if (args->da_name != NULL) {
631 			elfbufoffset += sprintf(&elfbuf[elfbufoffset],
632 				" \"%s\"", args->da_name);
633 		} else {
634 			elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s",
635 				empty_name);
636 		}
637 	}
638 	elfrec->bufoffset = elfbufoffset;
639 }
640 
641 static void
642 nfslog_sattr_print(struct nfsl_log_file *elfrec, nfslog_sattr *args)
643 {
644 	char	*elfbuf = elfrec->buf;
645 	int	elfbufoffset = elfrec->bufoffset;
646 
647 /* BEGIN CSTYLED */
648 	if (args->sa_mode != (uint32_t)-1) {
649 		elfbufoffset += sprintf(&elfbuf[elfbufoffset],
650 			" \"mode=0%o\"", args->sa_mode);
651 	}
652 	if (args->sa_uid != (uint32_t)-1) {
653 		elfbufoffset += sprintf(&elfbuf[elfbufoffset],
654 			" \"uid=0x%x\"", args->sa_uid);
655 	}
656 	if (args->sa_gid != (uint32_t)-1) {
657 		elfbufoffset += sprintf(&elfbuf[elfbufoffset],
658 			" \"gid=0x%x\"", args->sa_gid);
659 	}
660 	if (args->sa_size != (uint32_t)-1) {
661 		elfbufoffset += sprintf(&elfbuf[elfbufoffset],
662 			" \"size=0x%x\"", args->sa_size);
663 	}
664 	if (args->sa_atime.tv_sec != (uint32_t)-1) {
665 		elfbufoffset += sprintf(&elfbuf[elfbufoffset],
666 			" \"atime=%s\"",
667 		    nfsl_get_date_nq((time_t)args->sa_atime.tv_sec));
668 	}
669 	if (args->sa_mtime.tv_sec != (uint32_t)-1) {
670 		elfbufoffset += sprintf(&elfbuf[elfbufoffset],
671 			" \"mtime=%s\"",
672 		    nfsl_get_date_nq((time_t)args->sa_mtime.tv_sec));
673 	}
674 /* END CSTYLED */
675 	elfrec->bufoffset = elfbufoffset;
676 }
677 
678 static void
679 nfslog_setattrargs_print(struct nfsl_log_file *elfrec, nfslog_setattrargs *args)
680 {
681 	nfslog_fhandle_print(elfrec, &args->saa_fh);
682 	nfslog_sattr_print(elfrec, &args->saa_sa);
683 }
684 
685 static void
686 nfslog_nfsreadargs_print(struct nfsl_log_file *elfrec,
687 	nfslog_nfsreadargs *args)
688 {
689 	char	*elfbuf = elfrec->buf;
690 	int	elfbufoffset;
691 
692 	nfslog_fhandle_print(elfrec, &args->ra_fhandle);
693 	elfbufoffset = elfrec->bufoffset;
694 	elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
695 		args->ra_offset);
696 	elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
697 		args->ra_count);
698 	elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
699 		args->ra_totcount);
700 	elfrec->bufoffset = elfbufoffset;
701 }
702 
703 static void
704 nfslog_writeargs_print(struct nfsl_log_file *elfrec, nfslog_writeargs *args)
705 {
706 	char	*elfbuf = elfrec->buf;
707 	int	elfbufoffset = elfrec->bufoffset;
708 
709 	nfslog_fhandle_print(elfrec, &args->waargs_fhandle);
710 	elfbufoffset = elfrec->bufoffset;
711 	elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
712 		args->waargs_begoff);
713 	elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
714 		args->waargs_offset);
715 	elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
716 		args->waargs_totcount);
717 	elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " 0x%x",
718 		args->waargs_count);
719 }
720 
721 static void
722 nfslog_writeresult_print(struct nfsl_log_file *elfrec, nfslog_writeresult *res,
723 	bool_t print_status)
724 {
725 	char	*elfbuf = elfrec->buf;
726 	int	elfbufoffset = elfrec->bufoffset;
727 
728 	if (print_status) {
729 		nfslog_nfsstat_print(elfrec, &res->wr_status, print_status);
730 	} else if (res->wr_status == NFS_OK) {
731 		elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
732 			res->nfslog_writeresult_u.wr_size);
733 		elfrec->bufoffset = elfbufoffset;
734 	}
735 }
736 
737 static void
738 nfslog_creatargs_print(struct nfsl_log_file *elfrec, nfslog_createargs *args)
739 {
740 	nfslog_diropargs_print(elfrec, &args->ca_da);
741 	nfslog_sattr_print(elfrec, &args->ca_sa);
742 }
743 
744 
745 static void
746 nfslog_rddirargs_print(struct nfsl_log_file *elfrec, nfslog_rddirargs *args)
747 {
748 	char	*elfbuf = elfrec->buf;
749 	int	elfbufoffset;
750 
751 	nfslog_fhandle_print(elfrec, &args->rda_fh);
752 	elfbufoffset = elfrec->bufoffset;
753 	elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
754 		args->rda_offset);
755 	elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
756 		args->rda_count);
757 	elfrec->bufoffset = elfbufoffset;
758 }
759 
760 static void
761 nfslog_rnmargs_print(struct nfsl_log_file *elfrec, nfslog_rnmargs *args)
762 {
763 	nfslog_diropargs_print(elfrec, &args->rna_from);
764 	nfslog_diropargs_print(elfrec, &args->rna_to);
765 }
766 
767 static void
768 nfslog_linkargs_print(struct nfsl_log_file *elfrec, nfslog_linkargs *args)
769 {
770 	nfslog_fhandle_print(elfrec, &args->la_from);
771 	nfslog_diropargs_print(elfrec, &args->la_to);
772 }
773 
774 static void
775 nfslog_symlinkargs_print(struct nfsl_log_file *elfrec, nfslog_symlinkargs *args)
776 {
777 	char	*elfbuf = elfrec->buf;
778 	int	elfbufoffset;
779 
780 	nfslog_diropargs_print(elfrec, &args->sla_from);
781 	elfbufoffset = elfrec->bufoffset;
782 	elfbufoffset += sprintf(&elfbuf[elfbufoffset], " \"%s\"",
783 		args->sla_tnm);
784 	elfrec->bufoffset = elfbufoffset;
785 	nfslog_sattr_print(elfrec, &args->sla_sa);
786 }
787 
788 /*
789  * SHARE/UNSHARE fs log args copy
790  */
791 static void
792 nfslog_sharefsargs_print(struct nfsl_log_file *elfrec,
793 	nfslog_sharefsargs *args)
794 {
795 	unsigned int	elfbufoffset;
796 	char		*elfbuf = elfrec->buf;
797 
798 	nfslog_fhandle_print(elfrec, &args->sh_fh_buf);
799 
800 	elfbufoffset = elfrec->bufoffset;
801 	elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
802 		args->sh_flags);
803 	elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
804 		args->sh_anon);
805 	if (nfsl_print_fh) {
806 		if (args->sh_path != NULL) {
807 			elfbufoffset += sprintf(&elfbuf[elfbufoffset],
808 				" \"%s\"", args->sh_path);
809 		} else {
810 			elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s",
811 				empty_name);
812 		}
813 	}
814 	elfrec->bufoffset = elfbufoffset;
815 }
816 
817 static void
818 nfslog_getfhargs_print(struct nfsl_log_file *elfrec,
819 	nfslog_getfhargs *args)
820 {
821 	unsigned int	elfbufoffset;
822 	char		*elfbuf = elfrec->buf;
823 
824 	nfslog_fhandle_print(elfrec, &args->gfh_fh_buf);
825 
826 	elfbufoffset = elfrec->bufoffset;
827 	if (nfsl_print_fh) {
828 		if (args->gfh_path != NULL) {
829 			elfbufoffset += sprintf(&elfbuf[elfbufoffset],
830 				" \"%s\"", args->gfh_path);
831 		} else {
832 			elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s",
833 				empty_name);
834 		}
835 	}
836 	elfrec->bufoffset = elfbufoffset;
837 }
838 
839 static void
840 nfslog_nfsstat_print(struct nfsl_log_file *elfrec, enum nfsstat *res,
841 	bool_t print_status)
842 {
843 	if (print_status) {
844 		char	*statp = nfslog_get_status((short)(*res));
845 
846 		if (statp != NULL)
847 			elfrec->bufoffset +=
848 				sprintf(&elfrec->buf[elfrec->bufoffset], " %s",
849 						statp);
850 		else
851 			elfrec->bufoffset +=
852 				sprintf(&elfrec->buf[elfrec->bufoffset], " %5d",
853 						*res);
854 	}
855 }
856 
857 static void
858 nfslog_diropres_print(struct nfsl_log_file *elfrec, nfslog_diropres *res,
859 	bool_t print_status)
860 {
861 	if (print_status) {
862 		nfslog_nfsstat_print(elfrec, &res->dr_status, print_status);
863 	} else if (res->dr_status == NFS_OK) {
864 		nfslog_fhandle_print(elfrec,
865 			&res->nfslog_diropres_u.dr_ok.drok_fhandle);
866 	}
867 }
868 
869 static void
870 nfslog_rdlnres_print(struct nfsl_log_file *elfrec, nfslog_rdlnres *res,
871 	bool_t print_status)
872 {
873 	if (print_status) {
874 		nfslog_nfsstat_print(elfrec, &res->rl_status, print_status);
875 	} else if (res->rl_status == NFS_OK) {
876 		elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset],
877 			" \"%s\"", res->nfslog_rdlnres_u.rl_ok);
878 	}
879 }
880 
881 static void
882 nfslog_rdresult_print(struct nfsl_log_file *elfrec, nfslog_rdresult *res,
883 	bool_t print_status)
884 {
885 	if (print_status) {
886 		nfslog_nfsstat_print(elfrec, &res->r_status, print_status);
887 	} else if (res->r_status == NFS_OK) {
888 		elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset],
889 			" 0x%x", res->nfslog_rdresult_u.r_ok.filesize);
890 		elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset],
891 			" 0x%x", res->nfslog_rdresult_u.r_ok.rrok_count);
892 	}
893 }
894 
895 static void
896 nfslog_rddirres_print(struct nfsl_log_file *elfrec, nfslog_rddirres *res,
897 	bool_t print_status)
898 {
899 	if (print_status) {
900 		nfslog_nfsstat_print(elfrec, &res->rd_status, print_status);
901 	} else if (res->rd_status == NFS_OK) {
902 		char	*elfbuf = elfrec->buf;
903 		int	elfbufoffset = elfrec->bufoffset;
904 
905 		elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
906 			res->nfslog_rddirres_u.rd_ok.rdok_offset);
907 		elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
908 			res->nfslog_rddirres_u.rd_ok.rdok_size);
909 		elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
910 			res->nfslog_rddirres_u.rd_ok.rdok_eof);
911 		elfrec->bufoffset = elfbufoffset;
912 	}
913 }
914 
915 /*
916  * NFS VERSION 3
917  */
918 
919 static void
920 nfslog_diropargs3_print(struct nfsl_log_file *elfrec,
921 	nfslog_diropargs3 *args)
922 {
923 	if (nfsl_print_fh) {
924 		nfslog_fh3_print(elfrec, &args->dir);
925 		elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset],
926 			" \"%s\"", args->name);
927 	}
928 }
929 
930 static void
931 nfslog_size3_print(struct nfsl_log_file *elfrec, set_size3 *args)
932 {
933 	char	*elfbuf = elfrec->buf;
934 	int	elfbufoffset = elfrec->bufoffset;
935 
936 	if (args->set_it) {
937 		elfbufoffset += sprintf(&elfbuf[elfbufoffset],
938 		/* CSTYLED */
939 			" \"size=0x%llx\"", args->size);
940 	}
941 	elfrec->bufoffset = elfbufoffset;
942 }
943 
944 static void
945 nfslog_SETATTR3args_print(struct nfsl_log_file *elfrec,
946 	nfslog_SETATTR3args *args)
947 {
948 	nfslog_fh3_print(elfrec, &args->object);
949 	nfslog_size3_print(elfrec, &args->size);
950 }
951 
952 static void
953 nfslog_READ3args_print(struct nfsl_log_file *elfrec, nfslog_READ3args *args)
954 {
955 	nfslog_fh3_print(elfrec, &args->file);
956 	elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " 0x%llx",
957 		args->offset);
958 	elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " 0x%x",
959 		args->count);
960 }
961 
962 static void
963 nfslog_WRITE3args_print(struct nfsl_log_file *elfrec,
964 	nfslog_WRITE3args *args)
965 {
966 	char	*elfbuf = elfrec->buf;
967 	int	elfbufoffset;
968 
969 	nfslog_fh3_print(elfrec, &args->file);
970 	elfbufoffset = elfrec->bufoffset;
971 	elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%llx",
972 		args->offset);
973 	elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
974 		args->count);
975 	elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
976 		args->stable);
977 	elfrec->bufoffset = elfbufoffset;
978 }
979 
980 static void
981 nfslog_CREATE3args_print(struct nfsl_log_file *elfrec,
982 	nfslog_CREATE3args *args)
983 {
984 	nfslog_diropargs3_print(elfrec, &args->where);
985 	elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " %s",
986 		NFSL_CREATEMODE3(args->how.mode));
987 	if (args->how.mode != EXCLUSIVE) {
988 		nfslog_size3_print(elfrec,
989 			&args->how.nfslog_createhow3_u.size);
990 	}
991 }
992 
993 static void
994 nfslog_MKDIR3args_print(struct nfsl_log_file *elfrec,
995 	nfslog_MKDIR3args *args)
996 {
997 	nfslog_diropargs3_print(elfrec, &args->where);
998 }
999 
1000 static void
1001 nfslog_SYMLINK3args_print(struct nfsl_log_file *elfrec,
1002 	nfslog_SYMLINK3args *args)
1003 {
1004 	nfslog_diropargs3_print(elfrec, &args->where);
1005 	elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset],
1006 		" \"%s\"", args->symlink_data);
1007 }
1008 
1009 static void
1010 nfslog_MKNOD3args_print(struct nfsl_log_file *elfrec,
1011 	nfslog_MKNOD3args *args)
1012 {
1013 	nfslog_diropargs3_print(elfrec, &args->where);
1014 	elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " %s",
1015 		NFSL_FTYPE3(args->type));
1016 }
1017 
1018 static void
1019 nfslog_REMOVE3args_print(struct nfsl_log_file *elfrec,
1020 	nfslog_REMOVE3args *args)
1021 {
1022 	nfslog_diropargs3_print(elfrec, &args->object);
1023 }
1024 
1025 static void
1026 nfslog_RMDIR3args_print(struct nfsl_log_file *elfrec,
1027 	nfslog_RMDIR3args *args)
1028 {
1029 	nfslog_diropargs3_print(elfrec, &args->object);
1030 }
1031 
1032 static void
1033 nfslog_RENAME3args_print(struct nfsl_log_file *elfrec,
1034 	nfslog_RENAME3args *args)
1035 {
1036 	nfslog_diropargs3_print(elfrec, &args->from);
1037 	nfslog_diropargs3_print(elfrec, &args->to);
1038 }
1039 
1040 static void
1041 nfslog_LINK3args_print(struct nfsl_log_file *elfrec, nfslog_LINK3args *args)
1042 {
1043 	nfslog_fh3_print(elfrec, &args->file);
1044 	nfslog_diropargs3_print(elfrec, &args->link);
1045 }
1046 
1047 static void
1048 nfslog_READDIRPLUS3args_print(struct nfsl_log_file *elfrec,
1049 	nfslog_READDIRPLUS3args *args)
1050 {
1051 	nfslog_fh3_print(elfrec, &args->dir);
1052 	elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " 0x%x",
1053 		args->dircount);
1054 	elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " 0x%x",
1055 		args->maxcount);
1056 }
1057 
1058 static void
1059 nfslog_COMMIT3args_print(struct nfsl_log_file *elfrec,
1060 	nfslog_COMMIT3args *args)
1061 {
1062 	nfslog_fh3_print(elfrec, &args->file);
1063 	elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " 0x%llx",
1064 		args->offset);
1065 	elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " 0x%x",
1066 		args->count);
1067 }
1068 
1069 static void
1070 nfslog_nfsstat3_print(struct nfsl_log_file *elfrec, enum nfsstat3 *res,
1071 	bool_t print_status)
1072 {
1073 	if (print_status) {
1074 		char	*statp = nfslog_get_status((short)(*res));
1075 
1076 		if (statp != NULL)
1077 			elfrec->bufoffset +=
1078 				sprintf(&elfrec->buf[elfrec->bufoffset], " %s",
1079 					statp);
1080 		else
1081 			elfrec->bufoffset +=
1082 				sprintf(&elfrec->buf[elfrec->bufoffset], " %5d",
1083 					*res);
1084 	}
1085 }
1086 
1087 static void
1088 nfslog_LOOKUP3res_print(struct nfsl_log_file *elfrec,
1089 	nfslog_LOOKUP3res *res, bool_t print_status)
1090 {
1091 	if (print_status) {
1092 		nfslog_nfsstat3_print(elfrec, &res->status, print_status);
1093 	} else if (res->status == NFS3_OK) {
1094 		nfslog_fh3_print(elfrec, &res->nfslog_LOOKUP3res_u.object);
1095 	}
1096 }
1097 
1098 static void
1099 nfslog_READLINK3res_print(struct nfsl_log_file *elfrec,
1100 	nfslog_READLINK3res *res, bool_t print_status)
1101 {
1102 	if (print_status) {
1103 		nfslog_nfsstat3_print(elfrec, &res->status, print_status);
1104 	} else if (res->status == NFS3_OK) {
1105 		elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset],
1106 			" %s", res->nfslog_READLINK3res_u.data);
1107 	}
1108 }
1109 
1110 static void
1111 nfslog_READ3res_print(struct nfsl_log_file *elfrec, nfslog_READ3res *res,
1112 	bool_t print_status)
1113 {
1114 	if (print_status) {
1115 		nfslog_nfsstat3_print(elfrec, &res->status, print_status);
1116 	} else if (res->status == NFS3_OK) {
1117 		char	*elfbuf = elfrec->buf;
1118 		int	elfbufoffset = elfrec->bufoffset;
1119 
1120 		elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%llx",
1121 			res->nfslog_READ3res_u.ok.filesize);
1122 		elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
1123 			res->nfslog_READ3res_u.ok.count);
1124 		elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
1125 			res->nfslog_READ3res_u.ok.eof);
1126 		elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
1127 			res->nfslog_READ3res_u.ok.size);
1128 		elfrec->bufoffset = elfbufoffset;
1129 	}
1130 }
1131 
1132 static void
1133 nfslog_WRITE3res_print(struct nfsl_log_file *elfrec, nfslog_WRITE3res *res,
1134 	bool_t print_status)
1135 {
1136 	if (print_status) {
1137 		nfslog_nfsstat3_print(elfrec, &res->status, print_status);
1138 	} else if (res->status == NFS3_OK) {
1139 		char	*elfbuf = elfrec->buf;
1140 		int	elfbufoffset = elfrec->bufoffset;
1141 
1142 		elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%llx",
1143 			res->nfslog_WRITE3res_u.ok.filesize);
1144 		elfbufoffset += sprintf(&elfbuf[elfbufoffset],
1145 			" 0x%x", res->nfslog_WRITE3res_u.ok.count);
1146 		elfbufoffset += sprintf(&elfrec->buf[elfbufoffset],
1147 			" 0x%x", res->nfslog_WRITE3res_u.ok.committed);
1148 		elfrec->bufoffset = elfbufoffset;
1149 	}
1150 }
1151 
1152 static void
1153 nfslog_CREATE3res_print(struct nfsl_log_file *elfrec, nfslog_CREATE3res *res,
1154 	bool_t print_status)
1155 {
1156 	if (print_status) {
1157 		nfslog_nfsstat3_print(elfrec, &res->status, print_status);
1158 	} else if (res->status == NFS3_OK) {
1159 		if (res->nfslog_CREATE3res_u.ok.obj.handle_follows) {
1160 			nfslog_fh3_print(elfrec,
1161 				&res->nfslog_CREATE3res_u.ok.obj.handle);
1162 		}
1163 	}
1164 }
1165 
1166 static void
1167 nfslog_MKDIR3res_print(struct nfsl_log_file *elfrec, nfslog_MKDIR3res *res,
1168 	bool_t print_status)
1169 {
1170 	if (print_status) {
1171 		nfslog_nfsstat3_print(elfrec, &res->status, print_status);
1172 	} else if (res->status == NFS3_OK) {
1173 		if (res->nfslog_MKDIR3res_u.obj.handle_follows) {
1174 			nfslog_fh3_print(elfrec,
1175 				&res->nfslog_MKDIR3res_u.obj.handle);
1176 		}
1177 	}
1178 }
1179 
1180 static void
1181 nfslog_SYMLINK3res_print(struct nfsl_log_file *elfrec, nfslog_SYMLINK3res *res,
1182 	bool_t print_status)
1183 {
1184 	if (print_status) {
1185 		nfslog_nfsstat3_print(elfrec, &res->status, print_status);
1186 	} else if (res->status == NFS3_OK) {
1187 		if (res->nfslog_SYMLINK3res_u.obj.handle_follows) {
1188 			nfslog_fh3_print(elfrec,
1189 				&res->nfslog_SYMLINK3res_u.obj.handle);
1190 		}
1191 	}
1192 }
1193 
1194 static void
1195 nfslog_MKNOD3res_print(struct nfsl_log_file *elfrec, nfslog_MKNOD3res *res,
1196 	bool_t print_status)
1197 {
1198 	if (print_status) {
1199 		nfslog_nfsstat3_print(elfrec, &res->status, print_status);
1200 	} else if (res->status == NFS3_OK) {
1201 		if (res->nfslog_MKNOD3res_u.obj.handle_follows) {
1202 			nfslog_fh3_print(elfrec,
1203 				&res->nfslog_MKNOD3res_u.obj.handle);
1204 		}
1205 	}
1206 }
1207 
1208 static void
1209 nfslog_READDIRPLUS3res_print(struct nfsl_log_file *elfrec,
1210 	nfslog_READDIRPLUS3res *res, bool_t print_status)
1211 {
1212 	if (print_status) {
1213 		nfslog_nfsstat3_print(elfrec, &res->status, print_status);
1214 	}
1215 }
1216 
1217 /*
1218  * **** End of table functions for logging specific procs ****
1219  *
1220  * Hereafter are the general logging management and dispatcher.
1221  */
1222 
1223 
1224 /*
1225  * nfsl_ipaddr_print - extracts sender ip address from transport struct
1226  * and prints it in legible form.
1227  */
1228 static void
1229 nfsl_ipaddr_print(struct nfsl_log_file *elfrec, struct netbuf *ptr)
1230 {
1231 	struct hostent	*hp;
1232 	extern char	*inet_ntop();
1233 	int		size, sin_family, error;
1234 	char		*elfbuf = elfrec->buf;
1235 	char		*addrp;
1236 	int		elfbufoffset = elfrec->bufoffset;
1237 
1238 	if (ptr->len == 0) {
1239 		elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s",
1240 			empty_name);
1241 		elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s",
1242 			empty_name);
1243 		elfrec->bufoffset = elfbufoffset;
1244 		return;
1245 	}
1246 	elfbufoffset += sprintf(&elfbuf[elfbufoffset], " ");
1247 	/* LINTED */
1248 	sin_family = ((struct sockaddr_in *)ptr->buf)->sin_family;
1249 	switch (sin_family) {
1250 	case (AF_INET):
1251 		/* LINTED */
1252 		addrp = (char *)&((struct sockaddr_in *)ptr->buf)->sin_addr;
1253 		size = sizeof (struct in_addr);
1254 		break;
1255 	case (AF_INET6):
1256 		/* LINTED */
1257 		addrp = (char *)&((struct sockaddr_in6 *)ptr->buf)->sin6_addr;
1258 		size = sizeof (struct in6_addr);
1259 		break;
1260 	default:
1261 		/* unknown protocol: print address in hex form */
1262 		for (size = ptr->len, addrp = ptr->buf; size > 0; size--) {
1263 			elfbufoffset += sprintf(&elfbuf[elfbufoffset], "%02x",
1264 				*addrp);
1265 		}
1266 		elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s",
1267 			empty_name);
1268 		elfrec->bufoffset = elfbufoffset;
1269 		return;
1270 	}
1271 	if (inet_ntop(sin_family, addrp, &elfbuf[elfbufoffset],
1272 		(size_t)(DFLT_BUFFERSIZE + DFLT_OVFSIZE - elfbufoffset))
1273 		    == NULL) {
1274 		/* Not enough space to print - should never happen */
1275 		elfbuf[elfrec->bufoffset] = '\0';	/* just in case */
1276 		return;
1277 	}
1278 	/* inet_ntop copied address into elfbuf, so update offset */
1279 	elfbufoffset += strlen(&elfbuf[elfbufoffset]);
1280 	/* get host name and log it as well */
1281 	hp = getipnodebyaddr(addrp, size, sin_family, &error);
1282 	if (hp != NULL) {
1283 		elfbufoffset += sprintf(&elfbuf[elfbufoffset], " \"%s\"",
1284 			hp->h_name);
1285 	} else {
1286 		elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s",
1287 			empty_name);
1288 	}
1289 	elfrec->bufoffset = elfbufoffset;
1290 }
1291 
1292 static void
1293 nfsl_elf_record_header_print(struct nfsl_log_file *elfrec,
1294 	nfslog_record_header *lhp, char *principal_name, char *tag,
1295 	struct nfsl_proc_disp *disp, char *progname)
1296 {
1297 	struct passwd	*pwp = NULL;
1298 	char	*elfbuf = elfrec->buf;
1299 	int	elfbufoffset = elfrec->bufoffset;
1300 
1301 	/*
1302 	 * Fields: time bytes tag rpc-program rpc-version rpc-procedure
1303 	 *	   auth-flavor s-user-name s-uid uid u-name gid net-id
1304 	 *   c-ip c-dns s-dns status rpcarg-path <arguments> <response>
1305 	 */
1306 	elfbufoffset += sprintf(&elfbuf[elfbufoffset], "%s",
1307 		nfsl_get_time((time_t)lhp->rh_timestamp.tv_sec));
1308 	elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
1309 		lhp->rh_reclen);
1310 	if ((tag != NULL) && (tag[0] != '\0')) {
1311 		elfbufoffset += sprintf(&elfbuf[elfbufoffset], " \"%s\"", tag);
1312 	} else {
1313 		elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s",
1314 					empty_name);
1315 	}
1316 	elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s 0x%x %s",
1317 				progname, lhp->rh_version, disp->procname);
1318 	elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s",
1319 		NFSL_AUTH_FLAVOR_PRINT(lhp->rh_auth_flavor));
1320 	if ((principal_name != NULL) && (principal_name[0] != '\0')) {
1321 		elfbufoffset += sprintf(&elfbuf[elfbufoffset], " \"%s\"",
1322 			principal_name);
1323 		if ((pwp = getpwnam(principal_name)) != NULL) {
1324 			elfbufoffset += sprintf(&elfbuf[elfbufoffset],
1325 				" 0x%lx", pwp->pw_uid);
1326 		} else {
1327 			elfbufoffset += sprintf(&elfbuf[elfbufoffset],
1328 				" %s", empty_name);
1329 		}
1330 	} else {
1331 		elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s",
1332 			empty_name);
1333 		elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s",
1334 			empty_name);
1335 	}
1336 	elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%lx", lhp->rh_uid);
1337 	if (((pwp = getpwuid(lhp->rh_uid)) != NULL) && (pwp->pw_name != NULL)) {
1338 		elfbufoffset += sprintf(&elfbuf[elfbufoffset], " \"%s\"",
1339 			pwp->pw_name);
1340 	} else {
1341 		elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s",
1342 			empty_name);
1343 	}
1344 	elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%lx", lhp->rh_gid);
1345 	elfrec->bufoffset = elfbufoffset;
1346 }
1347 
1348 static void
1349 nfsl_elf_buffer_header_print(struct nfsl_log_file *elfrec,
1350 	nfslog_buffer_header *bufhdr)
1351 {
1352 	int	rc;
1353 	struct utsname	name;
1354 	char	*elfbuf = elfrec->buf;
1355 	int	elfbufoffset = elfrec->bufoffset;
1356 
1357 	rc = uname(&name);
1358 	elfbufoffset += sprintf(&elfbuf[elfbufoffset],
1359 		"#Version %d.0\n#Software \"%s\"\n",
1360 		bufhdr->bh_version, ((rc >= 0) ? name.sysname : empty_name));
1361 	elfbufoffset += sprintf(&elfbuf[elfbufoffset], "#Date %s\n",
1362 		nfsl_get_date((time_t)bufhdr->bh_timestamp.tv_sec));
1363 	elfbufoffset += sprintf(&elfbuf[elfbufoffset], "#Remark %s\n",
1364 		empty_name);
1365 	elfbufoffset += sprintf(&elfbuf[elfbufoffset],
1366 		"#Fields: time bytes tag");
1367 	elfbufoffset += sprintf(&elfbuf[elfbufoffset],
1368 		" rpc-program rpc-version rpc-procedure");
1369 	elfbufoffset += sprintf(&elfbuf[elfbufoffset],
1370 		" auth-flavor s-user-name s-uid uid u-name gid net-id c-ip");
1371 	elfbufoffset += sprintf(&elfbuf[elfbufoffset],
1372 		" c-dns s-dns status rpcarg-path");
1373 	elfbufoffset += sprintf(&elfbuf[elfbufoffset],
1374 		" rpc-arguments... rpc-response...\n");
1375 	elfrec->bufoffset = elfbufoffset;
1376 }
1377 
1378 /*
1379  * nfsl_find_elf_dispatch - get the dispatch struct for this request
1380  */
1381 static struct nfsl_proc_disp *
1382 nfsl_find_elf_dispatch(nfslog_request_record *logrec, char **prognamep)
1383 {
1384 	nfslog_record_header	*logrechdr = &logrec->re_header;
1385 	struct nfsl_prog_disp	*progtable;	/* prog struct */
1386 	struct nfsl_vers_disp	*verstable;	/* version struct */
1387 	int			i, vers;
1388 
1389 	/* Find prog element - search because can't use prog as array index */
1390 	for (i = 0; (i < nfsl_elf_dispatch_table_arglen) &&
1391 	    (logrechdr->rh_prognum != nfsl_elf_dispatch_table[i].nfsl_dis_prog);
1392 		i++);
1393 	if (i >= nfsl_elf_dispatch_table_arglen) {	/* program not logged */
1394 		/* not an error */
1395 		return (NULL);
1396 	}
1397 	progtable = &nfsl_elf_dispatch_table[i];
1398 	/* Find vers element - no validity check - if here it's valid vers */
1399 	vers = logrechdr->rh_version - progtable->nfsl_dis_versmin;
1400 	verstable = &progtable->nfsl_dis_vers_table[vers];
1401 	/* Find proc element - no validity check - if here it's valid proc */
1402 	*prognamep = progtable->progname;
1403 	return (&verstable->nfsl_dis_proc_table[logrechdr->rh_procnum]);
1404 }
1405 
1406 /*
1407  * nfsl_elf_rpc_print - Print the record buffer.
1408  */
1409 static void
1410 nfsl_elf_rpc_print(struct nfsl_log_file *elfrec,
1411 	nfslog_request_record *logrec, struct nfsl_proc_disp *disp,
1412 	char *progname, char *path1, char *path2)
1413 {
1414 	if (debug > 1) {
1415 		(void) printf("%s %d %s", progname,
1416 			logrec->re_header.rh_version, disp->procname);
1417 		(void) printf(": '%s', '%s'\n",
1418 			((path1 != NULL) ? path1 : empty_name),
1419 			((path2 != NULL) ? path2 : empty_name));
1420 	}
1421 	/*
1422 	 * XXXX programs using this file to get a usable record should
1423 	 * take "record" struct.
1424 	 */
1425 	/*
1426 	 * Print the variable fields:
1427 	 *	principal name
1428 	 *	netid
1429 	 *	ip address
1430 	 *	rpc args
1431 	 *	rpc res
1432 	 * Use the displacements calculated earlier...
1433 	 */
1434 
1435 	/*
1436 	 * Fields: time bytes tag rpc-program rpc-version rpc-procedure
1437 	 *	   auth-flavor s-user-name s-uid uid u-name gid net-id c-ip
1438 	 *	 c-dns s-dns status rpcarg-path <arguments> <response>
1439 	 */
1440 	nfsl_elf_record_header_print(elfrec, &logrec->re_header,
1441 			logrec->re_principal_name, logrec->re_tag,
1442 			disp, progname);
1443 	if ((logrec->re_netid != NULL) && (logrec->re_netid[0] != '\0')) {
1444 		elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset],
1445 			" \"%s\"", logrec->re_netid);
1446 	} else {
1447 		elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset],
1448 			" %s", empty_name);
1449 	}
1450 	nfsl_ipaddr_print(elfrec, &logrec->re_ipaddr);
1451 	elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset],
1452 		" \"%s\"", hostname);
1453 	/* Next is return status */
1454 	(*disp->nfsl_dis_res)(elfrec, logrec->re_rpc_res, TRUE);
1455 	/* Next is argpath */
1456 	if (path1 != NULL) {
1457 		elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset],
1458 			" \"%s\"", path1);
1459 	} else {
1460 		elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset],
1461 			" %s", empty_name);
1462 	}
1463 	/*
1464 	 * path2 is non-empty for rename/link type operations. If it is non-
1465 	 * empty print it here as it's a part of the args
1466 	 */
1467 	if (path2 != NULL) {
1468 		elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset],
1469 			" \"%s\"", path2);
1470 	}
1471 	/* Next print formatted rpc args */
1472 	(*disp->nfsl_dis_args)(elfrec, logrec->re_rpc_arg);
1473 	/* Next print formatted rpc res (minus status) */
1474 	(*disp->nfsl_dis_res)(elfrec, logrec->re_rpc_res, FALSE);
1475 	elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], "\n");
1476 }
1477 
1478 /*
1479  * nfsl_log_file_add - add a new record to the list
1480  */
1481 static void
1482 nfsl_log_file_add(struct nfsl_log_file *elfrec,
1483 	struct nfsl_log_file **elf_listp)
1484 {
1485 	elfrec->next = *elf_listp;
1486 	elfrec->prev = NULL;
1487 	if (*elf_listp != NULL) {
1488 		(*elf_listp)->prev = elfrec;
1489 	}
1490 	*elf_listp = elfrec;
1491 }
1492 
1493 /*
1494  * nfsl_log_file_find - finds a record in the list, given a cookie (== elfrec)
1495  * Returns the record.
1496  */
1497 static struct nfsl_log_file *
1498 nfsl_log_file_find(struct nfsl_log_file *elfrec,
1499 	struct nfsl_log_file *elf_list)
1500 {
1501 	struct nfsl_log_file	*rec;
1502 
1503 	for (rec = elf_list; (rec != NULL) && (rec != elfrec);
1504 		rec = rec->next);
1505 	return (rec);
1506 }
1507 
1508 /*
1509  * nfsl_log_file_del - delete a record from the list, does not free rec.
1510  * Returns the deleted record.
1511  */
1512 static struct nfsl_log_file *
1513 nfsl_log_file_del(struct nfsl_log_file *elfrec,
1514 	struct nfsl_log_file **elf_listp)
1515 {
1516 	struct nfsl_log_file	*rec;
1517 
1518 	if ((rec = nfsl_log_file_find(elfrec, *elf_listp)) == NULL) {
1519 		return (NULL);
1520 	}
1521 	if (rec->prev != NULL) {
1522 		rec->prev->next = rec->next;
1523 	} else {
1524 		*elf_listp = rec->next;
1525 	}
1526 	if (rec->next != NULL) {
1527 		rec->next->prev = rec->prev;
1528 	}
1529 	return (rec);
1530 }
1531 
1532 /*
1533  * nfsl_log_file_free - frees a record
1534  */
1535 static void
1536 nfsl_log_file_free(struct nfsl_log_file *elfrec)
1537 {
1538 	if (elfrec == NULL)
1539 		return;
1540 	if (elfrec->path != NULL)
1541 		free(elfrec->path);
1542 	if (elfrec->buf != NULL)
1543 		free(elfrec->buf);
1544 	free(elfrec);
1545 }
1546 
1547 /*
1548  * Exported Functions
1549  */
1550 
1551 /*
1552  * nfslog_open_elf_file - open the output elf file and mallocs needed buffers
1553  * Returns a pointer to the nfsl_log_file on success, NULL on error.
1554  *
1555  * *error contains the last error encountered on this object, It can
1556  * be used to avoid reporting the same error endlessly, by comparing
1557  * the current error to the last error. It is reset to the current error
1558  * code on return.
1559  */
1560 void *
1561 nfslog_open_elf_file(char *elfpath, nfslog_buffer_header *bufhdr, int *error)
1562 {
1563 	struct nfsl_log_file *elfrec;
1564 	struct stat stat_buf;
1565 	int preverror = *error;
1566 
1567 	if ((elfrec = malloc(sizeof (*elfrec))) == NULL) {
1568 		*error = errno;
1569 		if (*error != preverror) {
1570 			syslog(LOG_ERR, gettext("nfslog_open_elf_file: %s"),
1571 				strerror(*error));
1572 		}
1573 		return (NULL);
1574 	}
1575 	bzero(elfrec, sizeof (*elfrec));
1576 
1577 	elfrec->buf = (char *)malloc(DFLT_BUFFERSIZE + DFLT_OVFSIZE);
1578 	if (elfrec->buf == NULL) {
1579 		*error = errno;
1580 		if (*error != preverror) {
1581 			syslog(LOG_ERR, gettext("nfslog_open_elf_file: %s"),
1582 				strerror(*error));
1583 		}
1584 		nfsl_log_file_free(elfrec);
1585 		return (NULL);
1586 	}
1587 
1588 	if ((elfrec->path = strdup(elfpath)) == NULL) {
1589 		*error = errno;
1590 		if (*error != preverror) {
1591 			syslog(LOG_ERR, gettext("nfslog_open_elf_file: %s"),
1592 				strerror(*error));
1593 		}
1594 		nfsl_log_file_free(elfrec);
1595 		return (NULL);
1596 	}
1597 
1598 	if ((elfrec->fp = fopen(elfpath, "a")) == NULL) {
1599 		*error = errno;
1600 		if (*error != preverror) {
1601 			syslog(LOG_ERR, gettext("Cannot open '%s': %s"),
1602 				elfpath, strerror(*error));
1603 		}
1604 		nfsl_log_file_free(elfrec);
1605 		return (NULL);
1606 	}
1607 
1608 	if (stat(elfpath, &stat_buf) == -1) {
1609 		*error = errno;
1610 		if (*error != preverror) {
1611 			syslog(LOG_ERR, gettext("Cannot stat '%s': %s"),
1612 				elfpath, strerror(*error));
1613 		}
1614 		(void) fclose(elfrec->fp);
1615 		nfsl_log_file_free(elfrec);
1616 		return (NULL);
1617 	}
1618 
1619 	nfsl_log_file_add(elfrec, &elf_file_list);
1620 
1621 	if (stat_buf.st_size == 0) {
1622 		/*
1623 		 * Print header unto logfile
1624 		 */
1625 		nfsl_elf_buffer_header_print(elfrec, bufhdr);
1626 	}
1627 
1628 	if (hostname[0] == '\0') {
1629 		(void) gethostname(hostname, MAXHOSTNAMELEN);
1630 	}
1631 
1632 	return (elfrec);
1633 }
1634 
1635 /*
1636  * nfslog_close_elf_file - close elffile and write out last buffer
1637  */
1638 void
1639 nfslog_close_elf_file(void **elfcookie)
1640 {
1641 	struct nfsl_log_file	*elfrec;
1642 
1643 	if ((*elfcookie == NULL) || ((elfrec = nfsl_log_file_del(
1644 	    *elfcookie, &elf_file_list)) == NULL)) {
1645 		*elfcookie = NULL;
1646 		return;
1647 	}
1648 	if (elfrec->fp != NULL) {
1649 		/* Write the last output buffer to disk */
1650 		(void) nfsl_write_elfbuf(elfrec);
1651 		(void) fclose(elfrec->fp);
1652 	}
1653 	nfsl_log_file_free(elfrec);
1654 	*elfcookie = NULL;
1655 }
1656 
1657 /*
1658  * nfslog_process_elf_rec - processes the record in the buffer and outputs
1659  *	to the elf log.
1660  * Return 0 for success, errno else.
1661  */
1662 int
1663 nfslog_process_elf_rec(void *elfcookie, nfslog_request_record *logrec,
1664 	char *path1, char *path2)
1665 {
1666 	struct nfsl_log_file	*elfrec;
1667 	struct nfsl_proc_disp	*disp;
1668 	char			*progname;
1669 
1670 	if ((elfrec = nfsl_log_file_find(elfcookie, elf_file_list)) == NULL) {
1671 		return (EINVAL);
1672 	}
1673 	/* Make sure there is room */
1674 	if (elfrec->bufoffset > DFLT_BUFFERSIZE) {
1675 		if (nfsl_write_elfbuf(elfrec) < 0) {
1676 			return (errno);
1677 		}
1678 	}
1679 	if ((disp = nfsl_find_elf_dispatch(logrec, &progname)) != NULL) {
1680 		nfsl_elf_rpc_print(elfrec, logrec, disp, progname,
1681 			path1, path2);
1682 	}
1683 	return (0);
1684 }
1685