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 (c) 1999 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <strings.h>
30 #include <string.h>
31 #include <syslog.h>
32 #include <locale.h>
33 #include <nfs/nfs.h>
34 #include <nfs/export.h>
35 #include <nfs/nfssys.h>
36 #include <nfs/nfs_log.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <stdio.h>
40 #include <errno.h>
41 #include <assert.h>
42 #include <stdlib.h>
43 #include <unistd.h>
44 #include <nfs/nfs_log.h>
45 #include "../lib/nfslog_config.h"
46 #include "buffer_list.h"
47 #include "nfslogd.h"
48 
49 extern int _nfssys(int, void *);
50 
51 /*
52  * simple list used to keep track of bad tag messages syslogged.
53  */
54 struct list {
55 	char *l_name;
56 	struct list *l_next;
57 };
58 
59 static void badtag_notify(char *tag);
60 static struct list *badtag_list = NULL;
61 
62 static void cleanup_elf_state(nfsl_config_t *);
63 static void cleanup_trans_state(nfsl_config_t *);
64 
65 /*
66  * Read the contents of the 'bufferpath', process them and store the
67  * user-readable log in 'elfpath', updating the 'fhpath' filehandle
68  * table.
69  * The contents of the configuration list (*config_list) may be
70  * modified if the configuration file has been updated and we can not
71  * find the configuration entry in the currently loaded list.
72  *
73  * Returns 0 on success and sets *buffer_processed to 1.
74  *	   non zero error on failure and *buffer_processed set to 0.
75  */
76 int
77 process_buffer(
78 	struct buffer_ent *bep,
79 	nfsl_config_t **config_list,
80 	int min_size,
81 	int idle_time,
82 	int *buffer_processed)
83 {
84 	struct stat st;
85 	struct nfsl_flush_args nfa;
86 	struct nfslog_buf *lbp = NULL;
87 	struct nfslog_lr *lrp;
88 	char *path1 = NULL;
89 	char *path2 = NULL;
90 	char *buffer_inprog = NULL;
91 	int buffer_inprog_len;
92 	int error = 0;
93 	nfsl_config_t *ncp = NULL, *last_good_ncp;
94 	char *bufferpath = bep->be_name;
95 	char *tag;
96 	boolean_t elf_checked = B_FALSE;
97 	boolean_t trans_checked = B_FALSE;
98 
99 	assert(buffer_processed != NULL);
100 	assert(bufferpath != NULL);
101 
102 	if (stat(bufferpath, &st) == -1) {
103 		error = errno;
104 		if (error == ENOENT) {
105 			error = 0;
106 			buffer_inprog_len = strlen(bufferpath) +
107 				strlen(LOG_INPROG_STRING) + 1;
108 			buffer_inprog = (char *)malloc(buffer_inprog_len);
109 			if (buffer_inprog == NULL) {
110 				syslog(LOG_ERR, gettext(
111 					"process_buffer: malloc failed"));
112 				return (ENOMEM);
113 			}
114 			(void) sprintf(buffer_inprog, "%s%s", bufferpath,
115 				LOG_INPROG_STRING);
116 
117 			if (stat(buffer_inprog, &st) == -1) {
118 				error = errno;
119 				if (bep->be_error != error) {
120 					syslog(LOG_ERR, gettext(
121 						"Can not stat %s: %s"),
122 						buffer_inprog, strerror(error));
123 				}
124 				free(buffer_inprog);
125 				return (error);
126 			}
127 
128 			free(buffer_inprog);
129 
130 			/*
131 			 * Does the buffer in progress meet our minimum
132 			 * processing requirements? or has it been around
133 			 * longer than we're willing to wait for more
134 			 * data to be logged?
135 			 */
136 			if ((st.st_size < min_size) &&
137 			    ((time(0) - bep->be_lastprocessed) < idle_time)) {
138 				/*
139 				 * The buffer does not meet the minimum
140 				 * size processing requirements, and it has not
141 				 * been around longer than we're willing to
142 				 * wait for more data collection.
143 				 * We return now without processing it.
144 				 */
145 				return (0);
146 			}
147 
148 			/*
149 			 * Issue the LOG_FLUSH system call to flush the
150 			 * buffer and process it.
151 			 */
152 			(void) memset((void *)&nfa, 0, sizeof (nfa));
153 			nfa.version = NFSL_FLUSH_ARGS_VERS;
154 			nfa.directive = NFSL_RENAME | NFSL_SYNC;
155 			nfa.buff = bufferpath;
156 			nfa.buff_len = strlen(bufferpath) + 1;
157 
158 			if (_nfssys(LOG_FLUSH, &nfa) < 0) {
159 				error = errno;
160 				if (bep->be_error != error) {
161 					syslog(LOG_ERR, gettext(
162 						"_nfssys(%s) failed: %s"),
163 						nfa.buff, strerror(error));
164 				}
165 				return (error);
166 			}
167 		} else {
168 			if (bep->be_error != error) {
169 				syslog(LOG_ERR, gettext("Can not stat %s: %s"),
170 					bufferpath, strerror(error));
171 			}
172 			return (error);
173 		}
174 	}
175 
176 	/*
177 	 * Open and lock input buffer.
178 	 * Passes in the value of the last error so that it will not
179 	 * print it again if it is still hitting the same error condition.
180 	 */
181 	error = bep->be_error;
182 	if ((lbp = nfslog_open_buf(bufferpath, &error)) == NULL)
183 		goto done;
184 
185 	if ((ncp = last_good_ncp =
186 	    nfsl_findconfig(*config_list, "global", &error)) == NULL) {
187 		assert(error != 0);
188 		nfsl_freeconfig_list(config_list);
189 		if (error != bep->be_error) {
190 			syslog(LOG_ERR, gettext(
191 				"Could not search config list: %s"),
192 			strerror(error));
193 		}
194 		goto done;
195 	}
196 
197 	assert(error == 0);
198 	while ((lrp = nfslog_get_logrecord(lbp)) != NULL && keep_running) {
199 
200 		if (*buffer_processed == 0)
201 			(*buffer_processed)++;
202 
203 		/*
204 		 * Get the matching config entry.
205 		 */
206 		tag = lrp->log_record.re_tag;
207 		if (strcmp(tag, last_good_ncp->nc_name) != 0) {
208 			ncp = nfsl_findconfig(*config_list, tag, &error);
209 			if (error) {
210 				if (error != bep->be_error) {
211 					syslog(LOG_ERR, gettext(
212 					"Could not search config list: %s"),
213 					strerror(error));
214 				}
215 				nfsl_freeconfig_list(config_list);
216 				goto done;
217 			}
218 			if (ncp == NULL) {
219 				badtag_notify(tag);
220 				ncp = last_good_ncp;
221 				goto skip;
222 			}
223 			last_good_ncp = ncp;
224 		}
225 
226 		if (ncp->nc_flags & NC_UPDATED) {
227 			/*
228 			 * The location of the log files may have changed,
229 			 * we need to close transactions and invalidate
230 			 * cookies so that the log files can be reopened
231 			 * further down.
232 			 */
233 			cleanup_elf_state(ncp);
234 			cleanup_trans_state(ncp);
235 
236 			ncp->nc_flags &= ~NC_UPDATED;
237 
238 			/*
239 			 * Force cookies to be recreated if necessary.
240 			 */
241 			elf_checked = trans_checked = B_FALSE;
242 		}
243 
244 		/*
245 		 * Open output files.
246 		 */
247 		if (ncp->nc_rpclogpath != NULL) {
248 			/*
249 			 * Log rpc requests in W3C-ELF format.
250 			 */
251 			if (!elf_checked && ncp->nc_elfcookie != NULL) {
252 				/*
253 				 * Make sure file still exists.
254 				 * Do this once per buffer.
255 				 */
256 				if (stat(ncp->nc_rpclogpath, &st) == -1 &&
257 				    errno == ENOENT) {
258 					/*
259 					 * The open rpclogfile has been
260 					 * deleted.  Get new one below.
261 					 */
262 					cleanup_elf_state(ncp);
263 				}
264 				elf_checked = B_TRUE;
265 			}
266 			if (ncp->nc_elfcookie == NULL) {
267 				error = bep->be_error;
268 				ncp->nc_elfcookie = nfslog_open_elf_file(
269 					ncp->nc_rpclogpath,
270 					&lbp->bh, &error);
271 				if (ncp->nc_elfcookie == NULL) {
272 					bep->be_error = error;
273 					goto done;
274 				}
275 			}
276 		}
277 
278 		if (ncp->nc_logpath != NULL) {
279 			/*
280 			 * Log rpc reqs in trans/ftp format.
281 			 */
282 			if (!trans_checked && ncp->nc_transcookie != NULL) {
283 				/*
284 				 * Do this once per buffer.
285 				 */
286 				if (stat(ncp->nc_logpath, &st) == -1 &&
287 				    errno == ENOENT) {
288 					/*
289 					 * The open transaction file has been
290 					 * deleted. Close pending transaction
291 					 * work. A new transaction log will be
292 					 * opened by nfslog_open_trans_file()
293 					 * below.
294 					 */
295 					cleanup_trans_state(ncp);
296 				}
297 				trans_checked = B_TRUE;
298 			}
299 			if (ncp->nc_transcookie == NULL) {
300 				int transtolog;
301 
302 				transtolog =
303 				(ncp->nc_logformat == TRANSLOG_BASIC) ?
304 					TRANSTOLOG_OPER_READWRITE :
305 					TRANSTOLOG_ALL;
306 				error = bep->be_error;
307 				ncp->nc_transcookie = nfslog_open_trans_file(
308 					ncp->nc_logpath,
309 					ncp->nc_logformat,
310 					transtolog, &error);
311 				if (ncp->nc_transcookie == NULL) {
312 					bep->be_error = error;
313 					goto done;
314 				}
315 			}
316 		}
317 
318 		assert(ncp->nc_fhpath != NULL);
319 
320 		if (nfslog_process_fh_rec(lrp, ncp->nc_fhpath, &path1, &path2,
321 		    ncp->nc_elfcookie != NULL)) {
322 			/*
323 			 * Make sure there is room.
324 			 */
325 			if (ncp->nc_elfcookie != NULL) {
326 				(void) nfslog_process_elf_rec(ncp->nc_elfcookie,
327 					&lrp->log_record, path1, path2);
328 			}
329 
330 			if (ncp->nc_transcookie != NULL) {
331 				(void) nfslog_process_trans_rec(
332 					ncp->nc_transcookie,
333 					&lrp->log_record, ncp->nc_fhpath,
334 					path1, path2);
335 			}
336 		}
337 
338 skip:		if (path1 != NULL)
339 			free(path1);
340 		if (path2 != NULL)
341 			free(path2);
342 
343 		path1 = path2 = NULL;
344 		nfslog_free_logrecord(lrp, TRUE);
345 	} /* while */
346 
347 	if (!error && keep_running) {
348 		/*
349 		 * Keep track of when this buffer was last processed.
350 		 */
351 		bep->be_lastprocessed = time(0);
352 
353 		if (test && *buffer_processed != 0) {
354 			/*
355 			 * Save the buffer for future debugging. We do this
356 			 * by following the log cycling policy, with a maximum
357 			 * of 'max_logs_preserve' to save.
358 			 */
359 			if (cycle_log(bufferpath, max_logs_preserve)) {
360 				syslog(LOG_ERR, gettext(
361 					"could not save copy of buffer \"%s\""),
362 					bufferpath);
363 			}
364 		} else {
365 			/*
366 			 * Remove buffer since it has been processed.
367 			 */
368 			if (unlink(bufferpath)) {
369 				error = errno;
370 				syslog(LOG_ERR, gettext(
371 					"could not unlink %s: %s"),
372 					bufferpath, strerror(error));
373 				/*
374 				 * Buffer was processed correctly.
375 				 */
376 				error = 0;
377 			}
378 		}
379 	}
380 
381 done:
382 	if (lbp != NULL)
383 		nfslog_close_buf(lbp, quick_cleaning);
384 	if (ncp && !quick_cleaning)
385 		cleanup_elf_state(ncp);
386 
387 	return (error);
388 }
389 
390 static void
391 cleanup_elf_state(nfsl_config_t *ncp)
392 {
393 	if (ncp->nc_elfcookie != NULL) {
394 		nfslog_close_elf_file(&ncp->nc_elfcookie);
395 		assert(ncp->nc_elfcookie == NULL);
396 	}
397 }
398 
399 static void
400 cleanup_trans_state(nfsl_config_t *ncp)
401 {
402 	if (ncp->nc_transcookie != NULL) {
403 		nfslog_close_transactions(&ncp->nc_transcookie);
404 		assert(ncp->nc_transcookie == NULL);
405 	}
406 }
407 
408 /*
409  * Searches the list of previously seen bad tags. Note that this
410  * list is never pruned. This should not be a problem since the
411  * list of bad tags should be fairl small. New entries are inserted
412  * at the beginning of the list assuming it will be accessed more
413  * frequently since we have just seen it.
414  */
415 static void
416 badtag_notify(char *tag)
417 {
418 	struct list *lp, *p;
419 	int error;
420 
421 	for (p = badtag_list; p != NULL; p = p->l_next) {
422 		if (strcmp(tag, p->l_name) == 0) {
423 			/*
424 			 * We've seen this before, nothing to do.
425 			 */
426 			return;
427 		}
428 	}
429 
430 	/*
431 	 * Not on the list, add it.
432 	 */
433 	syslog(LOG_ERR, gettext(
434 		"tag \"%s\" not found in %s - "
435 		"ignoring records referencing such tag."),
436 		tag, NFSL_CONFIG_FILE_PATH);
437 
438 	if ((lp = (struct list *)malloc(sizeof (*lp))) != NULL) {
439 		if ((lp->l_name = strdup(tag)) != NULL) {
440 			lp->l_next = badtag_list;
441 			badtag_list = lp;
442 			return;		/* done */
443 		}
444 	}
445 
446 	if (lp->l_name != NULL)
447 		free(lp->l_name);
448 	if (lp)
449 		free(lp);
450 	error = errno;
451 	syslog(LOG_ERR, gettext(
452 	    "Cannot add \"%s\" to bad tag list: %s"), tag, strerror(error));
453 }
454