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