3b41791fef75198d62e676fb3b3e0c7c803e2b5f
[dragonfly.git] / lib / libc / gen / utmpx.c
1 /*-
2  * Copyright (c) 2002 The NetBSD Foundation, Inc.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to The NetBSD Foundation
6  * by Christos Zoulas.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 #include <sys/cdefs.h>
30
31 #include "namespace.h"
32 #include <sys/types.h>
33 #include <sys/param.h>
34 #include <sys/socket.h>
35 #include <sys/stat.h>
36 #include <sys/time.h>
37 #include <sys/wait.h>
38
39 #include <assert.h>
40 #include <db.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 #include <pwd.h>
48 #include <utmp.h>
49 #include <utmpx.h>
50 #include <vis.h>
51
52 static FILE *fp = NULL;
53 static int readonly = 0;
54 static struct utmpx ut;
55 static char utfile[MAXPATHLEN] = _PATH_UTMPX;
56
57 static struct utmpx *utmp_update(const struct utmpx *);
58
59 static const char vers[] = "utmpx-2.00";
60 static utx_db_t dbtype = UTX_DB_UTMPX;
61 DB *lastlogx_db = NULL;
62
63 static int
64 _open_db(char *fname)
65 {
66         struct stat st;
67
68         if ((fp = fopen(fname, "r+")) == NULL)
69                 if ((fp = fopen(fname, "w+")) == NULL) {
70                         if ((fp = fopen(fname, "r")) == NULL)
71                                 goto fail;
72                         else
73                                 readonly = 1;
74                 }
75
76         /* get file size in order to check if new file */
77         if (fstat(fileno(fp), &st) == -1)
78                 goto failclose;
79
80         if (st.st_size == 0) {
81                 /* new file, add signature record */
82                 (void)memset(&ut, 0, sizeof(ut));
83                 ut.ut_type = SIGNATURE;
84                 (void)memcpy(ut.ut_user, vers, sizeof(vers));
85                 if (fwrite(&ut, sizeof(ut), 1, fp) != 1)
86                         goto failclose;
87         } else {
88                 /* old file, read signature record */
89                 if (fread(&ut, sizeof(ut), 1, fp) != 1)
90                         goto failclose;
91                 if (memcmp(ut.ut_user, vers, 5) != 0 ||
92                     ut.ut_type != SIGNATURE) {
93                         errno = EINVAL;
94                         goto failclose;
95                 }
96         }
97
98         return 0;
99 failclose:
100         (void)fclose(fp);
101 fail:
102         (void)memset(&ut, 0, sizeof(ut));
103         return -1;
104 }
105
106 int
107 setutxdb(utx_db_t db_type, char *fname)
108 {
109         switch (db_type) {
110         case UTX_DB_UTMPX:
111                 if (fname == NULL)
112                         fname = _PATH_UTMPX;
113                 break;
114
115         case UTX_DB_WTMPX:
116                 if (fname == NULL)
117                         fname = _PATH_WTMPX;
118                 break;
119
120         default:
121                 errno = EINVAL;
122                 return -1;
123         }
124
125         /* A previous db file is still open */
126         if (fp != NULL) {
127                 fclose(fp);
128                 fp = NULL;
129         }
130         if ((_open_db(fname)) == -1)
131                 return -1;
132
133 done:
134         dbtype = db_type;
135         return 0;
136 }
137
138 void
139 setutxent()
140 {
141         (void)memset(&ut, 0, sizeof(ut));
142         if (fp == NULL)
143                 return;
144
145 #if 0
146         if (dbtype != UTX_DB_UTMPX)
147                 setutxdb(UTX_DB_UTMPX, utfile);
148 #endif
149         (void)fseeko(fp, (off_t)sizeof(ut), SEEK_SET);
150 }
151
152 void
153 endutxent()
154 {
155         (void)memset(&ut, 0, sizeof(ut));
156
157         if (fp != NULL) {
158                 (void)fclose(fp);
159                 fp = NULL;
160                 readonly = 0;
161         }
162 }
163
164 struct utmpx *
165 getutxent()
166 {
167         if (fp == NULL) {
168                 if ((_open_db(utfile)) == -1)
169                         goto fail;
170         }
171
172         if (fread(&ut, sizeof(ut), 1, fp) != 1)
173                 goto fail;
174
175         return &ut;
176 fail:
177         (void)memset(&ut, 0, sizeof(ut));
178         return NULL;
179 }
180
181 struct utmpx *
182 getutxid(const struct utmpx *utx)
183 {
184
185         _DIAGASSERT(utx != NULL);
186
187         if (utx->ut_type == EMPTY)
188                 return NULL;
189
190         do {
191                 if (ut.ut_type == EMPTY)
192                         continue;
193                 switch (utx->ut_type) {
194                 case EMPTY:
195                         return NULL;
196                 case RUN_LVL:
197                 case BOOT_TIME:
198                 case OLD_TIME:
199                 case NEW_TIME:
200                         if (ut.ut_type == utx->ut_type)
201                                 return &ut;
202                         break;
203                 case INIT_PROCESS:
204                 case LOGIN_PROCESS:
205                 case USER_PROCESS:
206                 case DEAD_PROCESS:
207                         switch (ut.ut_type) {
208                         case INIT_PROCESS:
209                         case LOGIN_PROCESS:
210                         case USER_PROCESS:
211                         case DEAD_PROCESS:
212                                 if (memcmp(ut.ut_id, utx->ut_id,
213                                     sizeof(ut.ut_id)) == 0)
214                                         return &ut;
215                                 break;
216                         default:
217                                 break;
218                         }
219                         break;
220                 default:
221                         return NULL;
222                 }
223         } while (getutxent() != NULL);
224         return NULL;
225 }
226
227 struct utmpx *
228 getutxline(const struct utmpx *utx)
229 {
230
231         _DIAGASSERT(utx != NULL);
232
233         do {
234                 switch (ut.ut_type) {
235                 case EMPTY:
236                         break;
237                 case LOGIN_PROCESS:
238                 case USER_PROCESS:
239                         if (strncmp(ut.ut_line, utx->ut_line,
240                             sizeof(ut.ut_line)) == 0)
241                                 return &ut;
242                         break;
243                 default:
244                         break;
245                 }
246         } while (getutxent() != NULL);
247         return NULL;
248 }
249
250 struct utmpx *
251 pututxline(const struct utmpx *utx)
252 {
253         struct passwd *pw;
254         struct lastlogx ll;
255         struct utmpx temp, *u = NULL;
256         int gotlock = 0;
257
258         _DIAGASSERT(utx != NULL);
259
260         if (utx == NULL)
261                 return NULL;
262
263         if (utx->ut_type == USER_PROCESS) {
264                 ll.ll_tv = utx->ut_tv;
265                 strcpy(ll.ll_host, utx->ut_host);
266                 strcpy(ll.ll_line, utx->ut_line);
267                 pw = getpwnam(utx->ut_name);
268                 if (pw != NULL)
269                         updlastlogx(_PATH_LASTLOGX, pw->pw_uid, &ll);
270         }
271
272         if (strcmp(_PATH_UTMPX, utfile) == 0)
273                 if ((fp != NULL && readonly) || (fp == NULL && geteuid() != 0))
274                         return utmp_update(utx);
275
276
277         (void)memcpy(&temp, utx, sizeof(temp));
278
279         if (fp == NULL) {
280                 (void)getutxent();
281                 if (fp == NULL || readonly)
282                         return NULL;
283         }
284
285         if (getutxid(&temp) == NULL) {
286                 setutxent();
287                 if (getutxid(&temp) == NULL) {
288                         if (lockf(fileno(fp), F_LOCK, (off_t)0) == -1)
289                                 return NULL;
290                         gotlock++;
291                         if (fseeko(fp, (off_t)0, SEEK_END) == -1)
292                                 goto fail;
293                 }
294         }
295
296         if (!gotlock) {
297                 /* we are not appending */
298                 if (fseeko(fp, -(off_t)sizeof(ut), SEEK_CUR) == -1)
299                         return NULL;
300         }
301
302         if (fwrite(&temp, sizeof (temp), 1, fp) != 1)
303                 goto fail;
304
305         if (fflush(fp) == -1)
306                 goto fail;
307
308         u = memcpy(&ut, &temp, sizeof(ut));
309 fail:
310         if (gotlock) {
311                 if (lockf(fileno(fp), F_ULOCK, (off_t)0) == -1)
312                         return NULL;
313         }
314         return u;
315 }
316
317 static struct utmpx *
318 utmp_update(const struct utmpx *utx)
319 {
320         char buf[sizeof(*utx) * 4 + 1];
321         pid_t pid;
322         int status;
323
324         _DIAGASSERT(utx != NULL);
325
326         (void)strvisx(buf, (const char *)(const void *)utx, sizeof(*utx),
327             VIS_WHITE);
328         switch (pid = fork()) {
329         case 0:
330                 (void)execl(_PATH_UTMP_UPDATE,
331                     strrchr(_PATH_UTMP_UPDATE, '/') + 1, buf, NULL);
332                 _exit(1);
333                 /*NOTREACHED*/
334         case -1:
335                 return NULL;
336         default:
337                 if (waitpid(pid, &status, 0) == -1)
338                         return NULL;
339                 if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
340                         return memcpy(&ut, utx, sizeof(ut));
341                 return NULL;
342         }
343
344 }
345
346 /*
347  * The following are extensions and not part of the X/Open spec.
348  */
349 int
350 updwtmpx(const char *file, const struct utmpx *utx)
351 {
352         int fd;
353         int saved_errno;
354
355         _DIAGASSERT(file != NULL);
356         _DIAGASSERT(utx != NULL);
357
358         fd = open(file, O_WRONLY|O_APPEND|O_SHLOCK);
359
360         if (fd == -1) {
361                 if ((fd = open(file, O_CREAT|O_WRONLY|O_EXLOCK, 0644)) == -1)
362                         return -1;
363                 (void)memset(&ut, 0, sizeof(ut));
364                 ut.ut_type = SIGNATURE;
365                 (void)memcpy(ut.ut_user, vers, sizeof(vers));
366                 if (write(fd, &ut, sizeof(ut)) == -1)
367                         goto failed;
368         }
369         if (write(fd, utx, sizeof(*utx)) == -1)
370                 goto failed;
371         if (close(fd) == -1)
372                 return -1;
373         return 0;
374
375   failed:
376         saved_errno = errno;
377         (void) close(fd);
378         errno = saved_errno;
379         return -1;
380 }
381
382 int
383 utmpxname(const char *fname)
384 {
385         size_t len;
386
387         _DIAGASSERT(fname != NULL);
388
389         len = strlen(fname);
390
391         if (len >= sizeof(utfile))
392                 return 0;
393
394         /* must end in x! */
395         if (fname[len - 1] != 'x')
396                 return 0;
397
398         (void)strlcpy(utfile, fname, sizeof(utfile));
399         endutxent();
400         return 1;
401 }
402
403 void
404 getutmp(const struct utmpx *ux, struct utmp *u)
405 {
406
407         _DIAGASSERT(ux != NULL);
408         _DIAGASSERT(u != NULL);
409
410         (void)memcpy(u->ut_name, ux->ut_name, sizeof(u->ut_name));
411         (void)memcpy(u->ut_line, ux->ut_line, sizeof(u->ut_line));
412         (void)memcpy(u->ut_host, ux->ut_host, sizeof(u->ut_host));
413         u->ut_time = ux->ut_tv.tv_sec;
414 }
415
416 void
417 getutmpx(const struct utmp *u, struct utmpx *ux)
418 {
419
420         _DIAGASSERT(ux != NULL);
421         _DIAGASSERT(u != NULL);
422
423         (void)memcpy(ux->ut_name, u->ut_name, sizeof(u->ut_name));
424         (void)memcpy(ux->ut_line, u->ut_line, sizeof(u->ut_line));
425         (void)memcpy(ux->ut_host, u->ut_host, sizeof(u->ut_host));
426         ux->ut_tv.tv_sec = u->ut_time;
427         ux->ut_tv.tv_usec = 0;
428         (void)memset(&ux->ut_ss, 0, sizeof(ux->ut_ss));
429         ux->ut_pid = 0;
430         ux->ut_type = USER_PROCESS;
431         ux->ut_session = 0;
432         ux->ut_exit.e_termination = 0;
433         ux->ut_exit.e_exit = 0;
434 }
435
436 struct lastlogx *
437 getlastlogx(const char *fname, uid_t uid, struct lastlogx *ll)
438 {
439         DBT key, data;
440         DB *db;
441
442         _DIAGASSERT(fname != NULL);
443         _DIAGASSERT(ll != NULL);
444
445         db = dbopen(fname, O_RDONLY|O_SHLOCK, 0, DB_HASH, NULL);
446
447         if (db == NULL)
448                 return NULL;
449
450         key.data = &uid;
451         key.size = sizeof(uid);
452
453         if ((db->get)(db, &key, &data, 0) != 0)
454                 goto error;
455
456         if (data.size != sizeof(*ll)) {
457                 errno = EFTYPE;
458                 goto error;
459         }
460
461         if (ll == NULL)
462                 if ((ll = malloc(sizeof(*ll))) == NULL)
463                         goto done;
464
465         (void)memcpy(ll, data.data, sizeof(*ll));
466         goto done;
467 error:
468         ll = NULL;
469 done:
470         (db->close)(db);
471         return ll;
472 }
473
474 int
475 updlastlogx(const char *fname, uid_t uid, struct lastlogx *ll)
476 {
477         DBT key, data;
478         int error = 0;
479         DB *db;
480
481         _DIAGASSERT(fname != NULL);
482         _DIAGASSERT(ll != NULL);
483
484         db = dbopen(fname, O_RDWR|O_CREAT|O_EXLOCK, 0644, DB_HASH, NULL);
485
486         if (db == NULL)
487                 return -1;
488
489         key.data = &uid;
490         key.size = sizeof(uid);
491         data.data = ll;
492         data.size = sizeof(*ll);
493         if ((db->put)(db, &key, &data, 0) != 0)
494                 error = -1;
495
496         (db->close)(db);
497         return error;
498 }