]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - lib/libc/gen/pututxline.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / lib / libc / gen / pututxline.c
1 /*-
2  * Copyright (c) 2010 Ed Schouten <ed@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include "namespace.h"
31 #include <sys/endian.h>
32 #include <sys/stat.h>
33 #include <sys/uio.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <utmpx.h>
40 #include "utxdb.h"
41 #include "un-namespace.h"
42
43 static FILE *
44 futx_open(const char *file)
45 {
46         FILE *fp;
47         struct stat sb;
48         int fd;
49
50         fd = _open(file, O_CREAT|O_RDWR|O_EXLOCK, 0644);
51         if (fd < 0)
52                 return (NULL);
53
54         /* Safety check: never use broken files. */
55         if (_fstat(fd, &sb) != -1 && sb.st_size % sizeof(struct futx) != 0) {
56                 _close(fd);
57                 errno = EFTYPE;
58                 return (NULL);
59         }
60
61         fp = fdopen(fd, "r+");
62         if (fp == NULL) {
63                 _close(fd);
64                 return (NULL);
65         }
66         return (fp);
67 }
68
69 static int
70 utx_active_add(const struct futx *fu)
71 {
72         FILE *fp;
73         struct futx fe;
74         off_t partial;
75         int error, ret;
76
77         partial = -1;
78         ret = 0;
79
80         /*
81          * Register user login sessions.  Overwrite entries of sessions
82          * that have already been terminated.
83          */
84         fp = futx_open(_PATH_UTX_ACTIVE);
85         if (fp == NULL)
86                 return (-1);
87         while (fread(&fe, sizeof(fe), 1, fp) == 1) {
88                 switch (fe.fu_type) {
89                 case USER_PROCESS:
90                 case INIT_PROCESS:
91                 case LOGIN_PROCESS:
92                 case DEAD_PROCESS:
93                         /* Overwrite when ut_id matches. */
94                         if (memcmp(fu->fu_id, fe.fu_id, sizeof(fe.fu_id)) ==
95                             0) {
96                                 ret = fseeko(fp, -(off_t)sizeof(fe), SEEK_CUR);
97                                 goto exact;
98                         }
99                         if (fe.fu_type != DEAD_PROCESS)
100                                 break;
101                         /* FALLTHROUGH */
102                 default:
103                         /* Allow us to overwrite unused records. */
104                         if (partial == -1) {
105                                 partial = ftello(fp);
106                                 /*
107                                  * Distinguish errors from valid values so we
108                                  * don't overwrite good data by accident.
109                                  */
110                                 if (partial != -1)
111                                         partial -= (off_t)sizeof(fe);
112                         }
113                         break;
114                 }
115         }
116
117         /*
118          * No exact match found.  Use the partial match.  If no partial
119          * match was found, just append a new record.
120          */
121         if (partial != -1)
122                 ret = fseeko(fp, partial, SEEK_SET);
123 exact:
124         if (ret == -1)
125                 error = errno;
126         else if (fwrite(fu, sizeof(*fu), 1, fp) < 1)
127                 error = errno;
128         else
129                 error = 0;
130         fclose(fp);
131         errno = error;
132         return (error == 0 ? 0 : 1);
133 }
134
135 static int
136 utx_active_remove(struct futx *fu)
137 {
138         FILE *fp;
139         struct futx fe;
140         int error, ret;
141
142         /*
143          * Remove user login sessions, having the same ut_id.
144          */
145         fp = futx_open(_PATH_UTX_ACTIVE);
146         if (fp == NULL)
147                 return (-1);
148         error = ESRCH;
149         ret = -1;
150         while (fread(&fe, sizeof(fe), 1, fp) == 1 && ret != 0)
151                 switch (fe.fu_type) {
152                 case USER_PROCESS:
153                 case INIT_PROCESS:
154                 case LOGIN_PROCESS:
155                         if (memcmp(fu->fu_id, fe.fu_id, sizeof(fe.fu_id)) != 0)
156                                 continue;
157
158                         /* Terminate session. */
159                         if (fseeko(fp, -(off_t)sizeof(fe), SEEK_CUR) == -1)
160                                 error = errno;
161                         else if (fwrite(fu, sizeof(*fu), 1, fp) < 1)
162                                 error = errno;
163                         else
164                                 ret = 0;
165
166                 }
167
168         fclose(fp);
169         errno = error;
170         return (ret);
171 }
172
173 static void
174 utx_active_purge(void)
175 {
176
177         truncate(_PATH_UTX_ACTIVE, 0);
178 }
179
180 static int
181 utx_lastlogin_add(const struct futx *fu)
182 {
183         struct futx fe;
184         FILE *fp;
185         int error, ret;
186
187         ret = 0;
188
189         /*
190          * Write an entry to lastlogin.  Overwrite the entry if the
191          * current user already has an entry.  If not, append a new
192          * entry.
193          */
194         fp = futx_open(_PATH_UTX_LASTLOGIN);
195         if (fp == NULL)
196                 return (-1);
197         while (fread(&fe, sizeof fe, 1, fp) == 1) {
198                 if (strncmp(fu->fu_user, fe.fu_user, sizeof fe.fu_user) != 0)
199                         continue;
200
201                 /* Found a previous lastlogin entry for this user. */
202                 ret = fseeko(fp, -(off_t)sizeof fe, SEEK_CUR);
203                 break;
204         }
205         if (ret == -1)
206                 error = errno;
207         else if (fwrite(fu, sizeof *fu, 1, fp) < 1) {
208                 error = errno;
209                 ret = -1;
210         }
211         fclose(fp);
212         errno = error;
213         return (ret);
214 }
215
216 static void
217 utx_lastlogin_upgrade(void)
218 {
219         struct stat sb;
220         int fd;
221
222         fd = _open(_PATH_UTX_LASTLOGIN, O_RDWR, 0644);
223         if (fd < 0)
224                 return;
225
226         /*
227          * Truncate broken lastlogin files.  In the future we should
228          * check for older versions of the file format here and try to
229          * upgrade it.
230          */
231         if (_fstat(fd, &sb) != -1 && sb.st_size % sizeof(struct futx) != 0)
232                 ftruncate(fd, 0);
233         _close(fd);
234 }
235
236 static int
237 utx_log_add(const struct futx *fu)
238 {
239         struct iovec vec[2];
240         int error, fd;
241         uint16_t l;
242
243         /*
244          * Append an entry to the log file.  We only need to append
245          * records to this file, so to conserve space, trim any trailing
246          * zero-bytes.  Prepend a length field, indicating the length of
247          * the record, excluding the length field itself.
248          */
249         for (l = sizeof(*fu); l > 0 && ((const char *)fu)[l - 1] == '\0'; l--) ;
250         vec[0].iov_base = &l;
251         vec[0].iov_len = sizeof(l);
252         vec[1].iov_base = __DECONST(void *, fu);
253         vec[1].iov_len = l;
254         l = htobe16(l);
255
256         fd = _open(_PATH_UTX_LOG, O_CREAT|O_WRONLY|O_APPEND, 0644);
257         if (fd < 0)
258                 return (-1);
259         if (_writev(fd, vec, 2) == -1)
260                 error = errno;
261         else
262                 error = 0;
263         _close(fd);
264         errno = error;
265         return (error == 0 ? 0 : 1);
266 }
267
268 struct utmpx *
269 pututxline(const struct utmpx *utmpx)
270 {
271         struct futx fu;
272         int bad;
273
274         bad = 0;
275
276         utx_to_futx(utmpx, &fu);
277
278         switch (fu.fu_type) {
279         case BOOT_TIME:
280         case SHUTDOWN_TIME:
281                 utx_active_purge();
282                 utx_lastlogin_upgrade();
283                 break;
284         case OLD_TIME:
285         case NEW_TIME:
286                 break;
287         case USER_PROCESS:
288                 bad |= utx_active_add(&fu);
289                 bad |= utx_lastlogin_add(&fu);
290                 break;
291 #if 0 /* XXX: Are these records of any use to us? */
292         case INIT_PROCESS:
293         case LOGIN_PROCESS:
294                 bad |= utx_active_add(&fu);
295                 break;
296 #endif
297         case DEAD_PROCESS:
298                 /*
299                  * In case writing a logout entry fails, never attempt
300                  * to write it to utx.log.  The logout entry's ut_id
301                  * might be invalid.
302                  */
303                 if (utx_active_remove(&fu) != 0)
304                         return (NULL);
305                 break;
306         default:
307                 errno = EINVAL;
308                 return (NULL);
309         }
310
311         bad |= utx_log_add(&fu);
312         return (bad ? NULL : futx_to_utx(&fu));
313 }