diff --git a/lib/libulog/Makefile b/lib/libulog/Makefile index f8cd91b21df8..4c3ac9308537 100644 --- a/lib/libulog/Makefile +++ b/lib/libulog/Makefile @@ -3,15 +3,25 @@ LIB= ulog SHLIB_MAJOR= 0 INCS= ulog.h -SRCS= ulog.h ulog_getutxent.c ulog_internal.h \ - ulog_login.c ulog_login_pseudo.c +SRCS= ulog.h ulog_getutxent.c ulog_internal.h ulog_login.c \ + ulog_login_pseudo.c ulog_pututxline.c ulog_util.c -MAN= ulog_getutxent.3 ulog_login.3 +MAN= ulog_getutxent.3 ulog_login.3 ulog_setutxfile.3 MLINKS+=ulog_getutxent.3 ulog_endutxent.3 \ + ulog_getutxent.3 ulog_getutxline.3 \ + ulog_getutxent.3 ulog_pututxline.3 \ ulog_getutxent.3 ulog_setutxent.3 \ ulog_login.3 ulog_login_pseudo.3 \ ulog_login.3 ulog_logout.3 \ - ulog_login.3 ulog_logout_pseudo.3 + ulog_login.3 ulog_logout_pseudo.3 \ + ulog_setutxfile.3 ulog_getutxuser.3 + +# Add links to -style functions. +MLINKS+=ulog_endutxent.3 endutxent.3 \ + ulog_getutxent.3 getutxent.3 \ + ulog_getutxline.3 getutxline.3 \ + ulog_pututxline.3 pututxline.3 \ + ulog_setutxent.3 setutxent.3 WARNS?= 6 diff --git a/lib/libulog/Symbol.map b/lib/libulog/Symbol.map index cd6dd00c2b8b..f435c9cb763a 100644 --- a/lib/libulog/Symbol.map +++ b/lib/libulog/Symbol.map @@ -5,9 +5,13 @@ FBSD_1.2 { ulog_endutxent; ulog_getutxent; + ulog_getutxline; + ulog_getutxuser; ulog_login; ulog_login_pseudo; ulog_logout; ulog_logout_pseudo; + ulog_pututxline; ulog_setutxent; + ulog_setutxfile; }; diff --git a/lib/libulog/ulog.h b/lib/libulog/ulog.h index 2551f918d1f5..1e45aed3569c 100644 --- a/lib/libulog/ulog.h +++ b/lib/libulog/ulog.h @@ -31,6 +31,12 @@ #include #include +#include + +#ifndef _PID_T_DECLARED +typedef __pid_t pid_t; +#define _PID_T_DECLARED +#endif /* * libulog. @@ -42,61 +48,61 @@ * processes as well, provided that they hold a file descriptor to a * pseudo-terminal master device. * - * Unlike struct utmpx, the buffers containing the strings are not - * stored inside struct ulog_utmpx itself. Processes should never - * handcraft these structures anyway. - * * This library (or at least parts of it) will hopefully deprecate over * time, when we provide the API. */ -#define _UTX_USERDISPSIZE 16 -#define _UTX_LINEDISPSIZE 8 -#define _UTX_HOSTDISPSIZE 16 - struct ulog_utmpx { - char *ut_user; -#if 0 - char *ut_id; -#endif - char *ut_line; - char *ut_host; -#if 0 - pid_t ut_pid; -#endif - short ut_type; + char ut_user[32]; + char ut_id[8]; /* XXX: unsupported. */ + char ut_line[32]; + char ut_host[256]; + pid_t ut_pid; /* XXX: unsupported. */ + short ut_type; #define EMPTY 0 -#if 0 #define BOOT_TIME 1 -#endif #define OLD_TIME 2 #define NEW_TIME 3 #define USER_PROCESS 4 -#if 0 -#define INIT_PROCESS 5 -#define LOGIN_PROCESS 6 -#endif +#define INIT_PROCESS 5 /* XXX: unsupported. */ +#define LOGIN_PROCESS 6 /* XXX: unsupported. */ #define DEAD_PROCESS 7 - #define SHUTDOWN_TIME 8 -#define REBOOT_TIME 9 - struct timeval ut_tv; + struct timeval ut_tv; }; __BEGIN_DECLS +/* POSIX routines. */ void ulog_endutxent(void); struct ulog_utmpx *ulog_getutxent(void); #if 0 -struct ulog_utmpx *ulog_getutxid(const struct ulog_utmpx *id); -struct ulog_utmpx *ulog_getutxline(const struct ulog_utmpx *line); -struct ulog_utmpx *ulog_pututxline(const struct ulog_utmpx *utmpx); +struct ulog_utmpx *ulog_getutxid(const struct ulog_utmpx *); #endif +struct ulog_utmpx *ulog_getutxline(const struct ulog_utmpx *); +struct ulog_utmpx *ulog_pututxline(const struct ulog_utmpx *); void ulog_setutxent(void); +/* Extensions. */ +struct ulog_utmpx *ulog_getutxuser(const char *); +int ulog_setutxfile(int, const char *); +#define UTXF_UTMP 0 +#define UTXF_WTMP 1 +#define UTXF_LASTLOG 2 + +/* Login/logout utility functions. */ void ulog_login(const char *, const char *, const char *); void ulog_login_pseudo(int, const char *); void ulog_logout(const char *); void ulog_logout_pseudo(int); __END_DECLS +#ifdef _ULOG_POSIX_NAMES +#define utmpx ulog_utmpx +#define endutxent ulog_endutxent +#define getutxent ulog_getutxent +#define getutxline ulog_getutxline +#define pututxline ulog_pututxline +#define setutxent ulog_setutxent +#endif /* _ULOG_POSIX_NAMES */ + #endif /* !_ULOG_H_ */ diff --git a/lib/libulog/ulog_getutxent.3 b/lib/libulog/ulog_getutxent.3 index 5960a90f0417..99c1ea173805 100644 --- a/lib/libulog/ulog_getutxent.3 +++ b/lib/libulog/ulog_getutxent.3 @@ -24,11 +24,13 @@ .\" .\" $FreeBSD$ .\" -.Dd December 2, 2009 +.Dd December 5, 2009 .Os .Dt ULOG_GETUTXENT 3 .Sh NAME .Nm ulog_getutxent , +.Nm ulog_getutxline , +.Nm ulog_pututxline , .Nm ulog_setutxent , .Nm ulog_endutxent .Nd read user login records @@ -38,6 +40,10 @@ .In ulog.h .Ft struct ulog_utmpx * .Fn ulog_getutxent "void" +.Ft struct ulog_utmpx * +.Fn ulog_getutxline "const struct ulog_utmpx *line" +.Ft struct ulog_utmpx * +.Fn ulog_pututxline "const struct ulog_utmpx *utmpx" .Ft void .Fn ulog_setutxent "void" .Ft void @@ -49,10 +55,12 @@ function returns a pointer to an object, with the following structure, containing stored information of an active user login session. .Bd -literal struct ulog_utmpx { - char *ut_user; /* Username. */ - char *ut_line; /* TTY device. */ - char *ut_host; /* Remote hostname. */ - short ut_type; /* Type of entry. */ + char ut_user[]; /* Username. */ + char ut_id[]; /* Private data. */ + char ut_line[]; /* TTY device. */ + char ut_host[]; /* Remote hostname. */ + pid_t ut_pid; /* Process identifier. */ + short ut_type; /* Type of entry. */ struct timeval ut_tv; /* Timestamp. */ }; .Ed @@ -61,6 +69,9 @@ The fields are as follows: .Bl -tag -width ut_user .It Fa ut_user The username of the logged in user. +.It Fa ut_id +Private data that can be used to later identify the record. +This implementation is not capable of storing this value on disk. .It Fa ut_line The pathname of the TTY device, without the leading .Pa /dev/ @@ -68,6 +79,9 @@ directory. .It Fa ut_host An optional hostname of a remote system, if the login session is provided through a networked login service. +.It Fa ut_pid +Process identifier of the session leader of the login session. +This implementation is not capable of storing this value on disk. .It Fa ut_type The .Fa ut_type @@ -76,28 +90,42 @@ following values: .Bl -tag -width SHUTDOWN_TIME .It Dv EMPTY No valid user accounting information. +.It Dv BOOT_TIME +Identifies time of system boot. .It Dv OLD_TIME Identifies time when system clock changed. .It Dv NEW_TIME Identifies time after system clock changed. .It Dv USER_PROCESS Identifies a process. +.It Dv INIT_PROCESS +Identifies a process spawned by the init process. +.It Dv LOGIN_PROCESS +Identifies the session leader of a logged-in user. .It Dv DEAD_PROCESS Identifies a session leader who has exited. .It Dv SHUTDOWN_TIME Identifies time when system was shut down. -.It Dv REBOOT_TIME -Identifies time when system was rebooted. .El .It Fa ut_tv Timestamp indicating when the entry was last modified. .El .Pp +This implementation guarantees all strings returned in the structure to +be null terminated. +.Pp The .Fn ulog_getutxent function reads the next entry from the utmp file, opening the file if necessary. The +.Fn ulog_getutxline +function reads entries from the utmp file, until finding an entry which +shares the same +.Fa ut_line +as the structure +.Fa line . +The .Fn ulog_setutxent opens the file, closing it first if already opened. The @@ -106,15 +134,50 @@ function closes any open files. .Pp The .Fn ulog_getutxent -function reads from the beginning of the file until and EOF is +and +.Fn ulog_getutxline +functions read from the beginning of the file until and EOF is encountered. +.Pp +The +.Fn ulog_pututxline +function writes a new entry to the file. .Sh RETURN VALUES The .Fn ulog_getutxent -function returns a null pointer on EOF or error. +and +.Fn ulog_getutxline +functions return a null pointer on EOF or error. .Sh SEE ALSO .Xr ulog_login 3 , +.Xr ulog_setutxfile 3 , .Xr utmp 5 +.Sh STANDARDS +This interface is similar to +.In utmpx.h +described in +.St -p1003.1-2008 , +but incompatible. +The underlying file format does not allow a correctly behaving +implementation of the standardized interface. +.Pp +This programming interface has been designed to ease the migration +towards +.In utmpx.h . +If +.Dv _ULOG_POSIX_NAMES +is set before inclusion of +.In ulog.h , +it is also possible to use the +.Vt utmpx +structure and the +.Fn getutxent , +.Fn getutxline , +.Fn pututxline , +.Fn setutxent +and +.Fn endutxent +functions. .Sh HISTORY These functions appeared in .Fx 9.0 . diff --git a/lib/libulog/ulog_getutxent.c b/lib/libulog/ulog_getutxent.c index 1dd464d56767..7ffc73913977 100644 --- a/lib/libulog/ulog_getutxent.c +++ b/lib/libulog/ulog_getutxent.c @@ -27,6 +27,9 @@ #include __FBSDID("$FreeBSD$"); +#include + +#include #include #include #include @@ -35,6 +38,7 @@ __FBSDID("$FreeBSD$"); #include "ulog_internal.h" static FILE *ufile; +static int ufiletype = -1; void ulog_endutxent(void) @@ -44,56 +48,255 @@ ulog_endutxent(void) ufile = NULL; } -struct ulog_utmpx * -ulog_getutxent(void) +/* + * Conversion from on-disk formats to generic ulog_utmpx structure. + */ + +static void +ulog_futmp_to_utmpx(const struct futmp *ut, struct ulog_utmpx *utx) +{ + + memset(utx, 0, sizeof *utx); +#define COPY_STRING(field) do { \ + strncpy(utx->ut_ ## field, ut->ut_ ## field, \ + MIN(sizeof utx->ut_ ## field - 1, sizeof ut->ut_ ## field));\ +} while (0) + COPY_STRING(user); + COPY_STRING(line); + COPY_STRING(host); +#undef COPY_STRING +#define MATCH(field, value) (strcmp(utx->ut_ ## field, (value)) == 0) + if (MATCH(user, "reboot") && MATCH(line, "~")) + utx->ut_type = BOOT_TIME; + else if (MATCH(user, "date") && MATCH(line, "|")) + utx->ut_type = OLD_TIME; + else if (MATCH(user, "date") && MATCH(line, "{")) + utx->ut_type = NEW_TIME; + else if (MATCH(user, "shutdown") && MATCH(line, "~")) + utx->ut_type = SHUTDOWN_TIME; + else if (MATCH(user, "") && MATCH(host, "")) + utx->ut_type = DEAD_PROCESS; + else if (!MATCH(user, "") && !MATCH(line, "") && ut->ut_time != 0) + utx->ut_type = USER_PROCESS; + else + utx->ut_type = EMPTY; + utx->ut_tv.tv_sec = _time32_to_time(ut->ut_time); + utx->ut_tv.tv_usec = 0; +} + +static void +ulog_flastlog_to_utmpx(const struct flastlog *ll, struct ulog_utmpx *utx) +{ + + memset(utx, 0, sizeof *utx); +#define COPY_STRING(field) do { \ + strncpy(utx->ut_ ## field, ll->ll_ ## field, \ + MIN(sizeof utx->ut_ ## field - 1, sizeof ll->ll_ ## field));\ +} while (0) + COPY_STRING(line); + COPY_STRING(host); +#undef COPY_STRING + if (!MATCH(line, "") && ll->ll_time != 0) + utx->ut_type = USER_PROCESS; + else + utx->ut_type = EMPTY; + utx->ut_tv.tv_sec = _time32_to_time(ll->ll_time); + utx->ut_tv.tv_usec = 0; +} + +/* + * File I/O. + */ + +static inline off_t +ulog_tell(void) +{ + + if (ufiletype == UTXF_LASTLOG) + return (ftello(ufile) / sizeof(struct flastlog)); + else + return (ftello(ufile) / sizeof(struct futmp)); +} + +static struct ulog_utmpx * +ulog_read(off_t off, int whence, int resolve_user) { - struct futmp ut; static struct ulog_utmpx utx; - /* Open the utmp file if not already done so. */ if (ufile == NULL) ulog_setutxent(); if (ufile == NULL) return (NULL); - if (fread(&ut, sizeof ut, 1, ufile) != 1) + /* Only allow seeking to move forward. */ + if (whence == SEEK_SET && ulog_tell() > off) return (NULL); -#define COPY_STRING(field) do { \ - free(utx.ut_ ## field); \ - utx.ut_ ## field = strndup(ut.ut_ ## field, \ - sizeof ut.ut_ ## field); \ - if (utx.ut_ ## field == NULL) \ - utx.ut_ ## field = __DECONST(char *, ""); \ -} while (0) - COPY_STRING(user); - COPY_STRING(line); - COPY_STRING(host); - utx.ut_tv.tv_sec = _time32_to_time(ut.ut_time); - utx.ut_tv.tv_usec = 0; -#define MATCH(field, value) (strcmp(utx.ut_ ## field, (value)) == 0) - if (MATCH(user, "date") && MATCH(line, "|")) - utx.ut_type = OLD_TIME; - else if (MATCH(user, "date") && MATCH(line, "{")) - utx.ut_type = NEW_TIME; - else if (MATCH(user, "shutdown") && MATCH(line, "~")) - utx.ut_type = SHUTDOWN_TIME; - else if (MATCH(user, "reboot") && MATCH(line, "~")) - utx.ut_type = REBOOT_TIME; - else if (MATCH(user, "") && MATCH(host, "")) - utx.ut_type = DEAD_PROCESS; - else if (!MATCH(user, "")) - utx.ut_type = USER_PROCESS; - else - utx.ut_type = EMPTY; - + + if (ufiletype == UTXF_LASTLOG) { + struct flastlog ll; + struct passwd *pw = NULL; + uid_t uid; + + if (fseeko(ufile, off * sizeof ll, whence) != 0) + return (NULL); + uid = ulog_tell(); + if (fread(&ll, sizeof ll, 1, ufile) != 1) + return (NULL); + ulog_flastlog_to_utmpx(&ll, &utx); + if (utx.ut_type == USER_PROCESS && resolve_user) + pw = getpwuid(uid); + if (pw != NULL) + strlcpy(utx.ut_user, pw->pw_name, sizeof utx.ut_user); + else + sprintf(utx.ut_user, "%u", (unsigned int)uid); + } else { + struct futmp ut; + + if (fseeko(ufile, off * sizeof(struct futmp), whence) != 0) + return (NULL); + if (fread(&ut, sizeof ut, 1, ufile) != 1) + return (NULL); + ulog_futmp_to_utmpx(&ut, &utx); + } return (&utx); } +/* + * getutxent(). + * + * Read the next entry from the file. + */ + +struct ulog_utmpx * +ulog_getutxent(void) +{ + + return ulog_read(0, SEEK_CUR, 1); +} + +/* + * ulog_getutxline(). + * + * Read entries from the file, until reaching an entry which matches the + * provided TTY device name. We can optimize the case for utmp files, + * because they are indexed by TTY device name. + */ + +struct ulog_utmpx * +ulog_getutxline(const struct ulog_utmpx *line) +{ + struct ulog_utmpx *utx; + + if (ufile == NULL) + ulog_setutxent(); + if (ufile == NULL) + return (NULL); + + if (ufiletype == UTXF_UTMP) { + unsigned int slot; + + slot = ulog_ttyslot(line->ut_line); + if (slot == 0) + return (NULL); + utx = ulog_read(slot, SEEK_SET, 1); + if (utx->ut_type == USER_PROCESS && + strcmp(utx->ut_line, line->ut_line) == 0) + return (utx); + return (NULL); + } else { + for (;;) { + utx = ulog_read(0, SEEK_CUR, 1); + if (utx == NULL) + return (NULL); + if (utx->ut_type == USER_PROCESS && + strcmp(utx->ut_line, line->ut_line) == 0) + return (utx); + } + } +} + +/* + * ulog_getutxuser(). + * + * Read entries from the file, until reaching an entry which matches the + * provided username. We can optimize the case for lastlog files, + * because they are indexed by user ID. + */ + +struct ulog_utmpx * +ulog_getutxuser(const char *user) +{ + struct ulog_utmpx *utx; + + if (ufiletype == UTXF_LASTLOG) { + struct passwd *pw; + + pw = getpwnam(user); + if (pw == NULL) + return (NULL); + utx = ulog_read(pw->pw_uid, SEEK_SET, 0); + if (utx != NULL) + strlcpy(utx->ut_user, user, sizeof utx->ut_user); + return (utx); + } else { + for (;;) { + utx = ulog_read(0, SEEK_CUR, 1); + if (utx == NULL) + return (NULL); + if (utx->ut_type == USER_PROCESS && + strcmp(utx->ut_user, user) == 0) + return (utx); + } + } +} + +/* + * ulog_setutxfile(). + * + * Switch to a different record file. When no filename is provided, the + * system default is opened. + */ + +int +ulog_setutxfile(int type, const char *file) +{ + + /* Supply default files. */ + switch (type) { + case UTXF_UTMP: + if (file == NULL) + file = _PATH_UTMP; + break; + case UTXF_WTMP: + if (file == NULL) + file = _PATH_WTMP; + break; + case UTXF_LASTLOG: + if (file == NULL) + file = _PATH_LASTLOG; + break; + default: + return (-1); + } + + if (ufile != NULL) + fclose(ufile); + ufile = fopen(file, "r"); + ufiletype = type; + if (ufile == NULL) + return (-1); + return (0); +} + +/* + * ulog_endutxfile(). + * + * Close any opened files. + */ + void ulog_setutxent(void) { - if (ufile != NULL) - fclose(ufile); - ufile = fopen(_PATH_UTMP, "r"); + ulog_setutxfile(UTXF_UTMP, NULL); } diff --git a/lib/libulog/ulog_internal.h b/lib/libulog/ulog_internal.h index 20c53304bb94..4969ea3dfd61 100644 --- a/lib/libulog/ulog_internal.h +++ b/lib/libulog/ulog_internal.h @@ -33,6 +33,8 @@ #include "ulog.h" +unsigned int ulog_ttyslot(const char *); + /* * On-disk format. */ diff --git a/lib/libulog/ulog_login.3 b/lib/libulog/ulog_login.3 index ca896fe116ab..45dba935ea5c 100644 --- a/lib/libulog/ulog_login.3 +++ b/lib/libulog/ulog_login.3 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd December 2, 2009 +.Dd December 5, 2009 .Os .Dt ULOG_LOGIN 3 .Sh NAME diff --git a/lib/libulog/ulog_login.c b/lib/libulog/ulog_login.c index 22de619c34d5..a788a9dfedb4 100644 --- a/lib/libulog/ulog_login.c +++ b/lib/libulog/ulog_login.c @@ -27,109 +27,48 @@ #include __FBSDID("$FreeBSD$"); -#include -#include +#include #include -#include #include -#include -#include -#include -#include #include "ulog_internal.h" void ulog_login(const char *line, const char *user, const char *host) { - struct futmp fu; - struct flastlog fl; - int fd; + struct ulog_utmpx utx; /* Remove /dev/ component. */ if (strncmp(line, _PATH_DEV, sizeof _PATH_DEV - 1) == 0) line += sizeof _PATH_DEV - 1; - /* Prepare log entries. */ - memset(&fu, 0, sizeof fu); - strlcpy(fu.ut_line, line, sizeof fu.ut_line); - strlcpy(fu.ut_user, user, sizeof fu.ut_user); - if (host != NULL) - strlcpy(fu.ut_host, host, sizeof fu.ut_host); - fu.ut_time = _time_to_time32(time(NULL)); + memset(&utx, 0, sizeof utx); - fl.ll_time = fu.ut_time; - memcpy(fl.ll_line, fu.ut_line, sizeof fl.ll_line); - memcpy(fl.ll_host, fu.ut_host, sizeof fl.ll_host); + /* XXX: ut_id, ut_pid missing. */ + utx.ut_type = USER_PROCESS; + strncpy(utx.ut_line, line, sizeof utx.ut_line); + strncpy(utx.ut_user, user, sizeof utx.ut_user); + strncpy(utx.ut_host, host, sizeof utx.ut_host); + gettimeofday(&utx.ut_tv, NULL); - /* Update utmp entry. */ - if ((fd = open(_PATH_UTMP, O_WRONLY|O_CREAT, 0644)) >= 0) { - struct ttyent *ty; - int idx; - - setttyent(); - for (idx = 1; (ty = getttyent()) != NULL; ++idx) { - if (strcmp(ty->ty_name, line) != 0) - continue; - lseek(fd, (off_t)(idx * sizeof fu), L_SET); - write(fd, &fu, sizeof fu); - break; - } - endttyent(); - close(fd); - } - - /* Add wtmp entry. */ - if ((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND, 0)) >= 0) { - write(fd, &fu, sizeof fu); - close(fd); - } - - /* Update lastlog entry. */ - if ((fd = open(_PATH_LASTLOG, O_WRONLY, 0)) >= 0) { - struct passwd *pw; - - pw = getpwnam(user); - if (pw != NULL) { - lseek(fd, (off_t)(pw->pw_uid * sizeof fl), L_SET); - write(fd, &fl, sizeof fl); - } - close(fd); - } + ulog_pututxline(&utx); } void ulog_logout(const char *line) { - struct futmp ut; - int fd, found; + struct ulog_utmpx utx; /* Remove /dev/ component. */ if (strncmp(line, _PATH_DEV, sizeof _PATH_DEV - 1) == 0) line += sizeof _PATH_DEV - 1; - /* Mark entry in utmp as logged out. */ - if ((fd = open(_PATH_UTMP, O_RDWR, 0)) < 0) - return; - found = 0; - while (read(fd, &ut, sizeof ut) == sizeof ut) { - if (ut.ut_user[0] == '\0' || - strncmp(ut.ut_line, line, sizeof ut.ut_line) != 0) - continue; - memset(ut.ut_user, 0, sizeof ut.ut_user); - memset(ut.ut_host, 0, sizeof ut.ut_host); - ut.ut_time = _time_to_time32(time(NULL)); - lseek(fd, -(off_t)sizeof ut, L_INCR); - write(fd, &ut, sizeof ut); - found = 1; - } - close(fd); - if (!found) - return; + memset(&utx, 0, sizeof utx); - /* utmp entry found. Also add logout entry to wtmp. */ - if ((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND, 0)) >= 0) { - write(fd, &ut, sizeof ut); - close(fd); - } + /* XXX: ut_id, ut_pid missing. ut_line not needed */ + utx.ut_type = DEAD_PROCESS; + strncpy(utx.ut_line, line, sizeof utx.ut_line); + gettimeofday(&utx.ut_tv, NULL); + + ulog_pututxline(&utx); } diff --git a/lib/libulog/ulog_pututxline.c b/lib/libulog/ulog_pututxline.c new file mode 100644 index 000000000000..d02dda51f49e --- /dev/null +++ b/lib/libulog/ulog_pututxline.c @@ -0,0 +1,208 @@ +/*- + * Copyright (c) 2009 Ed Schouten + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include "ulog_internal.h" + +static void +ulog_utmpx_to_futmp(const struct ulog_utmpx *utx, struct futmp *ut) +{ + + memset(ut, 0, sizeof *ut); +#define COPY_STRING(field) do { \ + strncpy(ut->ut_ ## field, utx->ut_ ## field, \ + MIN(sizeof ut->ut_ ## field, sizeof utx->ut_ ## field)); \ +} while (0) + switch (utx->ut_type) { + case BOOT_TIME: + strcpy(ut->ut_user, "reboot"); + ut->ut_line[0] = '~'; + break; + case OLD_TIME: + strcpy(ut->ut_user, "date"); + ut->ut_line[0] = '|'; + break; + case NEW_TIME: + strcpy(ut->ut_user, "date"); + ut->ut_line[0] = '{'; + break; + case USER_PROCESS: + COPY_STRING(user); + COPY_STRING(line); + COPY_STRING(host); + break; + case DEAD_PROCESS: + COPY_STRING(line); + break; + case SHUTDOWN_TIME: + strcpy(ut->ut_user, "shutdown"); + ut->ut_line[0] = '~'; + break; + } +#undef COPY_STRING + ut->ut_time = _time_to_time32(utx->ut_tv.tv_sec); +} + +static void +ulog_utmpx_to_flastlog(const struct ulog_utmpx *utx, struct flastlog *ll) +{ + + memset(ll, 0, sizeof *ll); +#define COPY_STRING(field) do { \ + strncpy(ll->ll_ ## field, utx->ut_ ## field, \ + MIN(sizeof ll->ll_ ## field, sizeof utx->ut_ ## field)); \ +} while (0) + switch (utx->ut_type) { + case USER_PROCESS: + COPY_STRING(line); + COPY_STRING(host); + break; + } +#undef COPY_STRING + ll->ll_time = _time_to_time32(utx->ut_tv.tv_sec); +} + +static void +ulog_write_utmp_fast(const struct futmp *ut) +{ + unsigned int idx; + char line[sizeof ut->ut_line + 1]; + int fd; + + if ((fd = open(_PATH_UTMP, O_WRONLY|O_CREAT, 0644)) < 0) + return; + strlcpy(line, ut->ut_line, sizeof line); + idx = ulog_ttyslot(line); + if (idx > 0) { + lseek(fd, (off_t)(idx * sizeof *ut), SEEK_SET); + write(fd, ut, sizeof *ut); + } + close(fd); +} + +static int +ulog_write_utmp_slow(const struct futmp *ut) +{ + struct futmp utf; + int fd, found; + + if ((fd = open(_PATH_UTMP, O_RDWR, 0)) < 0) + return (0); + found = 0; + while (read(fd, &utf, sizeof utf) == sizeof utf) { + if (utf.ut_user[0] == '\0' || + strncmp(utf.ut_line, ut->ut_line, sizeof utf.ut_line) != 0) + continue; + lseek(fd, -(off_t)sizeof utf, SEEK_CUR); + write(fd, &ut, sizeof ut); + found = 1; + } + close(fd); + return (found); +} + +static void +ulog_write_wtmp(const struct futmp *ut) +{ + int fd; + + if ((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND, 0)) < 0) + return; + write(fd, ut, sizeof *ut); + close(fd); +} + +static void +ulog_write_lastlog(const struct flastlog *ll, const char *user) +{ + struct passwd *pw; + int fd; + + if ((fd = open(_PATH_LASTLOG, O_WRONLY, 0)) < 0) + return; + pw = getpwnam(user); + if (pw != NULL) { + lseek(fd, (off_t)(pw->pw_uid * sizeof *ll), SEEK_SET); + write(fd, ll, sizeof *ll); + } + close(fd); +} + +struct ulog_utmpx * +ulog_pututxline(const struct ulog_utmpx *utmpx) +{ + static struct ulog_utmpx utx; + struct futmp ut; + struct flastlog ll; + char user[sizeof utmpx->ut_user + 1]; + + switch (utmpx->ut_type) { + case BOOT_TIME: + case OLD_TIME: + case NEW_TIME: + case SHUTDOWN_TIME: + ulog_utmpx_to_futmp(utmpx, &ut); + + /* Only log to wtmp. */ + ulog_write_wtmp(&ut); + break; + case USER_PROCESS: + ulog_utmpx_to_futmp(utmpx, &ut); + ulog_utmpx_to_flastlog(utmpx, &ll); + + /* Log to utmp, wtmp and lastlog. */ + ulog_write_utmp_fast(&ut); + ulog_write_wtmp(&ut); + strlcpy(user, utmpx->ut_user, sizeof user); + ulog_write_lastlog(&ll, user); + break; + case DEAD_PROCESS: + ulog_utmpx_to_futmp(utmpx, &ut); + + /* Only log to wtmp if logged in utmp. */ + if (ulog_write_utmp_slow(&ut)) + ulog_write_wtmp(&ut); + break; + default: + return (NULL); + } + + /* XXX: Can't we just return utmpx itself? */ + memcpy(&utx, utmpx, sizeof utx); + utx.ut_user[sizeof utx.ut_user - 1] = '\0'; + utx.ut_line[sizeof utx.ut_line - 1] = '\0'; + utx.ut_host[sizeof utx.ut_host - 1] = '\0'; + return (&utx); +} diff --git a/lib/libulog/ulog_setutxfile.3 b/lib/libulog/ulog_setutxfile.3 new file mode 100644 index 000000000000..9760a168381c --- /dev/null +++ b/lib/libulog/ulog_setutxfile.3 @@ -0,0 +1,94 @@ +.\" Copyright (c) 2009 Ed Schouten +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd December 5, 2009 +.Os +.Dt ULOG_SETUTXFILE 3 +.Sh NAME +.Nm ulog_setutxfile , +.Nm ulog_getutxuser +.Nd additional user login records management +.Sh LIBRARY +.Lb libulog +.Sh SYNOPSIS +.In ulog.h +.Ft int +.Fn ulog_setutxfile "int type" "const char *file" +.Ft struct ulog_utmpx * +.Fn ulog_getutxuser "const char *user" +.Sh DESCRIPTION +The +.Fn ulog_setutxfile +and +.Fn ulog_getutxuser +functions are extensions to the standard +.In ulog.h +interface. +.Pp +The +.Fn ulog_setutxfile +function is similar to +.Fn ulog_setutxent , +but is capable of returning an error code and also gives access to other +login record files by using one of the following values for +.Fa type : +.Bl -tag -width UTXF_LASTLOG +.It Dv UTXF_UTMP +Open the default +.Nm utmp +file, which is indexed by TTY device. +.It Dv UTXF_WTMP +Open the +.Nm wtmp +file, which is indexed by time. +.It Dv UTXF_LASTLOG +Open the +.Nm lastlog +file, which is indexed by user ID. +.El +.Pp +The +.Fa file +argument determines the file to be opened. +If left null, implementation-defined default file is opened. +.Pp +The +.Fn ulog_getutxuser +searches the currently opened file until an entry is found whose +.Fa ut_user +is equal to the +.Fa user +argument. +.Sh RETURN VALUES +If succesful, +.Fn ulog_setutxfile +returns 0. +It returns -1 on failure. +.Sh SEE ALSO +.Xr ulog_getutxent 3 +.Sh HISTORY +These functions appeared in +.Fx 9.0 . diff --git a/lib/libulog/ulog_util.c b/lib/libulog/ulog_util.c new file mode 100644 index 000000000000..b14920ca4125 --- /dev/null +++ b/lib/libulog/ulog_util.c @@ -0,0 +1,49 @@ +/*- + * Copyright (c) 2009 Ed Schouten + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include "ulog_internal.h" + +unsigned int +ulog_ttyslot(const char *name) +{ + struct ttyent *ty; + unsigned int slot; + + setttyent(); + for (slot = 1; (ty = getttyent()) != NULL; ++slot) + if (strcmp(ty->ty_name, name) == 0) { + endttyent(); + return (slot); + } + endttyent(); + return (0); +}