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