1 /* This file is part of GNU Rush.
2    Copyright (C) 2008-2019 Sergey Poznyakoff
3 
4    GNU Rush is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8 
9    GNU Rush is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with GNU Rush.  If not, see <http://www.gnu.org/licenses/>. */
16 
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 
21 #include <sys/types.h>
22 #include <sys/time.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <errno.h>
27 #include <string.h>
28 
29 #include "librush.h"
30 
31 enum rush_wtmp_dir rush_wtmp_dir = rush_wtmp_forward;
32 static int wtmp_fd = -1;
33 static size_t wtmp_recsize = 0;
34 
35 int
rush_wtmp_open(const char * name,int rw)36 rush_wtmp_open(const char *name, int rw)
37 {
38 	int fd;
39 
40 	fd = open(name, rw ? O_RDWR|O_CREAT : O_RDONLY, rushdb_file_mode);
41 	if (fd == -1)
42 		return -1;
43 	wtmp_fd = fd;
44 	return 0;
45 }
46 
47 int
rush_wtmp_close()48 rush_wtmp_close()
49 {
50 	int rc = close(wtmp_fd);
51 	wtmp_fd = -1;
52 	return rc;
53 }
54 
55 int
rush_wtmp_rewind(void)56 rush_wtmp_rewind(void)
57 {
58 	int whence;
59 
60 	switch (rush_wtmp_dir) {
61 	case rush_wtmp_forward:
62 		whence = SEEK_SET;
63 		break;
64 
65 	case rush_wtmp_backward:
66 		whence = SEEK_END;
67 		break;
68 
69 	default:
70 		/* Should not happen */
71 		abort();
72 	}
73 	return lseek(wtmp_fd, 0, whence) == -1;
74 }
75 
76 void
rush_wtmp_set_dir(enum rush_wtmp_dir dir)77 rush_wtmp_set_dir(enum rush_wtmp_dir dir)
78 {
79 	rush_wtmp_dir = dir;
80 	rush_wtmp_rewind();
81 }
82 
83 int
rush_wtmp_seek(off_t off)84 rush_wtmp_seek(off_t off)
85 {
86 	off_t rc = lseek(wtmp_fd, off, SEEK_SET);
87 	if (rc == off) {
88 		wtmp_recsize = 0;
89 		return 0;
90 	}
91 	return 1;
92 }
93 
94 struct rush_wtmp *
alloc_wtmp(size_t reclen)95 alloc_wtmp(size_t reclen)
96 {
97 	struct rush_wtmp *wtmp = malloc(sizeof(*wtmp) + reclen
98 					- sizeof(size_t));
99 	if (wtmp)
100 		wtmp->reclen = reclen;
101 	return wtmp;
102 }
103 
104 enum rushdb_result
rush_wtmp_read_fwd(struct rush_wtmp ** pwtmp)105 rush_wtmp_read_fwd(struct rush_wtmp **pwtmp)
106 {
107 	struct rush_wtmp *wtmprec;
108 	size_t reclen, left;
109 	ssize_t size;
110 	char *p, *s;
111 
112 	if (wtmp_fd == -1) {
113 		errno = EINVAL;
114 		return rushdb_result_fail;
115 	}
116 
117 	size = read(wtmp_fd, &reclen, sizeof(reclen));
118 	if (size == 0)
119 		return rushdb_result_eof;
120 	if (size != sizeof(reclen))
121 		return rushdb_result_fail;
122 	wtmp_recsize = reclen;
123 
124 	wtmprec = alloc_wtmp(reclen);
125 	if (!wtmprec)
126 		return rushdb_result_fail;
127 	p = RUSH_WTMP_DATA_PTR(wtmprec);
128 	reclen -= sizeof(reclen);
129 	left = reclen;
130 	while (left) {
131 		ssize_t n = read(wtmp_fd, p, left);
132 		if (n == -1)
133 			goto errlab;
134 		p += n;
135 		left -= n;
136 	}
137 	p = (char*) (wtmprec + 1);
138 	s = p;
139 	wtmprec->user = s;
140 	s += strlen(s) + 1;
141 	if (s - p > reclen)
142 		goto errlab;
143 	wtmprec->rule = s;
144 	s += strlen(s) + 1;
145 	if (s - p > reclen)
146 		goto errlab;
147 	wtmprec->command = s;
148 
149 	*pwtmp = wtmprec;
150 	return rushdb_result_ok;
151 
152   errlab:
153 	free(wtmprec);
154 	rush_wtmp_close();
155 	return rushdb_result_fail;
156 }
157 
158 enum rushdb_result
rush_wtmp_read(struct rush_wtmp ** pwtmp)159 rush_wtmp_read(struct rush_wtmp **pwtmp)
160 {
161 	size_t reclen;
162 	enum rushdb_result res;
163 
164 	switch (rush_wtmp_dir) {
165 	case rush_wtmp_forward:
166 		res = rush_wtmp_read_fwd(pwtmp);
167 		if (lseek(wtmp_fd, sizeof(reclen), SEEK_CUR) == -1)
168 			res = rushdb_result_fail;
169 		break;
170 
171 	case rush_wtmp_backward:
172 		if (lseek(wtmp_fd, 0, SEEK_CUR) == 0)
173 			return rushdb_result_eof;
174 		if (lseek(wtmp_fd, -sizeof(reclen), SEEK_CUR) == -1)
175 			return rushdb_result_fail;
176 		if (read(wtmp_fd, &reclen, sizeof(reclen)) != sizeof(reclen))
177 			return rushdb_result_fail;
178 		if (lseek(wtmp_fd, -(reclen + sizeof(reclen)), SEEK_CUR) == -1)
179 			return rushdb_result_fail;
180 		res = rush_wtmp_read_fwd(pwtmp);
181 		if (res == rushdb_result_ok) {
182 			if (lseek(wtmp_fd, -reclen, SEEK_CUR) == -1)
183 				return rushdb_result_fail;
184 		}
185 		break;
186 
187 	default:
188 		/* Should not happen */
189 		abort();
190 	}
191 	return res;
192 }
193 
194 size_t
rush_wtmp_reclen(struct rush_wtmp * src)195 rush_wtmp_reclen(struct rush_wtmp *src)
196 {
197 	size_t reclen = sizeof(struct rush_wtmp)
198 		+ strlen(src->user) + 1
199 		+ strlen(src->rule) + 1
200 		+ strlen(src->command) + 1;
201 	return reclen;
202 }
203 
204 struct rush_wtmp *
rush_wtmp_copy(struct rush_wtmp * src)205 rush_wtmp_copy(struct rush_wtmp *src)
206 {
207 	size_t reclen = rush_wtmp_reclen(src);
208 	struct rush_wtmp *dst = malloc(reclen);
209 	if (dst) {
210 		char *p;
211 
212 		dst->reclen = reclen;
213 		dst->pid = src->pid;
214 		dst->start = src->start;
215 		dst->stop = src->stop;
216 		p = (char*) (dst + 1);
217 		strcpy(p, src->user);
218 		dst->user = NULL;
219 		p += strlen(p) + 1;
220 		dst->rule = NULL;
221 		strcpy(p, src->rule);
222 		p += strlen(p) + 1;
223 		dst->command = NULL;
224 		strcpy(p, src->command);
225 		p += strlen(p) + 1;
226 	}
227 	return dst;
228 }
229 
230 off_t
rush_wtmp_append(struct rush_wtmp * wtmp)231 rush_wtmp_append(struct rush_wtmp *wtmp)
232 {
233 	size_t left;
234 	char *p;
235 	off_t off;
236 	struct rush_wtmp *record;
237 
238 	if (wtmp_fd == -1) {
239 		errno = EINVAL;
240 		return -1;
241 	}
242 
243 	off = lseek(wtmp_fd, 0, SEEK_END);
244 	if (off == -1)
245 		return -1;
246 
247 	record = rush_wtmp_copy(wtmp);
248 
249 	rushdb_lock(wtmp_fd, record->reclen, off, SEEK_SET, RUSH_LOCK_WRITE);
250 	left = record->reclen;
251 	p = (char*) record;
252 	while (left) {
253 		ssize_t n = write(wtmp_fd, p, left);
254 		if (n == -1)
255 			goto errlab;
256 		p += n;
257 		left -= n;
258 	}
259 	if (write(wtmp_fd, &record->reclen, sizeof(record->reclen)) !=
260 	    sizeof(wtmp->reclen))
261 		goto errlab;
262 
263 	rushdb_unlock(wtmp_fd, record->reclen, off, SEEK_SET);
264         wtmp_recsize = record->reclen;
265 	free(record);
266 	return off;
267 
268   errlab:
269 	rushdb_unlock(wtmp_fd, record->reclen, off, SEEK_SET);
270 	rush_wtmp_close();
271 	return -1;
272 }
273 
274 int
rush_wtmp_update(struct timeval * tv)275 rush_wtmp_update(struct timeval *tv)
276 {
277 	struct rush_wtmp wtmp;
278 	if (lseek(wtmp_fd, - (wtmp_recsize + sizeof(size_t)), SEEK_CUR) == -1)
279 		return 1;
280 	if (read(wtmp_fd, &wtmp, sizeof wtmp) != sizeof wtmp)
281 		return 1;
282 	if (lseek(wtmp_fd, - sizeof(wtmp), SEEK_CUR) == -1)
283 		return 1;
284 	wtmp.stop = *tv;
285 	return write(wtmp_fd, &wtmp, sizeof wtmp) != sizeof wtmp;
286 }
287