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