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/*
30 * Manipulates the nfslogtab
31 */
32
33#ifndef _REENTRANT
34#define	_REENTRANT
35#endif
36
37#include <sys/types.h>
38#include <sys/stat.h>
39#include <errno.h>
40#include <utmpx.h>
41#include <assert.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <unistd.h>
45#include <strings.h>
46#include <string.h>
47#include "nfslogtab.h"
48
49#ifndef	LINTHAPPY
50#define	LINTHAPPY
51#endif
52
53static void logtab_ent_list_free(struct logtab_ent_list *);
54
55/*
56 * Retrieves the next entry from nfslogtab.
57 * Assumes the file is locked.
58 * '*lepp' points to the new entry if successful.
59 * Returns:
60 *      > 0  valid entry
61 *      = 0  end of file
62 *      < 0  error
63 */
64int
65logtab_getent(FILE *fd, struct logtab_ent **lepp)
66{
67	char line[MAXBUFSIZE + 1];
68	char *p;
69	char *lasts, *tmp;
70	char *w = " \t";
71	struct logtab_ent *lep = NULL;
72	int error = 0;
73
74	if ((lep = (struct logtab_ent *)malloc(sizeof (*lep))) == NULL) {
75		return (-1);
76	}
77	(void) memset((char *)lep, 0, sizeof (*lep));
78
79	if ((p = fgets(line, MAXBUFSIZE, fd)) == NULL)  {
80		error = 0;
81		goto errout;
82	}
83
84	line[strlen(line) - 1] = '\0';
85
86	tmp = (char *)strtok_r(p, w, &lasts);
87	if (tmp == NULL) {
88		error = -1;
89		goto errout;
90	}
91	if ((lep->le_buffer = strdup(tmp)) == NULL) {
92		error = -1;
93		goto errout;
94	}
95
96	tmp = (char *)strtok_r(NULL, w, &lasts);
97	if (tmp == NULL) {
98		error = -1;
99		goto errout;
100	}
101	if ((lep->le_path = strdup(tmp)) == NULL) {
102		error = -1;
103		goto errout;
104	}
105
106	tmp = (char *)strtok_r(NULL, w, &lasts);
107	if (tmp == NULL) {
108		error = -1;
109		goto errout;
110	}
111	if ((lep->le_tag = strdup(tmp)) == NULL) {
112		error = -1;
113		goto errout;
114	}
115
116	tmp = (char *)strtok_r(NULL, w, &lasts);
117	if (tmp == NULL) {
118		error = -1;
119		goto errout;
120	}
121	lep->le_state = atoi(tmp);
122
123	*lepp = lep;
124	return (1);
125
126errout:
127	logtab_ent_free(lep);
128
129	return (error);
130}
131
132/*
133 * Append an entry to the logtab file.
134 */
135int
136logtab_putent(FILE *fd, struct logtab_ent *lep)
137{
138	int r;
139
140	if (fseek(fd, 0L, SEEK_END) < 0)
141		return (errno);
142
143	r = fprintf(fd, "%s\t%s\t%s\t%d\n",
144		lep->le_buffer,
145		lep->le_path,
146		lep->le_tag,
147		lep->le_state);
148
149	return (r);
150}
151
152#ifndef	LINTHAPPY
153/*
154 * Searches the nfslogtab file looking for the next entry which matches
155 * the search criteria. The search is continued at the current position
156 * in the nfslogtab file.
157 * If 'buffer' != NULL, then buffer is matched.
158 * If 'path' != NULL, then path is matched.
159 * If 'tag' != NULL, then tag is matched.
160 * If 'state' != -1, then state is matched.
161 * 'buffer', 'path' and 'tag' can all be non-NULL, which means the entry must
162 * satisfy all requirements.
163 *
164 * Returns 0 on success, ENOENT otherwise.
165 * If found, '*lepp' points to the matching entry, otherwise '*lepp' is
166 * undefined.
167 */
168static int
169logtab_findent(FILE *fd, char *buffer, char *path, char *tag, int state,
170		struct logtab_ent **lepp)
171{
172	boolean_t found = B_FALSE;
173
174	while (!found && (logtab_getent(fd, lepp) > 0)) {
175		found = B_TRUE;
176		if (buffer != NULL)
177			found = strcmp(buffer, (*lepp)->le_buffer) == 0;
178		if (path != NULL)
179			found = found && (strcmp(path, (*lepp)->le_path) == 0);
180		if (tag != NULL)
181			found = found && (strcmp(tag, (*lepp)->le_tag) == 0);
182		if (state != -1)
183			found = found && (state == (*lepp)->le_state);
184		if (!found)
185			logtab_ent_free(*lepp);
186	}
187
188	return (found ? 0 : ENOENT);
189}
190#endif
191
192/*
193 * Remove all entries which match the search criteria.
194 * If 'buffer' != NULL, then buffer is matched.
195 * If 'path' != NULL, then path is matched.
196 * If 'tag' != NULL, then tag is matched.
197 * If 'state' != -1, then state is matched.
198 * 'buffer', 'path' and 'tag' can all be non-NULL, which means the entry must
199 * satisfy all requirements.
200 * The file is assumed to be locked.
201 * Read the entries into a linked list of logtab_ent structures
202 * minus the entries to be removed, then truncate the nfslogtab
203 * file and write it back to the file from the linked list.
204 *
205 * On success returns 0, -1 otherwise.
206 * Entry not found is treated as success since it was going to be removed
207 * anyway.
208 */
209int
210logtab_rement(FILE *fd, char *buffer, char *path, char *tag, int state)
211{
212	struct logtab_ent_list *head = NULL, *tail = NULL, *tmpl;
213	struct logtab_ent *lep;
214	int remcnt = 0;		/* remove count */
215	int error = 0;
216	boolean_t found;
217
218	rewind(fd);
219	while ((error = logtab_getent(fd, &lep)) > 0) {
220		found = B_TRUE;
221		if (buffer != NULL)
222			found = strcmp(buffer, lep->le_buffer) == 0;
223		if (path != NULL)
224			found = found && (strcmp(path, lep->le_path) == 0);
225		if (tag != NULL)
226			found = found && (strcmp(tag, lep->le_tag) == 0);
227		if (state != -1)
228			found = found && (state == lep->le_state);
229		if (found) {
230			remcnt++;
231			logtab_ent_free(lep);
232		} else {
233			tmpl = (struct logtab_ent_list *)
234				malloc(sizeof (struct logtab_ent));
235			if (tmpl == NULL) {
236				error = ENOENT;
237				break;
238			}
239
240			tmpl->lel_le = lep;
241			tmpl->lel_next = NULL;
242			if (head == NULL) {
243				/*
244				 * empty list
245				 */
246				head = tail = tmpl;
247			} else {
248				/*
249				 * Add to the end of the list and remember
250				 * the new last element.
251				 */
252				tail->lel_next = tmpl;
253				tail = tmpl;	/* remember the last element */
254			}
255		}
256	}
257
258	if (error)
259		goto deallocate;
260
261	if (remcnt == 0) {
262		/*
263		 * Entry not found, nothing to do
264		 */
265		goto deallocate;
266	}
267
268	if (ftruncate(fileno(fd), 0) < 0) {
269		error = -1;
270		goto deallocate;
271	}
272
273	for (tmpl = head; tmpl != NULL; tmpl = tmpl->lel_next)
274		(void) logtab_putent(fd, tmpl->lel_le);
275
276deallocate:
277	logtab_ent_list_free(head);
278
279	return (error);
280}
281
282/*
283 * Deactivate all entries matching search criteria.
284 * If 'buffer' != NULL then match buffer.
285 * If 'path' != NULL then match path.
286 * If 'tag' != NULL then match tag.
287 * Note that 'buffer', 'path' and 'tag' can al be non-null at the same time.
288 *
289 * Rewrites the nfslogtab file with the updated state for each entry.
290 * Assumes the nfslogtab file has been locked for writing.
291 * Returns 0 on success, -1 on failure.
292 */
293int
294logtab_deactivate(FILE *fd, char *buffer, char *path, char *tag)
295{
296	struct logtab_ent_list *lelp, *head = NULL, *tail = NULL;
297	struct logtab_ent *lep;
298	boolean_t found;
299	int error = 0;
300	int count = 0;
301
302	rewind(fd);
303	while ((error = logtab_getent(fd, &lep)) > 0) {
304		found = B_TRUE;
305		if (buffer != NULL)
306			found = strcmp(buffer, lep->le_buffer) == 0;
307		if (path != NULL)
308			found = found && (strcmp(path, lep->le_path) == 0);
309		if (tag != NULL)
310			found = found && (strcmp(tag, lep->le_tag) == 0);
311		if (found && (lep->le_state == LES_ACTIVE)) {
312			count++;
313			lep->le_state = LES_INACTIVE;
314		}
315
316		lelp = (struct logtab_ent_list *)
317			malloc(sizeof (struct logtab_ent));
318		if (lelp == NULL) {
319			error = ENOENT;
320			break;
321		}
322
323		lelp->lel_le = lep;
324		lelp->lel_next = NULL;
325		if (head == NULL) {
326			/*
327			 * empty list
328			 */
329			head = tail = lelp;
330		} else {
331			/*
332			 * Add to the end of the list and remember
333			 * the new last element.
334			 */
335			tail->lel_next = lelp;
336			tail = lelp;	/* remember the last element */
337		}
338	}
339
340	if (error)
341		goto deallocate;
342
343	if (count == 0) {
344		/*
345		 * done
346		 */
347		error = 0;
348		goto deallocate;
349	}
350
351	if (ftruncate(fileno(fd), 0) < 0) {
352		error = -1;
353		goto deallocate;
354	}
355
356	for (lelp = head; lelp != NULL; lelp = lelp->lel_next)
357		(void) logtab_putent(fd, lelp->lel_le);
358
359deallocate:
360	logtab_ent_list_free(head);
361
362	return (error);
363}
364
365/*
366 * Deactivates all entries if nfslogtab exists and is older than boot time
367 * This will only happen the first time it is called.
368 * Assumes 'fd' has been locked by the caller.
369 * Returns 0 on success, otherwise -1.
370 */
371int
372logtab_deactivate_after_boot(FILE *fd)
373{
374	struct stat st;
375	struct utmpx *utmpxp;
376	int error = 0;
377
378	if ((fstat(fileno(fd), &st) == 0) &&
379	    ((utmpxp = getutxent()) != NULL) &&
380	    (utmpxp->ut_xtime > st.st_mtime)) {
381		if (logtab_deactivate(fd, NULL, NULL, NULL))
382			error = -1;
383	}
384
385	return (error);
386}
387
388void
389logtab_ent_free(struct logtab_ent *lep)
390{
391	if (lep->le_buffer)
392		free(lep->le_buffer);
393	if (lep->le_path)
394		free(lep->le_path);
395	if (lep->le_tag)
396		free(lep->le_tag);
397	free(lep);
398}
399
400static void
401logtab_ent_list_free(struct logtab_ent_list *head)
402{
403	struct logtab_ent_list *lelp, *next;
404
405	if (head == NULL)
406		return;
407
408	for (lelp = head; lelp != NULL; lelp = next) {
409		if (lelp->lel_le != NULL)
410			logtab_ent_free(lelp->lel_le);
411		next = lelp->lel_next;
412		free(lelp);
413	}
414}
415