diff options
Diffstat (limited to '')
33 files changed, 2928 insertions, 0 deletions
diff --git a/liblastlog2/COPYING b/liblastlog2/COPYING new file mode 100644 index 0000000..df71a98 --- /dev/null +++ b/liblastlog2/COPYING @@ -0,0 +1,24 @@ +BSD 2-Clause License + +Copyright (c) 2023, Thorsten Kukuk + +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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. diff --git a/liblastlog2/Makemodule.am b/liblastlog2/Makemodule.am new file mode 100644 index 0000000..2f69f88 --- /dev/null +++ b/liblastlog2/Makemodule.am @@ -0,0 +1,10 @@ +if BUILD_LIBLASTLOG2 + +include liblastlog2/man/Makemodule.am +include liblastlog2/src/Makemodule.am + +pkgconfig_DATA += liblastlog2/lastlog2.pc +PATHFILES += liblastlog2/lastlog2.pc +EXTRA_DIST += liblastlog2/COPYING + +endif # BUILD_LIBLASTLOG2 diff --git a/liblastlog2/lastlog2.pc.in b/liblastlog2/lastlog2.pc.in new file mode 100644 index 0000000..39df5da --- /dev/null +++ b/liblastlog2/lastlog2.pc.in @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: BSD-2-Clause +# +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@usrlib_execdir@ +includedir=@includedir@ + +Name: lastlog2 +Description: Y2038 safe version of lastlog +Version: @LIBLASTLOG2_VERSION@ +Requires.private: sqlite3 +Cflags: -I${includedir}/liblastlog2 +Libs: -L${libdir} -llastlog2 diff --git a/liblastlog2/man/Makemodule.am b/liblastlog2/man/Makemodule.am new file mode 100644 index 0000000..803bfb7 --- /dev/null +++ b/liblastlog2/man/Makemodule.am @@ -0,0 +1,20 @@ + +MANPAGES += \ + liblastlog2/man/lastlog2.3 \ + liblastlog2/man/ll2_import_lastlog.3 \ + liblastlog2/man/ll2_read_all.3 \ + liblastlog2/man/ll2_read_entry.3 \ + liblastlog2/man/ll2_remove_entry.3 \ + liblastlog2/man/ll2_rename_user.3 \ + liblastlog2/man/ll2_update_login_time.3 \ + liblastlog2/man/ll2_write_entry.3 + +dist_noinst_DATA += \ + liblastlog2/man/lastlog2.3.adoc \ + liblastlog2/man/ll2_import_lastlog.3.adoc \ + liblastlog2/man/ll2_read_all.3.adoc \ + liblastlog2/man/ll2_read_entry.3.adoc \ + liblastlog2/man/ll2_remove_entry.3.adoc \ + liblastlog2/man/ll2_rename_user.3.adoc \ + liblastlog2/man/ll2_update_login_time.3.adoc \ + liblastlog2/man/ll2_write_entry.3.adoc diff --git a/liblastlog2/man/lastlog2.3 b/liblastlog2/man/lastlog2.3 new file mode 100644 index 0000000..b2ac0d7 --- /dev/null +++ b/liblastlog2/man/lastlog2.3 @@ -0,0 +1,67 @@ +'\" t +.\" Title: lastlog2 +.\" Author: [see the "AUTHOR(S)" section] +.\" Generator: Asciidoctor 2.0.20 +.\" Date: 2024-03-20 +.\" Manual: Programmer's Manual +.\" Source: util-linux 2.40 +.\" Language: English +.\" +.TH "LASTLOG2" "3" "2024-03-20" "util\-linux 2.40" "Programmer\*(Aqs Manual" +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.ss \n[.ss] 0 +.nh +.ad l +.de URL +\fI\\$2\fP <\\$1>\\$3 +.. +.als MTO URL +.if \n[.g] \{\ +. mso www.tmac +. am URL +. ad l +. . +. am MTO +. ad l +. . +. LINKSTYLE blue R < > +.\} +.SH "NAME" +lastlog2 \- Y2038 safe version of lastlog library. +.SH "SYNOPSIS" +.sp +\fB#include <lastlog2.h>\fP +.SH "DESCRIPTION" +.sp +\fBlastlog2\fP reports the last login of a given user or of all users who did ever login on a system. +.sp +It\(cqs using sqlite3 as database backend. Data is only collected via a PAM module, so that every +tools can make use of it, without modifying existing packages. +The output is as compatible as possible with the old lastlog implementation. +By default the database will be written as \f(CR/var/lib/lastlog/lastlog2.db\fP. +The size of the database depends on the amount of users, not how big the biggest UID is. +.SH "AUTHORS" +.sp +Thorsten Kukuk (\c +.MTO "kukuk\(atsuse.de" "" ")" +.SH "SEE ALSO" +.sp +\fBlastlog2\fP(3), +\fBll2_new_context(3), +*ll2_unref_context(3), +*ll2_write_entry\fP(3), +\fBll2_read_all\fP(3), +\fBll2_read_entry\fP(3), +\fBll2_update_login_time\fP(3), +\fBll2_remove_entry\fP(3), +\fBll2_rename_user\fP(3), +\fBll2_import_lastlog\fP(3) +.SH "REPORTING BUGS" +.sp +For bug reports, use the issue tracker at \c +.URL "https://github.com/util\-linux/util\-linux/issues" "" "." +.SH "AVAILABILITY" +.sp +The \fBliblastlog2\fP library is part of the util\-linux package since version 2.40. It can be downloaded from \c +.URL "https://www.kernel.org/pub/linux/utils/util\-linux/" "Linux Kernel Archive" "."
\ No newline at end of file diff --git a/liblastlog2/man/lastlog2.3.adoc b/liblastlog2/man/lastlog2.3.adoc new file mode 100644 index 0000000..05cab12 --- /dev/null +++ b/liblastlog2/man/lastlog2.3.adoc @@ -0,0 +1,51 @@ +//po4a: entry man manual += lastlog2(3) +:doctype: manpage +:man manual: Programmer's Manual +:man source: util-linux {release-version} +:page-layout: base +:lib: liblastlog2 +:firstversion: 2.40 + +== NAME + +lastlog2 - Y2038 safe version of lastlog library. + +== SYNOPSIS + +*#include <lastlog2.h>* + +== DESCRIPTION + +*lastlog2* reports the last login of a given user or of all users who did ever login on a system. + +It's using sqlite3 as database backend. Data is only collected via a PAM module, so that every +tools can make use of it, without modifying existing packages. +The output is as compatible as possible with the old lastlog implementation. +By default the database will be written as `/var/lib/lastlog/lastlog2.db`. +The size of the database depends on the amount of users, not how big the biggest UID is. + +== AUTHORS + +Thorsten Kukuk (kukuk@suse.de) + +== SEE ALSO + +*lastlog2*(3), +*ll2_new_context(3), +*ll2_unref_context(3), +*ll2_write_entry*(3), +*ll2_read_all*(3), +*ll2_read_entry*(3), +*ll2_update_login_time*(3), +*ll2_remove_entry*(3), +*ll2_rename_user*(3), +*ll2_import_lastlog*(3) + +include::man-common/bugreports.adoc[] + +include::man-common/footer-lib.adoc[] + +ifdef::translation[] +include::man-common/translation.adoc[] +endif::[] diff --git a/liblastlog2/man/ll2_import_lastlog.3 b/liblastlog2/man/ll2_import_lastlog.3 new file mode 100644 index 0000000..23231c4 --- /dev/null +++ b/liblastlog2/man/ll2_import_lastlog.3 @@ -0,0 +1,84 @@ +'\" t +.\" Title: ll2_import_lastlog +.\" Author: [see the "AUTHOR(S)" section] +.\" Generator: Asciidoctor 2.0.20 +.\" Date: 2024-03-20 +.\" Manual: Programmer's Manual +.\" Source: util-linux 2.40 +.\" Language: English +.\" +.TH "LL2_IMPORT_LASTLOG" "3" "2024-03-20" "util\-linux 2.40" "Programmer\*(Aqs Manual" +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.ss \n[.ss] 0 +.nh +.ad l +.de URL +\fI\\$2\fP <\\$1>\\$3 +.. +.als MTO URL +.if \n[.g] \{\ +. mso www.tmac +. am URL +. ad l +. . +. am MTO +. ad l +. . +. LINKSTYLE blue R < > +.\} +.SH "NAME" +ll2_import_lastlog \- Import old lastlog file. +.SH "SYNOPSIS" +.sp +\fB#include <lastlog2.h>\fP +.sp +\fBint ll2_import_lastlog (struct ll2_context *\fIcontext\fP, +const char *\fIlastlog_file\fP, +char **\fIerror\fP);\fP +.SH "DESCRIPTION" +.sp +Importing all entries from \fIlastlog_file\fP file (lastlog(8)) into +lastlog2 database defined with \fIcontext\fP. +If \fIcontext\fP is NULL, the default database, defined in \fILL2_DEFAULT_DATABASE\fP, +will be taken. +.sp +.if n .RS 4 +.nf +.fam C +char *error = NULL; +const char *lastlog_path = "/var/log/lastlog"; + +int ret = ll2_import_lastlog (NULL, lastlog_path, &error); +.fam +.fi +.if n .RE +.SH "RETURN VALUE" +.sp +Returns 0 on success, \-ENOMEM or \-1 on other failure. +\fIerror\fP contains an error string if the return value is \-1. +\fIerror\fP is not guaranteed to contain an error string, could also be NULL. +\fIerror\fP should be freed by the caller. +.SH "AUTHORS" +.sp +Thorsten Kukuk (\c +.MTO "kukuk\(atsuse.de" "" ")" +.SH "SEE ALSO" +.sp +\fBlastlog2\fP(3), +\fBll2_new_context(3), +*ll2_unref_context(3), +*ll2_read_all\fP(3), +\fBll2_write_entry\fP(3), +\fBll2_read_entry\fP(3), +\fBll2_update_login_time\fP(3), +\fBll2_rename_user\fP(3), +\fBll2_remove_entry\fP(3) +.SH "REPORTING BUGS" +.sp +For bug reports, use the issue tracker at \c +.URL "https://github.com/util\-linux/util\-linux/issues" "" "." +.SH "AVAILABILITY" +.sp +The \fBliblastlog2\fP library is part of the util\-linux package since version 2.40. It can be downloaded from \c +.URL "https://www.kernel.org/pub/linux/utils/util\-linux/" "Linux Kernel Archive" "."
\ No newline at end of file diff --git a/liblastlog2/man/ll2_import_lastlog.3.adoc b/liblastlog2/man/ll2_import_lastlog.3.adoc new file mode 100644 index 0000000..9ddf54b --- /dev/null +++ b/liblastlog2/man/ll2_import_lastlog.3.adoc @@ -0,0 +1,65 @@ +//po4a: entry man manual += ll2_import_lastlog(3) +:doctype: manpage +:man manual: Programmer's Manual +:man source: util-linux {release-version} +:page-layout: base +:lib: liblastlog2 +:firstversion: 2.40 + +== NAME + +ll2_import_lastlog - Import old lastlog file. + +== SYNOPSIS + +*#include <lastlog2.h>* + +*int ll2_import_lastlog (struct ll2_context *__context__, + const char *__lastlog_file__, + char **__error__);* + +== DESCRIPTION + +Importing all entries from _lastlog_file_ file (lastlog(8)) into +lastlog2 database defined with _context_. +If _context_ is NULL, the default database, defined in _LL2_DEFAULT_DATABASE_, +will be taken. + +-------------------------------------- +char *error = NULL; +const char *lastlog_path = "/var/log/lastlog"; + +int ret = ll2_import_lastlog (NULL, lastlog_path, &error); +-------------------------------------- + +== RETURN VALUE + +Returns 0 on success, -ENOMEM or -1 on other failure. +_error_ contains an error string if the return value is -1. +_error_ is not guaranteed to contain an error string, could also be NULL. +_error_ should be freed by the caller. + +== AUTHORS + +Thorsten Kukuk (kukuk@suse.de) + +== SEE ALSO + +*lastlog2*(3), +*ll2_new_context(3), +*ll2_unref_context(3), +*ll2_read_all*(3), +*ll2_write_entry*(3), +*ll2_read_entry*(3), +*ll2_update_login_time*(3), +*ll2_rename_user*(3), +*ll2_remove_entry*(3) + +include::man-common/bugreports.adoc[] + +include::man-common/footer-lib.adoc[] + +ifdef::translation[] +include::man-common/translation.adoc[] +endif::[] diff --git a/liblastlog2/man/ll2_read_all.3 b/liblastlog2/man/ll2_read_all.3 new file mode 100644 index 0000000..753749f --- /dev/null +++ b/liblastlog2/man/ll2_read_all.3 @@ -0,0 +1,93 @@ +'\" t +.\" Title: ll2_read_all +.\" Author: [see the "AUTHOR(S)" section] +.\" Generator: Asciidoctor 2.0.20 +.\" Date: 2024-03-20 +.\" Manual: Programmer's Manual +.\" Source: util-linux 2.40 +.\" Language: English +.\" +.TH "LL2_READ_ALL" "3" "2024-03-20" "util\-linux 2.40" "Programmer\*(Aqs Manual" +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.ss \n[.ss] 0 +.nh +.ad l +.de URL +\fI\\$2\fP <\\$1>\\$3 +.. +.als MTO URL +.if \n[.g] \{\ +. mso www.tmac +. am URL +. ad l +. . +. am MTO +. ad l +. . +. LINKSTYLE blue R < > +.\} +.SH "NAME" +ll2_read_all \- Reads all entries from database and calls the callback function for each entry. +.SH "SYNOPSIS" +.sp +\fB#include <lastlog2.h>\fP +\fBint ll2_read_all (struct ll2_context *\fIcontext\fP, +int (*\fIcallback\fP)(const char *\fIuser\fP, int64_t \fIll_time\fP, +const char *\fItty\fP, const char *\fIrhost\fP, +const char *\fIpam_service\fP, const char *\fIcb_error\fP), +char **\fIerror\fP);\fP +.SH "DESCRIPTION" +.sp +Reads all entries from database, defined in \fIcontext\fP, and calls callback fuction \fIcallback\fP for each entry. +If \fIcontext\fP is NULL, the default database, defined in \fILL2_DEFAULT_DATABASE\fP, will be taken. +.sp +.if n .RS 4 +.nf +.fam C +char *error = NULL; +const char *user = "root"; + +static int +callback (const char *res_user, int64_t ll_time, const char *res_tty, + const char *res_rhost, const char *res_service, const char *cb_error) +{ + /* returning != 0 if no further entry has to be handled by the callback */ + return 0; +} + +int ret = ll2_read_all (NULL, callback, &error); +.fam +.fi +.if n .RE +.SH "RETURN VALUE" +.sp +Returns 0 on success, \-ENOMEM or \-1 on other failure. +\fIerror\fP contains an error string if the return value is \-1. +\fIerror\fP is not guaranteed to contain an error string, could also be NULL. +\fIerror\fP should be freed by the caller. +If lastlog2 database does not exist at all, the errno ENOENT has been set +and can be checked. +.SH "AUTHORS" +.sp +Thorsten Kukuk (\c +.MTO "kukuk\(atsuse.de" "" ")" +.SH "SEE ALSO" +.sp +\fBlastlog2\fP(3), +\fBll2_new_context(3), +*ll2_unref_context(3), +*ll2_write_entry\fP(3), +\fBll2_read_entry\fP(3), +\fBll2_update_login_time\fP(3), +\fBll2_remove_entry\fP(3), +\fBll2_rename_user\fP(3), +\fBll2_import_lastlog\fP(3) +.SH "REPORTING BUGS" +.sp +For bug reports, use the issue tracker at \c +.URL "https://github.com/util\-linux/util\-linux/issues" "" "." +.SH "AVAILABILITY" +.sp +The \fBliblastlog2\fP library is part of the util\-linux package since version 2.40. It can be downloaded from \c +.URL "https://www.kernel.org/pub/linux/utils/util\-linux/" "Linux Kernel Archive" "."
\ No newline at end of file diff --git a/liblastlog2/man/ll2_read_all.3.adoc b/liblastlog2/man/ll2_read_all.3.adoc new file mode 100644 index 0000000..fbe589d --- /dev/null +++ b/liblastlog2/man/ll2_read_all.3.adoc @@ -0,0 +1,74 @@ +//po4a: entry man manual += ll2_read_all(3) +:doctype: manpage +:man manual: Programmer's Manual +:man source: util-linux {release-version} +:page-layout: base +:lib: liblastlog2 +:firstversion: 2.40 + +== NAME + +ll2_read_all - Reads all entries from database and calls the callback function for each entry. + +== SYNOPSIS + +*#include <lastlog2.h>* +*int ll2_read_all (struct ll2_context *__context__, + int (*__callback__)(const char *__user__, int64_t __ll_time__, + const char *__tty__, const char *__rhost__, + const char *__pam_service__, const char *__cb_error__), + char **__error__);* + +== DESCRIPTION + +Reads all entries from database, defined in _context_, and calls callback fuction _callback_ for each entry. +If _context_ is NULL, the default database, defined in _LL2_DEFAULT_DATABASE_, will be taken. + +-------------------------------------- +char *error = NULL; +const char *user = "root"; + +static int +callback (const char *res_user, int64_t ll_time, const char *res_tty, + const char *res_rhost, const char *res_service, const char *cb_error) +{ + /* returning != 0 if no further entry has to be handled by the callback */ + return 0; +} + +int ret = ll2_read_all (NULL, callback, &error); +-------------------------------------- + +== RETURN VALUE + +Returns 0 on success, -ENOMEM or -1 on other failure. +_error_ contains an error string if the return value is -1. +_error_ is not guaranteed to contain an error string, could also be NULL. +_error_ should be freed by the caller. +If lastlog2 database does not exist at all, the errno ENOENT has been set +and can be checked. + +== AUTHORS + +Thorsten Kukuk (kukuk@suse.de) + +== SEE ALSO + +*lastlog2*(3), +*ll2_new_context(3), +*ll2_unref_context(3), +*ll2_write_entry*(3), +*ll2_read_entry*(3), +*ll2_update_login_time*(3), +*ll2_remove_entry*(3), +*ll2_rename_user*(3), +*ll2_import_lastlog*(3) + +include::man-common/bugreports.adoc[] + +include::man-common/footer-lib.adoc[] + +ifdef::translation[] +include::man-common/translation.adoc[] +endif::[] diff --git a/liblastlog2/man/ll2_read_entry.3 b/liblastlog2/man/ll2_read_entry.3 new file mode 100644 index 0000000..53f1e26 --- /dev/null +++ b/liblastlog2/man/ll2_read_entry.3 @@ -0,0 +1,91 @@ +'\" t +.\" Title: ll2_read_entry +.\" Author: [see the "AUTHOR(S)" section] +.\" Generator: Asciidoctor 2.0.20 +.\" Date: 2024-03-20 +.\" Manual: Programmer's Manual +.\" Source: util-linux 2.40 +.\" Language: English +.\" +.TH "LL2_READ_ENTRY" "3" "2024-03-20" "util\-linux 2.40" "Programmer\*(Aqs Manual" +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.ss \n[.ss] 0 +.nh +.ad l +.de URL +\fI\\$2\fP <\\$1>\\$3 +.. +.als MTO URL +.if \n[.g] \{\ +. mso www.tmac +. am URL +. ad l +. . +. am MTO +. ad l +. . +. LINKSTYLE blue R < > +.\} +.SH "NAME" +ll2_read_entry \- Reads one entry from database and returns that. +.SH "SYNOPSIS" +.sp +\fB#include <lastlog2.h>\fP +.sp +\fBint ll2_read_entry (struct ll2_context *\fIcontext\fP, const char *\fIuser\fP, +int64_t *\fIll_time\fP, char \fB\fItty\fP, char \fP\fIrhost\fP, +char \fB\fIpam_service\fP, char \fP\fIerror\fP);\fP +.SH "DESCRIPTION" +.sp +Reads the first entry from database, defined in \fIcontext\fP, for user \fIuser\fP. +If \fIcontext\fP is NULL, the default database, defined in \fILL2_DEFAULT_DATABASE\fP, +will be taken. +.sp +.if n .RS 4 +.nf +.fam C +char *error = NULL; +const char *user = "root"; +int64_t res_time; +char *res_tty = NULL; +char *res_rhost = NULL; +char *res_service = NULL; + +int ret = ll2_read_entry (NULL, user, &res_time, &res_tty, &res_rhost, &res_service, &error); +.fam +.fi +.if n .RE +.SH "RETURN VALUE" +.sp +Returns 0 on success, \-ENOMEM or \-1 on other failure. +\fIerror\fP contains an error string if the return value is \-1. +\fIerror\fP is not guaranteed to contain an error string, could also be NULL. +\fIerror\fP should be freed by the caller. +If lastlog2 database does not exist at all, the errno ENOENT has been set +and can be checked. +.sp +The evaluated values are returned by \fIll_time\fP, \fItty\fP, \fIrhost\fP and \fIpam_service\fP. +.SH "AUTHORS" +.sp +Thorsten Kukuk (\c +.MTO "kukuk\(atsuse.de" "" ")" +.SH "SEE ALSO" +.sp +\fBlastlog2\fP(3), +\fBll2_new_context(3), +*ll2_unref_context(3), +*ll2_read_all\fP(3), +\fBll2_write_entry\fP(3), +\fBll2_update_login_time\fP(3), +\fBll2_remove_entry\fP(3), +\fBll2_rename_user\fP(3), +\fBll2_import_lastlog\fP(3) +.SH "REPORTING BUGS" +.sp +For bug reports, use the issue tracker at \c +.URL "https://github.com/util\-linux/util\-linux/issues" "" "." +.SH "AVAILABILITY" +.sp +The \fBliblastlog2\fP library is part of the util\-linux package since version 2.40. It can be downloaded from \c +.URL "https://www.kernel.org/pub/linux/utils/util\-linux/" "Linux Kernel Archive" "."
\ No newline at end of file diff --git a/liblastlog2/man/ll2_read_entry.3.adoc b/liblastlog2/man/ll2_read_entry.3.adoc new file mode 100644 index 0000000..3bef1a8 --- /dev/null +++ b/liblastlog2/man/ll2_read_entry.3.adoc @@ -0,0 +1,72 @@ +//po4a: entry man manual += ll2_read_entry(3) +:doctype: manpage +:man manual: Programmer's Manual +:man source: util-linux {release-version} +:page-layout: base +:lib: liblastlog2 +:firstversion: 2.40 + +== NAME + +ll2_read_entry - Reads one entry from database and returns that. + +== SYNOPSIS + +*#include <lastlog2.h>* + +*int ll2_read_entry (struct ll2_context *__context__, const char *__user__, + int64_t *__ll_time__, char **__tty__, char **__rhost__, + char **__pam_service__, char **__error__);* + +== DESCRIPTION + +Reads the first entry from database, defined in _context_, for user _user_. +If _context_ is NULL, the default database, defined in _LL2_DEFAULT_DATABASE_, +will be taken. + +-------------------------------------- +char *error = NULL; +const char *user = "root"; +int64_t res_time; +char *res_tty = NULL; +char *res_rhost = NULL; +char *res_service = NULL; + +int ret = ll2_read_entry (NULL, user, &res_time, &res_tty, &res_rhost, &res_service, &error); +-------------------------------------- + +== RETURN VALUE + +Returns 0 on success, -ENOMEM or -1 on other failure. +_error_ contains an error string if the return value is -1. +_error_ is not guaranteed to contain an error string, could also be NULL. +_error_ should be freed by the caller. +If lastlog2 database does not exist at all, the errno ENOENT has been set +and can be checked. + +The evaluated values are returned by _ll_time_, _tty_, _rhost_ and _pam_service_. + +== AUTHORS + +Thorsten Kukuk (kukuk@suse.de) + +== SEE ALSO + +*lastlog2*(3), +*ll2_new_context(3), +*ll2_unref_context(3), +*ll2_read_all*(3), +*ll2_write_entry*(3), +*ll2_update_login_time*(3), +*ll2_remove_entry*(3), +*ll2_rename_user*(3), +*ll2_import_lastlog*(3) + +include::man-common/bugreports.adoc[] + +include::man-common/footer-lib.adoc[] + +ifdef::translation[] +include::man-common/translation.adoc[] +endif::[] diff --git a/liblastlog2/man/ll2_remove_entry.3 b/liblastlog2/man/ll2_remove_entry.3 new file mode 100644 index 0000000..3cb444b --- /dev/null +++ b/liblastlog2/man/ll2_remove_entry.3 @@ -0,0 +1,82 @@ +'\" t +.\" Title: ll2_remove_entry +.\" Author: [see the "AUTHOR(S)" section] +.\" Generator: Asciidoctor 2.0.20 +.\" Date: 2024-03-20 +.\" Manual: Programmer's Manual +.\" Source: util-linux 2.40 +.\" Language: English +.\" +.TH "LL2_REMOVE_ENTRY" "3" "2024-03-20" "util\-linux 2.40" "Programmer\*(Aqs Manual" +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.ss \n[.ss] 0 +.nh +.ad l +.de URL +\fI\\$2\fP <\\$1>\\$3 +.. +.als MTO URL +.if \n[.g] \{\ +. mso www.tmac +. am URL +. ad l +. . +. am MTO +. ad l +. . +. LINKSTYLE blue R < > +.\} +.SH "NAME" +ll2_remove_entry \- Remove all entries of an user. +.SH "SYNOPSIS" +.sp +\fB#include <lastlog2.h>\fP +.sp +\fBint ll2_remove_entry (struct ll2_context *\fIcontext\fP, const char *\fIuser\fP, +char **\fIerror\fP);\fP +.SH "DESCRIPTION" +.sp +Removing all database entries, defined in \fIcontext\fP, with the user name \fIuser\fP. +If \fIcontext\fP is NULL, the default database, defined in \fILL2_DEFAULT_DATABASE\fP, +will be taken. +.sp +.if n .RS 4 +.nf +.fam C +char *error = NULL; +const char *user = "root"; + +int ret = ll2_remove_entry (NULL, user, &error); +.fam +.fi +.if n .RE +.SH "RETURN VALUE" +.sp +Returns 0 on success, \-ENOMEM or \-1 on other failure. +\fIerror\fP contains an error string if the return value is \-1. +\fIerror\fP is not guaranteed to contain an error string, could also be NULL. +\fIerror\fP should be freed by the caller. +.SH "AUTHORS" +.sp +Thorsten Kukuk (\c +.MTO "kukuk\(atsuse.de" "" ")" +.SH "SEE ALSO" +.sp +\fBlastlog2\fP(3), +\fBll2_new_context(3), +*ll2_unref_context(3), +*ll2_read_all\fP(3), +\fBll2_write_entry\fP(3), +\fBll2_read_entry\fP(3), +\fBll2_update_login_time\fP(3), +\fBll2_rename_user\fP(3), +\fBll2_import_lastlog\fP(3) +.SH "REPORTING BUGS" +.sp +For bug reports, use the issue tracker at \c +.URL "https://github.com/util\-linux/util\-linux/issues" "" "." +.SH "AVAILABILITY" +.sp +The \fBliblastlog2\fP library is part of the util\-linux package since version 2.40. It can be downloaded from \c +.URL "https://www.kernel.org/pub/linux/utils/util\-linux/" "Linux Kernel Archive" "."
\ No newline at end of file diff --git a/liblastlog2/man/ll2_remove_entry.3.adoc b/liblastlog2/man/ll2_remove_entry.3.adoc new file mode 100644 index 0000000..f38ab66 --- /dev/null +++ b/liblastlog2/man/ll2_remove_entry.3.adoc @@ -0,0 +1,63 @@ +//po4a: entry man manual += ll2_remove_entry(3) +:doctype: manpage +:man manual: Programmer's Manual +:man source: util-linux {release-version} +:page-layout: base +:lib: liblastlog2 +:firstversion: 2.40 + +== NAME + +ll2_remove_entry - Remove all entries of an user. + +== SYNOPSIS + +*#include <lastlog2.h>* + +*int ll2_remove_entry (struct ll2_context *__context__, const char *__user__, + char **__error__);* + +== DESCRIPTION + +Removing all database entries, defined in _context_, with the user name _user_. +If _context_ is NULL, the default database, defined in _LL2_DEFAULT_DATABASE_, +will be taken. + +-------------------------------------- +char *error = NULL; +const char *user = "root"; + +int ret = ll2_remove_entry (NULL, user, &error); +-------------------------------------- + +== RETURN VALUE + +Returns 0 on success, -ENOMEM or -1 on other failure. +_error_ contains an error string if the return value is -1. +_error_ is not guaranteed to contain an error string, could also be NULL. +_error_ should be freed by the caller. + +== AUTHORS + +Thorsten Kukuk (kukuk@suse.de) + +== SEE ALSO + +*lastlog2*(3), +*ll2_new_context(3), +*ll2_unref_context(3), +*ll2_read_all*(3), +*ll2_write_entry*(3), +*ll2_read_entry*(3), +*ll2_update_login_time*(3), +*ll2_rename_user*(3), +*ll2_import_lastlog*(3) + +include::man-common/bugreports.adoc[] + +include::man-common/footer-lib.adoc[] + +ifdef::translation[] +include::man-common/translation.adoc[] +endif::[] diff --git a/liblastlog2/man/ll2_rename_user.3 b/liblastlog2/man/ll2_rename_user.3 new file mode 100644 index 0000000..7644838 --- /dev/null +++ b/liblastlog2/man/ll2_rename_user.3 @@ -0,0 +1,85 @@ +'\" t +.\" Title: ll2_rename_user +.\" Author: [see the "AUTHOR(S)" section] +.\" Generator: Asciidoctor 2.0.20 +.\" Date: 2024-03-20 +.\" Manual: Programmer's Manual +.\" Source: util-linux 2.40 +.\" Language: English +.\" +.TH "LL2_RENAME_USER" "3" "2024-03-20" "util\-linux 2.40" "Programmer\*(Aqs Manual" +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.ss \n[.ss] 0 +.nh +.ad l +.de URL +\fI\\$2\fP <\\$1>\\$3 +.. +.als MTO URL +.if \n[.g] \{\ +. mso www.tmac +. am URL +. ad l +. . +. am MTO +. ad l +. . +. LINKSTYLE blue R < > +.\} +.SH "NAME" +ll2_rename_user \- Renames an user entry. +.SH "SYNOPSIS" +.sp +\fB#include <lastlog2.h>\fP +.sp +\fBint ll2_rename_user (struct ll2_context *\fIcontext\fP, const char *\fIuser\fP, +const char *\fInewname\fP, char **\fIerror\fP);\fP +.SH "DESCRIPTION" +.sp +Changing user name from \fIuser\fP to \fInewname\fP of one entry in +database, which is defined by \fIcontext\fP. All other entries with the user \fIuser\fP +will be deleted. +If \fIcontext\fP is NULL, the default database, defined in \fILL2_DEFAULT_DATABASE\fP, +will be taken. +.sp +.if n .RS 4 +.nf +.fam C +char *error = NULL; +const char *user = "root"; +const char *new_user = "notroot"; + +int ret = ll2_rename_user (NULL, user, new_user, &error); +.fam +.fi +.if n .RE +.SH "RETURN VALUE" +.sp +Returns 0 on success, \-ENOMEM or \-1 on other failure. +\fIerror\fP contains an error string if the return value is \-1. +\fIerror\fP is not guaranteed to contain an error string, could also be NULL. +\fIerror\fP should be freed by the caller. +.SH "AUTHORS" +.sp +Thorsten Kukuk (\c +.MTO "kukuk\(atsuse.de" "" ")" +.SH "SEE ALSO" +.sp +\fBlastlog2\fP(3), +\fBll2_new_context(3), +*ll2_unref_context(3), +*ll2_read_all\fP(3), +\fBll2_write_entry\fP(3), +\fBll2_read_entry\fP(3), +\fBll2_remove_entry\fP(3), +\fBll2_update_login_time\fP(3), +\fBll2_import_lastlog\fP(3) +.SH "REPORTING BUGS" +.sp +For bug reports, use the issue tracker at \c +.URL "https://github.com/util\-linux/util\-linux/issues" "" "." +.SH "AVAILABILITY" +.sp +The \fBliblastlog2\fP library is part of the util\-linux package since version 2.40. It can be downloaded from \c +.URL "https://www.kernel.org/pub/linux/utils/util\-linux/" "Linux Kernel Archive" "."
\ No newline at end of file diff --git a/liblastlog2/man/ll2_rename_user.3.adoc b/liblastlog2/man/ll2_rename_user.3.adoc new file mode 100644 index 0000000..2202fe2 --- /dev/null +++ b/liblastlog2/man/ll2_rename_user.3.adoc @@ -0,0 +1,66 @@ +//po4a: entry man manual += ll2_rename_user(3) +:doctype: manpage +:man manual: Programmer's Manual +:man source: util-linux {release-version} +:page-layout: base +:lib: liblastlog2 +:firstversion: 2.40 + +== NAME + +ll2_rename_user - Renames an user entry. + +== SYNOPSIS + +*#include <lastlog2.h>* + +*int ll2_rename_user (struct ll2_context *__context__, const char *__user__, + const char *__newname__, char **__error__);* + +== DESCRIPTION + +Changing user name from _user_ to _newname_ of one entry in +database, which is defined by _context_. All other entries with the user _user_ +will be deleted. +If _context_ is NULL, the default database, defined in _LL2_DEFAULT_DATABASE_, +will be taken. + +-------------------------------------- +char *error = NULL; +const char *user = "root"; +const char *new_user = "notroot"; + +int ret = ll2_rename_user (NULL, user, new_user, &error); +-------------------------------------- + +== RETURN VALUE + +Returns 0 on success, -ENOMEM or -1 on other failure. +_error_ contains an error string if the return value is -1. +_error_ is not guaranteed to contain an error string, could also be NULL. +_error_ should be freed by the caller. + +== AUTHORS + +Thorsten Kukuk (kukuk@suse.de) + +== SEE ALSO + +*lastlog2*(3), +*ll2_new_context(3), +*ll2_unref_context(3), +*ll2_read_all*(3), +*ll2_write_entry*(3), +*ll2_read_entry*(3), +*ll2_remove_entry*(3), +*ll2_update_login_time*(3), +*ll2_import_lastlog*(3) + +include::man-common/bugreports.adoc[] + +include::man-common/footer-lib.adoc[] + +ifdef::translation[] +include::man-common/translation.adoc[] +endif::[] diff --git a/liblastlog2/man/ll2_update_login_time.3 b/liblastlog2/man/ll2_update_login_time.3 new file mode 100644 index 0000000..ce08bc4 --- /dev/null +++ b/liblastlog2/man/ll2_update_login_time.3 @@ -0,0 +1,86 @@ +'\" t +.\" Title: ll2_update_login_time +.\" Author: [see the "AUTHOR(S)" section] +.\" Generator: Asciidoctor 2.0.20 +.\" Date: 2024-03-20 +.\" Manual: Programmer's Manual +.\" Source: util-linux 2.40 +.\" Language: English +.\" +.TH "LL2_UPDATE_LOGIN_TIME" "3" "2024-03-20" "util\-linux 2.40" "Programmer\*(Aqs Manual" +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.ss \n[.ss] 0 +.nh +.ad l +.de URL +\fI\\$2\fP <\\$1>\\$3 +.. +.als MTO URL +.if \n[.g] \{\ +. mso www.tmac +. am URL +. ad l +. . +. am MTO +. ad l +. . +. LINKSTYLE blue R < > +.\} +.SH "NAME" +ll2_update_login_time \- Writes an *new* entry with updated login time. +.SH "SYNOPSIS" +.sp +\fB#include <lastlog2.h>\fP +.sp +\fBint ll2_update_login_time (struct ll2_context *\fIcontext\fP, +const char *\fIuser\fP, int64_t \fIll_time\fP, +char **\fIerror\fP);\fP +.SH "DESCRIPTION" +.sp +Writes an \fBnew\fP entry to database, defined in \fIcontext\fP, for user \fIuser\fP. +Time is set by \fIll_time\fP whereas the other values are taken from +an already existing entry. +If \fIcontext\fP is NULL, the default database, defined in \fILL2_DEFAULT_DATABASE\fP, +will be taken. +.sp +.if n .RS 4 +.nf +.fam C +char *error = NULL; +const char *user = "root"; +int64_t login_time = time(0); // Get the system time; + +int ret = ll2_update_login_time (NULL, user, login_time, &error); +.fam +.fi +.if n .RE +.SH "RETURN VALUE" +.sp +Returns 0 on success, \-ENOMEM or \-1 on other failure. +\fIerror\fP contains an error string if the return value is \-1. +\fIerror\fP is not guaranteed to contain an error string. It could also be NULL if the return value is \-1. +\fIerror\fP should be freed by the caller. +.SH "AUTHORS" +.sp +Thorsten Kukuk (\c +.MTO "kukuk\(atsuse.de" "" ")" +.SH "SEE ALSO" +.sp +\fBlastlog2\fP(3), +\fBll2_new_context(3), +*ll2_unref_context(3), +*ll2_read_all\fP(3), +\fBll2_write_entry\fP(3), +\fBll2_read_entry\fP(3), +\fBll2_remove_entry\fP(3), +\fBll2_rename_user\fP(3), +\fBll2_import_lastlog\fP(3) +.SH "REPORTING BUGS" +.sp +For bug reports, use the issue tracker at \c +.URL "https://github.com/util\-linux/util\-linux/issues" "" "." +.SH "AVAILABILITY" +.sp +The \fBliblastlog2\fP library is part of the util\-linux package since version 2.40. It can be downloaded from \c +.URL "https://www.kernel.org/pub/linux/utils/util\-linux/" "Linux Kernel Archive" "."
\ No newline at end of file diff --git a/liblastlog2/man/ll2_update_login_time.3.adoc b/liblastlog2/man/ll2_update_login_time.3.adoc new file mode 100644 index 0000000..9cc43e1 --- /dev/null +++ b/liblastlog2/man/ll2_update_login_time.3.adoc @@ -0,0 +1,67 @@ +//po4a: entry man manual += ll2_update_login_time(3) +:doctype: manpage +:man manual: Programmer's Manual +:man source: util-linux {release-version} +:page-layout: base +:lib: liblastlog2 +:firstversion: 2.40 + +== NAME + +ll2_update_login_time - Writes an *new* entry with updated login time. + +== SYNOPSIS + +*#include <lastlog2.h>* + +*int ll2_update_login_time (struct ll2_context *__context__, + const char *__user__, int64_t __ll_time__, + char **__error__);* + +== DESCRIPTION + +Writes an *new* entry to database, defined in _context_, for user _user_. +Time is set by _ll_time_ whereas the other values are taken from +an already existing entry. +If _context_ is NULL, the default database, defined in _LL2_DEFAULT_DATABASE_, +will be taken. + +-------------------------------------- +char *error = NULL; +const char *user = "root"; +int64_t login_time = time(0); // Get the system time; + +int ret = ll2_update_login_time (NULL, user, login_time, &error); +-------------------------------------- + +== RETURN VALUE + +Returns 0 on success, -ENOMEM or -1 on other failure. +_error_ contains an error string if the return value is -1. +_error_ is not guaranteed to contain an error string. It could also be NULL if the return value is -1. +_error_ should be freed by the caller. + +== AUTHORS + +Thorsten Kukuk (kukuk@suse.de) + +== SEE ALSO + +*lastlog2*(3), +*ll2_new_context(3), +*ll2_unref_context(3), +*ll2_read_all*(3), +*ll2_write_entry*(3), +*ll2_read_entry*(3), +*ll2_remove_entry*(3), +*ll2_rename_user*(3), +*ll2_import_lastlog*(3) + +include::man-common/bugreports.adoc[] + +include::man-common/footer-lib.adoc[] + +ifdef::translation[] +include::man-common/translation.adoc[] +endif::[] diff --git a/liblastlog2/man/ll2_write_entry.3 b/liblastlog2/man/ll2_write_entry.3 new file mode 100644 index 0000000..f16294a --- /dev/null +++ b/liblastlog2/man/ll2_write_entry.3 @@ -0,0 +1,88 @@ +'\" t +.\" Title: ll2_write_entry +.\" Author: [see the "AUTHOR(S)" section] +.\" Generator: Asciidoctor 2.0.20 +.\" Date: 2024-03-20 +.\" Manual: Programmer's Manual +.\" Source: util-linux 2.40 +.\" Language: English +.\" +.TH "LL2_WRITE_ENTRY" "3" "2024-03-20" "util\-linux 2.40" "Programmer\*(Aqs Manual" +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.ss \n[.ss] 0 +.nh +.ad l +.de URL +\fI\\$2\fP <\\$1>\\$3 +.. +.als MTO URL +.if \n[.g] \{\ +. mso www.tmac +. am URL +. ad l +. . +. am MTO +. ad l +. . +. LINKSTYLE blue R < > +.\} +.SH "NAME" +ll2_write_entry \- Write a new entry into the database. +.SH "SYNOPSIS" +.sp +\fB#include <lastlog2.h>\fP +.sp +\fBint ll2_write_entry (struct ll2_context *\fIcontext\fP, const char *\fIuser\fP, +int64_t \fIll_time\fP, const char *\fItty\fP, +const char *\fIrhost\fP, const char *\fIpam_service\fP, +char **\fIerror\fP);\fP +.SH "DESCRIPTION" +.sp +Writes an new entry into database, which is defined in \fIcontext\fP. +If \fIcontext\fP is NULL, the default database, defined in \fILL2_DEFAULT_DATABASE\fP, +will be taken. +.sp +.if n .RS 4 +.nf +.fam C +time_t login_time = time(0); // Get the system time +char *error = NULL; +const char *user = "root"; + +int ret = ll2_write_entry (NULL, user, login_time, "pts/0", + "192.168.122.1", NULL, &error); +.fam +.fi +.if n .RE +.sp +\fIpam_service\fP is the service or instance name which has generated the entry (optional). +.SH "RETURN VALUE" +.sp +Returns 0 on success, \-ENOMEM or \-1 on other failure. +\fIerror\fP contains an error string if the return value is \-1. +\fIerror\fP is not guaranteed to contain an error string, could also be NULL. +\fIerror\fP should be freed by the caller. +.SH "AUTHORS" +.sp +Thorsten Kukuk (\c +.MTO "kukuk\(atsuse.de" "" ")" +.SH "SEE ALSO" +.sp +\fBlastlog2\fP(3), +\fBll2_new_context(3), +*ll2_unref_context(3), +*ll2_read_all\fP(3), +\fBll2_read_entry\fP(3), +\fBll2_update_login_time\fP(3), +\fBll2_remove_entry\fP(3), +\fBll2_rename_user\fP(3), +\fBll2_import_lastlog\fP(3) +.SH "REPORTING BUGS" +.sp +For bug reports, use the issue tracker at \c +.URL "https://github.com/util\-linux/util\-linux/issues" "" "." +.SH "AVAILABILITY" +.sp +The \fBliblastlog2\fP library is part of the util\-linux package since version 2.40. It can be downloaded from \c +.URL "https://www.kernel.org/pub/linux/utils/util\-linux/" "Linux Kernel Archive" "."
\ No newline at end of file diff --git a/liblastlog2/man/ll2_write_entry.3.adoc b/liblastlog2/man/ll2_write_entry.3.adoc new file mode 100644 index 0000000..6dca1b5 --- /dev/null +++ b/liblastlog2/man/ll2_write_entry.3.adoc @@ -0,0 +1,70 @@ +//po4a: entry man manual += ll2_write_entry(3) +:doctype: manpage +:man manual: Programmer's Manual +:man source: util-linux {release-version} +:page-layout: base +:lib: liblastlog2 +:firstversion: 2.40 + +== NAME + +ll2_write_entry - Write a new entry into the database. + +== SYNOPSIS + +*#include <lastlog2.h>* + +*int ll2_write_entry (struct ll2_context *__context__, const char *__user__, + int64_t __ll_time__, const char *__tty__, + const char *__rhost__, const char *__pam_service__, + char **__error__);* + +== DESCRIPTION + +Writes an new entry into database, which is defined in _context_. +If _context_ is NULL, the default database, defined in _LL2_DEFAULT_DATABASE_, +will be taken. + + +-------------------------------------- +time_t login_time = time(0); // Get the system time +char *error = NULL; +const char *user = "root"; + +int ret = ll2_write_entry (NULL, user, login_time, "pts/0", + "192.168.122.1", NULL, &error); +-------------------------------------- + +_pam_service_ is the service or instance name which has generated the entry (optional). + +== RETURN VALUE + +Returns 0 on success, -ENOMEM or -1 on other failure. +_error_ contains an error string if the return value is -1. +_error_ is not guaranteed to contain an error string, could also be NULL. +_error_ should be freed by the caller. + +== AUTHORS + +Thorsten Kukuk (kukuk@suse.de) + +== SEE ALSO + +*lastlog2*(3), +*ll2_new_context(3), +*ll2_unref_context(3), +*ll2_read_all*(3), +*ll2_read_entry*(3), +*ll2_update_login_time*(3), +*ll2_remove_entry*(3), +*ll2_rename_user*(3), +*ll2_import_lastlog*(3) + +include::man-common/bugreports.adoc[] + +include::man-common/footer-lib.adoc[] + +ifdef::translation[] +include::man-common/translation.adoc[] +endif::[] diff --git a/liblastlog2/meson.build b/liblastlog2/meson.build new file mode 100644 index 0000000..6f8db53 --- /dev/null +++ b/liblastlog2/meson.build @@ -0,0 +1,63 @@ +cc = meson.get_compiler('c') +pkg = import('pkgconfig') +dir_liblastlog2 = include_directories('src') +lib_lastlog2_sources = ''' + src/lastlog2.h + src/lastlog2P.h + src/lastlog2.c +'''.split() + +liblastlog2_sym = 'src/liblastlog2.sym' +liblastlog2_sym_path = '@0@/@1@'.format(meson.current_source_dir(), liblastlog2_sym) + +if build_liblastlog2 + lib_lastlog2 = both_libraries( + 'lastlog2', + lib_lastlog2_sources, + include_directories : [dir_include], + link_args : ['-Wl,--version-script=@0@'.format(liblastlog2_sym_path)], + link_depends : liblastlog2_sym, + dependencies : [lib_sqlite3], + install : build_liblastlog2, + version : liblastlog2_version, + ) + + lastlog2_dep = declare_dependency(link_with: lib_lastlog2, include_directories: dir_liblastlog2) + + lastlog2_tests = [ + 'dlopen', + 'pam_lastlog2_output', + 'remove_entry', + 'rename_user', + 'write_read_user', + 'y2038_ll2_read_all', + 'y2038_sqlite3_time', + ] + libdl = cc.find_library('dl') + + pkg.generate( + lib_lastlog2, + description : 'library to manage last login data', + subdirs : 'lastlog2', + version : pc_version + ) + meson.override_dependency('lastlog2', lastlog2_dep) + + install_headers('src/lastlog2.h', subdir : 'liblastlog2') + + foreach lastlog2_test: lastlog2_tests + test_name = 'test_lastlog2_' + lastlog2_test + exe = executable( + test_name, + 'src/tests/tst_' + lastlog2_test + '.c', + include_directories : [dir_include], + link_with : [lib_common], + dependencies : [libdl, lastlog2_dep], + ) + # the test-setup expects the helpers in the toplevel build-directory + link = meson.project_build_root() / test_name + run_command('ln', '-srf', exe.full_path(), link, + check : true) + endforeach + +endif diff --git a/liblastlog2/src/Makemodule.am b/liblastlog2/src/Makemodule.am new file mode 100644 index 0000000..04b8e97 --- /dev/null +++ b/liblastlog2/src/Makemodule.am @@ -0,0 +1,99 @@ +# includes +lastlog2incdir = $(includedir)/liblastlog2 +lastlog2inc_HEADERS = liblastlog2/src/lastlog2.h + +usrlib_exec_LTLIBRARIES += liblastlog2.la + +liblastlog2_la_SOURCES = \ + liblastlog2/src/lastlog2.c \ + liblastlog2/src/lastlog2P.h + +EXTRA_liblastlog2_la_DEPENDENCIES = \ + liblastlog2/src/liblastlog2.sym + +liblastlog2_la_LIBADD = $(SQLITE3_LIBS) + +liblastlog2_la_CFLAGS = \ + $(AM_CFLAGS) \ + $(SOLIB_CFLAGS) \ + -I$(ul_liblastlog2_incdir) \ + -I$(top_srcdir)/liblastlog2/src + +liblastlog2_la_LDFLAGS = $(SOLIB_LDFLAGS) +if HAVE_VSCRIPT +liblastlog2_la_LDFLAGS += $(VSCRIPT_LDFLAGS),$(top_srcdir)/liblastlog2/src/liblastlog2.sym +endif +liblastlog2_la_LDFLAGS += -version-info $(LIBLASTLOG2_VERSION_INFO) + +EXTRA_DIST += liblastlog2/src/liblastlog2.sym + + +if BUILD_LIBLASTLOG2_TESTS +check_PROGRAMS += \ + test_lastlog2_dlopen \ + test_lastlog2_pam_lastlog2_output \ + test_lastlog2_remove_entry \ + test_lastlog2_rename_user \ + test_lastlog2_write_read_user \ + test_lastlog2_y2038_ll2_read_all \ + test_lastlog2_y2038_sqlite3_time + +lastlog2_tests_cflags = -DTEST_PROGRAM $(liblastlog2_la_CFLAGS) +lastlog2_tests_ldflags = -static +lastlog2_tests_ldadd = $(LDADD) liblastlog2.la $(SOLIB_LDFLAGS) $(SQLITE3_LIBS) + +test_lastlog2_dlopen_SOURCES = liblastlog2/src/tests/tst_dlopen.c +test_lastlog2_dlopen_CFLAGS = $(lastlog2_tests_cflags) +test_lastlog2_dlopen_LDFLAGS = $(lastlog2_tests_ldflags) -ldl +test_lastlog2_dlopen_LDADD = $(lastlog2_tests_ldadd) + +test_lastlog2_pam_lastlog2_output_SOURCES = liblastlog2/src/tests/tst_pam_lastlog2_output.c +test_lastlog2_pam_lastlog2_output_CFLAGS = $(lastlog2_tests_cflags) +test_lastlog2_pam_lastlog2_output_LDFLAGS = $(lastlog2_tests_ldflags) +test_lastlog2_pam_lastlog2_output_LDADD = $(lastlog2_tests_ldadd) + +test_lastlog2_remove_entry_SOURCES = liblastlog2/src/tests/tst_remove_entry.c +test_lastlog2_remove_entry_CFLAGS = $(lastlog2_tests_cflags) +test_lastlog2_remove_entry_LDFLAGS = $(lastlog2_tests_ldflags) +test_lastlog2_remove_entry_LDADD = $(lastlog2_tests_ldadd) + +test_lastlog2_rename_user_SOURCES = liblastlog2/src/tests/tst_rename_user.c +test_lastlog2_rename_user_CFLAGS = $(lastlog2_tests_cflags) +test_lastlog2_rename_user_LDFLAGS = $(lastlog2_tests_ldflags) +test_lastlog2_rename_user_LDADD = $(lastlog2_tests_ldadd) + +test_lastlog2_write_read_user_SOURCES = liblastlog2/src/tests/tst_write_read_user.c +test_lastlog2_write_read_user_CFLAGS = $(lastlog2_tests_cflags) +test_lastlog2_write_read_user_LDFLAGS = $(lastlog2_tests_ldflags) +test_lastlog2_write_read_user_LDADD = $(lastlog2_tests_ldadd) + +test_lastlog2_y2038_ll2_read_all_SOURCES = liblastlog2/src/tests/tst_y2038_ll2_read_all.c +test_lastlog2_y2038_ll2_read_all_CFLAGS = $(lastlog2_tests_cflags) +test_lastlog2_y2038_ll2_read_all_LDFLAGS = $(lastlog2_tests_ldflags) +test_lastlog2_y2038_ll2_read_all_LDADD = $(lastlog2_tests_ldadd) + +test_lastlog2_y2038_sqlite3_time_SOURCES = liblastlog2/src/tests/tst_y2038_sqlite3_time.c +test_lastlog2_y2038_sqlite3_time_CFLAGS = $(lastlog2_tests_cflags) +test_lastlog2_y2038_sqlite3_time_LDFLAGS = $(lastlog2_tests_ldflags) +test_lastlog2_y2038_sqlite3_time_LDADD = $(lastlog2_tests_ldadd) + +endif #BUILD_LIBLIBLASTLOG2_TESTS + + +# move lib from $(usrlib_execdir) to $(libdir) if needed +install-exec-hook-liblastlog2: + if test "$(usrlib_execdir)" != "$(libdir)" -a -f "$(DESTDIR)$(usrlib_execdir)/liblastlog2.so"; then \ + $(MKDIR_P) $(DESTDIR)$(libdir); \ + mv $(DESTDIR)$(usrlib_execdir)/liblastlog2.so.* $(DESTDIR)$(libdir); \ + so_img_name=$$(readlink $(DESTDIR)$(usrlib_execdir)/liblastlog2.so); \ + so_img_rel_target=$$(echo $(usrlib_execdir) | sed 's,\(^/\|\)[^/][^/]*,..,g'); \ + (cd $(DESTDIR)$(usrlib_execdir) && \ + rm -f liblastlog2.so && \ + $(LN_S) $$so_img_rel_target$(libdir)/$$so_img_name liblastlog2.so); \ + fi + +uninstall-hook-liblastlog2: + rm -f $(DESTDIR)$(libdir)/liblastlog2.so* + +INSTALL_EXEC_HOOKS += install-exec-hook-liblastlog2 +UNINSTALL_HOOKS += uninstall-hook-liblastlog2 diff --git a/liblastlog2/src/lastlog2.c b/liblastlog2/src/lastlog2.c new file mode 100644 index 0000000..744d41f --- /dev/null +++ b/liblastlog2/src/lastlog2.c @@ -0,0 +1,595 @@ +/* SPDX-License-Identifier: BSD-2-Clause + + Copyright (c) 2023, Thorsten Kukuk <kukuk@suse.com> + + 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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 <pwd.h> +#include <errno.h> +#include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <sys/stat.h> +#include <sqlite3.h> +#include <lastlog.h> + +#include "lastlog2P.h" +#include "strutils.h" + +/* Set the ll2 context/environment */ +/* Returns the context or NULL if an error has happened. */ +extern struct ll2_context * ll2_new_context(const char *db_path) +{ + struct ll2_context *context = (struct ll2_context *)malloc(sizeof(struct ll2_context)); + + if (context) { + if (db_path) { + if ((context->lastlog2_path = strdup(db_path)) == NULL) { + free(context); + context = NULL; + } + } else { + if ((context->lastlog2_path = strdup(LL2_DEFAULT_DATABASE)) == NULL) { + free(context); + context = NULL; + } + } + } + return context; +} + +/* Release ll2 context/environment */ +extern void ll2_unref_context(struct ll2_context *context) +{ + if (context) + free(context->lastlog2_path); + free(context); +} + +/* Returns 0 on success, -ENOMEM or -1 on other failure. */ +static int +open_database_ro(struct ll2_context *context, sqlite3 **db, char **error) +{ + int ret = 0; + char *path = LL2_DEFAULT_DATABASE; + + if (context && context->lastlog2_path) + path = context->lastlog2_path; + + if (sqlite3_open_v2(path, db, SQLITE_OPEN_READONLY, NULL) != SQLITE_OK) { + ret = -1; + if (error) + if (asprintf(error, "Cannot open database (%s): %s", + path, sqlite3_errmsg(*db)) < 0) + ret = -ENOMEM; + + sqlite3_close(*db); + } + + return ret; +} + +/* Returns 0 on success, -ENOMEM or -1 on other failure. */ +static int +open_database_rw(struct ll2_context *context, sqlite3 **db, char **error) +{ + int ret = 0; + char *path = LL2_DEFAULT_DATABASE; + + if (context && context->lastlog2_path) + path = context->lastlog2_path; + + if (sqlite3_open(path, db) != SQLITE_OK) { + ret = -1; + if (error) + if (asprintf(error, "Cannot create/open database (%s): %s", + path, sqlite3_errmsg(*db)) < 0) + ret = -ENOMEM; + + sqlite3_close(*db); + } + + return ret; +} + +/* Reads one entry from database and returns that. + Returns 0 on success, -ENOMEM or -1 on other failure. */ +static int +read_entry(sqlite3 *db, const char *user, + int64_t *ll_time, char **tty, char **rhost, + char **pam_service, char **error) +{ + int retval = 0; + sqlite3_stmt *res = NULL; + static const char *sql = "SELECT Name,Time,TTY,RemoteHost,Service FROM Lastlog2 WHERE Name = ?"; + + if (sqlite3_prepare_v2(db, sql, -1, &res, 0) != SQLITE_OK) { + retval = -1; + if (error) + if (asprintf(error, "Failed to execute statement: %s", + sqlite3_errmsg(db)) < 0) + retval = -ENOMEM; + goto out_read_entry; + } + + if (sqlite3_bind_text(res, 1, user, -1, SQLITE_STATIC) != SQLITE_OK) { + retval = -1; + if (error) + if (asprintf(error, "Failed to create search query: %s", + sqlite3_errmsg(db)) < 0) + retval = -ENOMEM; + goto out_read_entry; + } + + int step = sqlite3_step(res); + + if (step == SQLITE_ROW) { + const unsigned char *luser = sqlite3_column_text(res, 0); + const unsigned char *uc; + + if (strcmp((const char *)luser, user) != 0) { + retval = -1; + if (error) + if (asprintf(error, "Returned data is for %s, not %s", luser, user) < 0) + retval = -ENOMEM; + goto out_read_entry; + } + + if (ll_time) + *ll_time = sqlite3_column_int64(res, 1); + + if (tty) { + uc = sqlite3_column_text(res, 2); + if (uc != NULL && strlen((const char *)uc) > 0) + if ((*tty = strdup((const char *)uc)) == NULL) { + retval = -ENOMEM; + goto out_read_entry; + } + } + if (rhost) { + uc = sqlite3_column_text(res, 3); + if (uc != NULL && strlen((const char *)uc) > 0) + if ((*rhost = strdup((const char *)uc)) == NULL) { + retval = -ENOMEM; + goto out_read_entry; + } + } + if (pam_service) { + uc = sqlite3_column_text(res, 4); + if (uc != NULL && strlen((const char *)uc) > 0) + if ((*pam_service = strdup((const char *)uc)) == NULL) { + retval = -ENOMEM; + goto out_read_entry; + } + } + } else { + retval = -1; + if (error) + if (asprintf(error, "User '%s' not found (%d)", user, step) < 0) + retval = -ENOMEM; + } + +out_read_entry: + if (res) + sqlite3_finalize(res); + + return retval; +} + +/* reads 1 entry from database and returns that. Returns 0 on success, -ENOMEM or -1 on other failure. */ +int +ll2_read_entry(struct ll2_context *context, const char *user, + int64_t *ll_time, char **tty, char **rhost, + char **pam_service, char **error) +{ + sqlite3 *db; + int retval; + + if ((retval = open_database_ro(context, &db, error)) != 0) + return retval; + + retval = read_entry(db, user, ll_time, tty, rhost, pam_service, error); + + sqlite3_close(db); + + return retval; +} + +/* Write a new entry. Returns 0 on success, -ENOMEM or -1 on other failure. */ +static int +write_entry(sqlite3 *db, const char *user, + int64_t ll_time, const char *tty, const char *rhost, + const char *pam_service, char **error) +{ + int retval = 0; + char *err_msg = NULL; + sqlite3_stmt *res = NULL; + static const char *sql_table = "CREATE TABLE IF NOT EXISTS Lastlog2(Name TEXT PRIMARY KEY, Time INTEGER, TTY TEXT, RemoteHost TEXT, Service TEXT);"; + static const char *sql_replace = "REPLACE INTO Lastlog2 VALUES(?,?,?,?,?);"; + + if (sqlite3_exec(db, sql_table, 0, 0, &err_msg) != SQLITE_OK) { + retval = -1; + if (error) + if (asprintf(error, "SQL error: %s", err_msg) < 0) + retval = -ENOMEM; + + sqlite3_free(err_msg); + goto out_ll2_read_entry; + } + + if (sqlite3_prepare_v2(db, sql_replace, -1, &res, 0) != SQLITE_OK) { + retval = -1; + if (error) + if (asprintf(error, "Failed to execute statement: %s", + sqlite3_errmsg(db)) < 0) + retval = -ENOMEM; + goto out_ll2_read_entry; + } + + if (sqlite3_bind_text(res, 1, user, -1, SQLITE_STATIC) != SQLITE_OK) { + retval = -1; + if (error) + if (asprintf(error, "Failed to create replace statement for user: %s", + sqlite3_errmsg(db)) < 0) + retval = -ENOMEM; + goto out_ll2_read_entry; + } + + if (sqlite3_bind_int64(res, 2, ll_time) != SQLITE_OK) { + retval = -1; + if (error) + if (asprintf(error, "Failed to create replace statement for ll_time: %s", + sqlite3_errmsg(db)) < 0) + retval = -ENOMEM; + goto out_ll2_read_entry; + } + + if (sqlite3_bind_text(res, 3, tty, -1, SQLITE_STATIC) != SQLITE_OK) { + retval = -1; + if (error) + if (asprintf(error, "Failed to create replace statement for tty: %s", + sqlite3_errmsg(db)) < 0) + retval = -ENOMEM; + goto out_ll2_read_entry; + } + + if (sqlite3_bind_text(res, 4, rhost, -1, SQLITE_STATIC) != SQLITE_OK) { + retval = -1; + if (error) + if (asprintf(error, "Failed to create replace statement for rhost: %s", + sqlite3_errmsg(db)) < 0) + retval = -ENOMEM; + goto out_ll2_read_entry; + } + + if (sqlite3_bind_text(res, 5, pam_service, -1, SQLITE_STATIC) != SQLITE_OK) { + retval = -1; + if (error) + if (asprintf(error, "Failed to create replace statement for PAM service: %s", + sqlite3_errmsg(db)) < 0) + retval = -ENOMEM; + goto out_ll2_read_entry; + } + + int step = sqlite3_step(res); + + if (step != SQLITE_DONE) { + retval = -1; + if (error) + if (asprintf(error, "Delete statement did not return SQLITE_DONE: %d", + step) < 0) + retval = -ENOMEM; + goto out_ll2_read_entry; + } +out_ll2_read_entry: + if (res) + sqlite3_finalize(res); + + return retval; +} + +/* Write a new entry. Returns 0 on success, -ENOMEM or -1 on other failure. */ +int +ll2_write_entry(struct ll2_context *context, const char *user, + int64_t ll_time, const char *tty, const char *rhost, + const char *pam_service, char **error) +{ + sqlite3 *db; + int retval; + + if ((retval = open_database_rw(context, &db, error)) != 0) + return retval; + + retval = write_entry(db, user, ll_time, tty, rhost, pam_service, error); + + sqlite3_close(db); + + return retval; +} + +/* Write a new entry with updated login time. + Returns 0 on success, -ENOMEM or -1 on other failure. */ +int +ll2_update_login_time(struct ll2_context *context, const char *user, + int64_t ll_time, char **error) +{ + sqlite3 *db; + int retval; + char *tty; + char *rhost; + char *pam_service; + + if ((retval = open_database_rw(context , &db, error)) != 0) + return retval; + + if ((retval = read_entry(db, user, 0, &tty, &rhost, &pam_service, error)) != 0) { + sqlite3_close(db); + return retval; + } + + retval = write_entry(db, user, ll_time, tty, rhost, pam_service, error); + + sqlite3_close(db); + + free(tty); + free(rhost); + free(pam_service); + + return retval; +} + + +typedef int (*callback_f)(const char *user, int64_t ll_time, + const char *tty, const char *rhost, + const char *pam_service, const char *cb_error); + +static int +callback(void *cb_func, __attribute__((unused)) int argc, char **argv, __attribute__((unused)) char **azColName) +{ + char *endptr; + callback_f print_entry = cb_func; + + errno = 0; + char *cb_error = NULL; + int64_t ll_time = strtoll(argv[1], &endptr, 10); + if ((errno == ERANGE && (ll_time == INT64_MAX || ll_time == INT64_MIN)) + || (endptr == argv[1]) || (*endptr != '\0')) + if (asprintf(&cb_error, "Invalid numeric time entry for '%s': '%s'\n", argv[0], argv[1]) < 0) + return -1; + + print_entry(argv[0], ll_time, argv[2], argv[3], argv[4], cb_error); + free(cb_error); + + return 0; +} + +/* Reads all entries from database and calls the callback function for each entry. + Returns 0 on success, -ENOMEM or -1 on other failure. */ +int +ll2_read_all(struct ll2_context *context, + int (*cb_func)(const char *user, int64_t ll_time, + const char *tty, const char *rhost, + const char *pam_service, const char *cb_error), + char **error) +{ + sqlite3 *db; + char *err_msg = 0; + int retval = 0; + + if ((retval = open_database_ro(context, &db, error)) != 0) + return retval; + + static const char *sql = "SELECT Name,Time,TTY,RemoteHost,Service FROM Lastlog2 ORDER BY Name ASC"; + + if (sqlite3_exec(db, sql, callback, cb_func, &err_msg) != SQLITE_OK) { + retval = -1; + if (error) + if (asprintf(error, "SQL error: %s", err_msg) < 0) + retval = -ENOMEM; + + sqlite3_free(err_msg); + } + + sqlite3_close(db); + + return retval; +} + +/* Remove an user entry. Returns 0 on success, -ENOMEM or -1 on other failure. */ +static int +remove_entry(sqlite3 *db, const char *user, char **error) +{ + int retval = 0; + sqlite3_stmt *res = NULL; + static const char *sql = "DELETE FROM Lastlog2 WHERE Name = ?"; + + if (sqlite3_prepare_v2(db, sql, -1, &res, 0) != SQLITE_OK) { + if (error) + if (asprintf(error, "Failed to execute statement: %s", + sqlite3_errmsg(db)) < 0) + return -ENOMEM; + + return -1; + } + + if (sqlite3_bind_text(res, 1, user, -1, SQLITE_STATIC) != SQLITE_OK) { + retval = -1; + if (error) + if (asprintf(error, "Failed to create delete statement: %s", + sqlite3_errmsg(db)) < 0) + retval = -ENOMEM; + goto out_remove_entry; + } + + int step = sqlite3_step(res); + + if (step != SQLITE_DONE) { + retval = -1; + if (error) + if (asprintf(error, "Delete statement did not return SQLITE_DONE: %d", + step) < 0) + retval = -ENOMEM; + } +out_remove_entry: + if (res) + sqlite3_finalize(res); + + return retval; +} + +/* Remove an user entry. Returns 0 on success, -ENOMEM or -1 on other failure. */ +int +ll2_remove_entry(struct ll2_context *context, const char *user, + char **error) +{ + sqlite3 *db; + int retval; + + if ((retval = open_database_rw(context, &db, error)) != 0) + return retval; + + retval = remove_entry(db, user, error); + + sqlite3_close(db); + + return retval; +} + +/* Renames an user entry. Returns 0 on success, -ENOMEM or -1 on other failure. */ +int +ll2_rename_user(struct ll2_context *context, const char *user, + const char *newname, char **error) +{ + sqlite3 *db; + int64_t ll_time; + char *tty; + char *rhost; + char *pam_service; + int retval; + + if ((retval = open_database_rw(context, &db, error)) != 0) + return retval; + + if ((retval = read_entry(db, user, &ll_time, &tty, &rhost, &pam_service, error) != 0)) { + sqlite3_close(db); + return retval; + } + + if ((retval = write_entry(db, newname, ll_time, tty, rhost, pam_service, error) != 0)) { + sqlite3_close(db); + free(tty); + free(rhost); + return retval; + } + + retval = remove_entry(db, user, error); + + sqlite3_close(db); + + free(tty); + free(rhost); + free(pam_service); + + return retval; +} + +/* Import old lastlog file. + Returns 0 on success, -ENOMEM or -1 on other failure. */ +int +ll2_import_lastlog(struct ll2_context *context, const char *lastlog_file, + char **error) +{ + const struct passwd *pw; + struct stat statll; + sqlite3 *db; + FILE *ll_fp; + int retval = 0; + + if ((retval = open_database_rw(context, &db, error)) != 0) + return retval; + + ll_fp = fopen(lastlog_file, "r"); + if (ll_fp == NULL) { + if (error && asprintf(error, "Failed to open '%s': %s", + lastlog_file, strerror(errno)) < 0) + return -ENOMEM; + + return -1; + } + + + if (fstat(fileno(ll_fp), &statll) != 0) { + retval = -1; + if (error && asprintf(error, "Cannot get size of '%s': %s", + lastlog_file, strerror(errno)) < 0) + retval = -ENOMEM; + + goto done; + } + + setpwent(); + while ((pw = getpwent()) != NULL ) { + off_t offset; + struct lastlog ll; + + offset = (off_t) pw->pw_uid * sizeof (ll); + + if ((offset + (off_t)sizeof(ll)) <= statll.st_size) { + if (fseeko(ll_fp, offset, SEEK_SET) == -1) + continue; /* Ignore seek error */ + + if (fread(&ll, sizeof(ll), 1, ll_fp) != 1) { + retval = -1; + if (error) + if (asprintf(error, "Failed to get the entry for UID '%lu'", + (unsigned long int)pw->pw_uid) < 0) + retval = -ENOMEM; + goto out_import_lastlog; + } + + if (ll.ll_time != 0) { + int64_t ll_time; + char tty[sizeof(ll.ll_line) + 1]; + char rhost[sizeof(ll.ll_host) + 1]; + + ll_time = ll.ll_time; + mem2strcpy(tty, ll.ll_line, sizeof(ll.ll_line), sizeof(tty)); + mem2strcpy(rhost, ll.ll_host, sizeof(ll.ll_host), sizeof(rhost)); + + if ((retval = write_entry(db, pw->pw_name, ll_time, tty, + rhost, NULL, error)) != 0) + goto out_import_lastlog; + } + } + } +out_import_lastlog: + endpwent(); + sqlite3_close(db); +done: + fclose(ll_fp); + + return retval; +} diff --git a/liblastlog2/src/lastlog2.h b/liblastlog2/src/lastlog2.h new file mode 100644 index 0000000..280f387 --- /dev/null +++ b/liblastlog2/src/lastlog2.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: BSD-2-Clause + + Copyright (c) 2023, Thorsten Kukuk <kukuk@suse.com> + + 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. +*/ + +#ifndef _LIBLASTLOG2_H +#define _LIBLASTLOG2_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define LL2_DEFAULT_DATABASE _PATH_LOCALSTATEDIR "/lib/lastlog/lastlog2.db" + +#include <stdint.h> + +struct ll2_context; + +/* Set the ll2 context/environment */ +/* Returns the context or NULL if an error has happened. */ +extern struct ll2_context * ll2_new_context(const char *db_path); + +/* Release ll2 context/environment */ +extern void ll2_unref_context(struct ll2_context *context); + +/* Writes a new entry. Returns 0 on success, -ENOMEM or -1 on other failure. */ +extern int ll2_write_entry (struct ll2_context *context, const char *user, + int64_t ll_time, const char *tty, + const char *rhost, const char *pam_service, + char **error); + +/* Calling a defined function for each entry. Returns 0 on success, -ENOMEM or -1 on other failure. */ +extern int ll2_read_all (struct ll2_context *context, + int (*callback)(const char *user, int64_t ll_time, + const char *tty, const char *rhost, + const char *pam_service, const char *cb_error), + char **error); + +/* Reads one entry from database and returns that. + Returns 0 on success, -ENOMEM or -1 on other failure. */ +extern int ll2_read_entry (struct ll2_context *context, const char *user, + int64_t *ll_time, char **tty, char **rhost, + char **pam_service, char **error); + +/* Write a new entry with updated login time. + Returns 0 on success, -ENOMEM or -1 on other failure. */ +extern int ll2_update_login_time (struct ll2_context *context, + const char *user, int64_t ll_time, + char **error); + +/* Remove an user entry. Returns 0 on success, -ENOMEM or -1 on other failure. */ +extern int ll2_remove_entry (struct ll2_context *context, const char *user, + char **error); + +/* Renames an user entry. Returns 0 on success, -ENOMEM or -1 on other failure. */ +extern int ll2_rename_user (struct ll2_context *context, const char *user, + const char *newname, char **error); + + +/* Import old lastlog file. + Returns 0 on success, -ENOMEM or -1 on other failure. */ +extern int ll2_import_lastlog (struct ll2_context *context, + const char *lastlog_file, char **error); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBLASTLOG2_H */ diff --git a/liblastlog2/src/lastlog2P.h b/liblastlog2/src/lastlog2P.h new file mode 100644 index 0000000..b39f660 --- /dev/null +++ b/liblastlog2/src/lastlog2P.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: BSD-2-Clause + + Copyright (c) 2024, Thorsten Kukuk <kukuk@suse.com> + + 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. +*/ + +#ifndef _LIBLASTLOG2_P_H +#define _LIBLASTLOG2_P_H + +#include "lastlog2.h" + +struct ll2_context { + char *lastlog2_path; +}; + +#endif /* _LIBLASTLOG2_P_H */ diff --git a/liblastlog2/src/liblastlog2.sym b/liblastlog2/src/liblastlog2.sym new file mode 100644 index 0000000..c21cced --- /dev/null +++ b/liblastlog2/src/liblastlog2.sym @@ -0,0 +1,13 @@ +LIBLASTLOG2_2_40 { + global: + ll2_new_context; + ll2_unref_context; + ll2_read_all; + ll2_read_entry; + ll2_remove_entry; + ll2_rename_user; + ll2_write_entry; + ll2_update_login_time; + ll2_import_lastlog; + local: *; +}; diff --git a/liblastlog2/src/tests/tst_dlopen.c b/liblastlog2/src/tests/tst_dlopen.c new file mode 100644 index 0000000..112564b --- /dev/null +++ b/liblastlog2/src/tests/tst_dlopen.c @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + + Copyright (C) Nalin Dahyabhai <nalin@redhat.com> 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. +*/ + +#include <dlfcn.h> +#include <stdio.h> +#include <limits.h> +#include <sys/stat.h> + +/* Simple program to see if dlopen() would succeed. */ +int main(int argc, char **argv) +{ + int i; + struct stat st; + char buf[PATH_MAX]; + + for (i = 1; i < argc; i++) { + if (dlopen(argv[i], RTLD_NOW)) { + fprintf(stdout, "dlopen() of \"%s\" succeeded.\n", + argv[i]); + } else { + snprintf(buf, sizeof(buf), "./%s", argv[i]); + if ((stat(buf, &st) == 0) && dlopen(buf, RTLD_NOW)) { + fprintf(stdout, "dlopen() of \"./%s\" " + "succeeded.\n", argv[i]); + } else { + fprintf(stdout, "dlopen() of \"%s\" failed: " + "%s\n", argv[i], dlerror()); + return 1; + } + } + } + return 0; +} diff --git a/liblastlog2/src/tests/tst_pam_lastlog2_output.c b/liblastlog2/src/tests/tst_pam_lastlog2_output.c new file mode 100644 index 0000000..1fd1eec --- /dev/null +++ b/liblastlog2/src/tests/tst_pam_lastlog2_output.c @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: BSD-2-Clause + + Copyright (c) 2023, Thorsten Kukuk <kukuk@suse.com> + + 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. +*/ + +/* Test case: + Store defined data into the database, read it, create the time + string like pam_lastlog2 does, compare the result. +*/ + +#include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> + +#include "lastlog2P.h" + +const char *expected = "Last login: Mon Mar 13 07:13:41 UTC 2023 from 192.168.122.1 on pts/0"; +const time_t login_time = 1678691621; +int +main(void) +{ + const char *user = "root"; + struct ll2_context *context = ll2_new_context("pam_lastlog2-output.db"); + int64_t ll_time = 0; + char *tty = NULL; + char *rhost = NULL; + char *date = NULL; + char the_time[256]; + char *error = NULL; + char *output = NULL; + + if (ll2_write_entry (context, user, login_time, "pts/0", + "192.168.122.1", NULL, &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } else + fprintf (stderr, "ll2_write_entry failed\n"); + ll2_unref_context(context); + return 1; + } + + if (ll2_read_entry (context, user, &ll_time, &tty, &rhost, + NULL, &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } else + fprintf (stderr, "Unknown error reading database %s", context ? context->lastlog2_path : "NULL"); + ll2_unref_context(context); + return 1; + } + + if (ll_time) { + struct tm *tm, tm_buf; + /* this is necessary if you compile this on architectures with + a 32bit time_t type. */ + time_t t_time = ll_time; + + if ((tm = localtime_r (&t_time, &tm_buf)) != NULL) { + strftime (the_time, sizeof (the_time), + " %a %b %e %H:%M:%S %Z %Y", tm); + date = the_time; + } + } + + if (asprintf (&output, "Last login:%s%s%s%s%s", + date ? date : "", + rhost ? " from " : "", + rhost ? rhost : "", + tty ? " on " : "", + tty ? tty : "") < 0) { + fprintf (stderr, "Out of memory!\n"); + ll2_unref_context(context); + return 1; + } + + if (strcmp (output, expected) != 0) { + fprintf (stderr, "Output '%s'\n does not match '%s'\n", + output, expected); + ll2_unref_context(context); + return 1; + } + + ll2_unref_context(context); + free (output); + free (tty); + free (rhost); + + return 0; +} diff --git a/liblastlog2/src/tests/tst_remove_entry.c b/liblastlog2/src/tests/tst_remove_entry.c new file mode 100644 index 0000000..39079c8 --- /dev/null +++ b/liblastlog2/src/tests/tst_remove_entry.c @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: BSD-2-Clause + + Copyright (c) 2023, Thorsten Kukuk <kukuk@suse.com> + + 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. +*/ + +/* Test case: + Create an entry, delete that entry, and try to read entry again. + Reading the entry should fail. +*/ + +#include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lastlog2.h" + +int +main(void) +{ + const char *user = "user"; + struct ll2_context *context = ll2_new_context("tst-delete-user.db"); + int64_t ll_time = 0; + char *tty = NULL; + char *rhost = NULL; + char *service = NULL; + char *error = NULL; + + if (ll2_write_entry (context, user, time (NULL), "test-tty", + "localhost", "sshd", &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } else + fprintf (stderr, "ll2_write_entry failed\n"); + ll2_unref_context(context); + return 1; + } + + if (ll2_remove_entry (context, user, &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } else + fprintf (stderr, "ll2_remove_entry failed\n"); + ll2_unref_context(context); + return 1; + } + + /* this needs to fail, as the old entry shouldn't exist anymore. */ + if (ll2_read_entry (context, user, &ll_time, &tty, &rhost, &service, &error) == 0) { + fprintf (stderr, "Reading old user from database did not fail!\n"); + fprintf (stderr, "ll_time=%lld, tty='%s', rhost='%s', service='%s'\n", + (long long int)ll_time, tty, rhost, service); + ll2_unref_context(context); + return 1; + } + + ll2_unref_context(context); + free (error); + free (tty); + free (rhost); + free (service); + + return 0; +} diff --git a/liblastlog2/src/tests/tst_rename_user.c b/liblastlog2/src/tests/tst_rename_user.c new file mode 100644 index 0000000..a1788b5 --- /dev/null +++ b/liblastlog2/src/tests/tst_rename_user.c @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: BSD-2-Clause + + Copyright (c) 2023, Thorsten Kukuk <kukuk@suse.com> + + 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. +*/ + +/* Test case: + Create an entry, rename that entry, and try to read the old and + new entry again. Reading the old entry should fail. +*/ + +#include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lastlog2P.h" + +int +main(void) +{ + const char *user = "user"; + const char *newname = "new"; + struct ll2_context *context = ll2_new_context("tst-rename-user.db"); + int64_t ll_time = 0; + char *tty = NULL; + char *rhost = NULL; + char *service = NULL; + char *error = NULL; + + if (ll2_write_entry (context, user, time (NULL), "test-tty", + "localhost", "test-service", &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } + else + fprintf (stderr, "ll2_write_entry failed\n"); + ll2_unref_context(context); + return 1; + } + + if (ll2_rename_user (context, user, newname, &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } + else + fprintf (stderr, "ll2_rename_user failed\n"); + ll2_unref_context(context); + return 1; + } + + /* this needs to fail, as the old entry shouldn't exist anymore. */ + if (ll2_read_entry (context, user, &ll_time, &tty, &rhost, + &service, &error) == 0) { + fprintf (stderr, "Reading old user from database did not fail!\n"); + fprintf (stderr, "ll_time=%lld, tty='%s', rhost='%s', service='%s'\n", + (long long int)ll_time, tty, rhost, service); + ll2_unref_context(context); + return 1; + } + + if (error) { + free (error); + error = NULL; + } + + if (ll2_read_entry (context, newname, &ll_time, &tty, &rhost, &service, + &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } else + fprintf (stderr, "Unknown error reading database %s", context->lastlog2_path); + ll2_unref_context(context); + return 1; + } + + if (strcmp (tty, "test-tty") != 0 || strcmp (rhost, "localhost") != 0 || + strcmp (service, "test-service") != 0) { + fprintf (stderr, "New entry data does not match old entry data!\n"); + } + + ll2_unref_context(context); + free (tty); + free (rhost); + free (service); + + return 0; +} diff --git a/liblastlog2/src/tests/tst_write_read_user.c b/liblastlog2/src/tests/tst_write_read_user.c new file mode 100644 index 0000000..dbf1db7 --- /dev/null +++ b/liblastlog2/src/tests/tst_write_read_user.c @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: BSD-2-Clause + + Copyright (c) 2023, Thorsten Kukuk <kukuk@suse.com> + + 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. +*/ + +/* Test case: + Create an entry, rename that entry, and try to read the old and + new entry again. Reading the old entry should fail. +*/ + +#include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "lastlog2P.h" + +static int +test_args (struct ll2_context *context, const char *user, int64_t ll_time, + const char *tty, const char *rhost, const char *service) +{ + char *error = NULL; + int64_t res_time; + char *res_tty = NULL; + char *res_rhost = NULL; + char *res_service = NULL; + + if (ll2_write_entry (context, user, ll_time, tty, rhost, service, &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } else + fprintf (stderr, "ll2_write_entry failed\n"); + return 1; + } + + if (ll2_read_entry (context, user, &res_time, &res_tty, &res_rhost, &res_service, &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } else + fprintf (stderr, "Unknown error reading database %s", context->lastlog2_path); + return 1; + } + + if (ll_time != res_time) { + fprintf (stderr, "Wrong time: got %lld, expect %lld\n", + (long long int)res_time, (long long int)ll_time); + return 1; + } + + if ((tty == NULL && res_tty != NULL) || + (tty != NULL && res_tty == NULL) || + (tty != NULL && res_tty != NULL && strcmp (tty, res_tty) != 0)) { + fprintf (stderr, "Wrong tty: got %s, expect %s\n", tty, res_tty); + return 1; + } + + if ((rhost == NULL && res_rhost != NULL) || + (rhost != NULL && res_rhost == NULL) || + (rhost != NULL && res_rhost != NULL && strcmp (rhost, res_rhost) != 0)) { + fprintf (stderr, "Wrong rhost: got %s, expect %s\n", rhost, res_rhost); + return 1; + } + + if ((service == NULL && res_service != NULL) || + (service != NULL && res_service == NULL) || + (service != NULL && res_service != NULL && strcmp (service, res_service) != 0)) { + fprintf (stderr, "Wrong service: got %s, expect %s\n", service, res_service); + return 1; + } + + + free (res_tty); + free (res_rhost); + free (res_service); + + return 0; +} + +int +main(void) +{ + struct ll2_context *context = ll2_new_context("tst-write-read-user.db"); + char *error = NULL; + int64_t res_time; + char *res_tty = NULL; + char *res_rhost = NULL; + char *res_service = NULL; + + if (test_args (context, "user1", time (NULL), "test-tty", "localhost", "test") != 0) { + ll2_unref_context(context); + return 1; + } + if (test_args (context, "user2", 0, NULL, NULL, NULL) != 0) { + ll2_unref_context(context); + return 1; + } + if (test_args (context, "user3", time (NULL), NULL, NULL, NULL) != 0) { + ll2_unref_context(context); + return 1; + } + if (test_args (context, "user4", time (NULL), "test-tty", NULL, NULL) != 0) { + ll2_unref_context(context); + return 1; + } + if (test_args (context, "user5", time (NULL), NULL, "localhost", NULL) != 0) { + ll2_unref_context(context); + return 1; + } + + /* Checking errno if the db file does not exist */ + struct ll2_context *context_not_found = ll2_new_context("no_file"); + if (ll2_read_entry (context_not_found, "user", &res_time, &res_tty, &res_rhost, &res_service, &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } else + fprintf (stderr, "Couldn't read entries for all users\n"); + + if(errno) { + if (errno == ENOENT) + { + fprintf (stderr, "Returning the correct errno: %s\n", + strerror (errno)); + ll2_unref_context(context_not_found); + return 0; + } + fprintf (stderr, "errno: %s\n", + strerror (errno)); + } else { + fprintf (stderr, "errno: NULL\n"); + } + + ll2_unref_context(context_not_found); + ll2_unref_context(context); + return 1; + } + + ll2_unref_context(context); + return 0; +} diff --git a/liblastlog2/src/tests/tst_y2038_ll2_read_all.c b/liblastlog2/src/tests/tst_y2038_ll2_read_all.c new file mode 100644 index 0000000..014ae9d --- /dev/null +++ b/liblastlog2/src/tests/tst_y2038_ll2_read_all.c @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: BSD-2-Clause + + Copyright (c) 2023, Thorsten Kukuk <kukuk@suse.com> + + 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. +*/ + +/* Test case: + Create an entry with an 3*INT32_MAX timestamp, store that, + read that via ll2_read_all callback again and make sure the + timestamp is correct. +*/ + +#include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> + +#include "lastlog2P.h" + +#define BIG_TIME_VALUE ((int64_t)3*INT32_MAX) + +const char *user = "y2038"; +const char *on_tty = "pts/test"; +const char *rhost = NULL; +const char *service = "sshd"; + +static int +check_y2038 (const char *res_user, int64_t ll_time, const char *res_tty, + const char *res_rhost, const char *res_service, const char *error) +{ + + if (strcmp (user, res_user) != 0) { + fprintf (stderr, "write/read entry user mismatch: written: %s, got: %s\n", + user, res_user); + exit (1); + } + + if (ll_time != BIG_TIME_VALUE) { + fprintf (stderr, "write/read entry time mismatch: written: %lld, got: %lld\n", + (long long int)BIG_TIME_VALUE, (long long int)ll_time); + exit (1); + } + + if (strcmp (on_tty, res_tty) != 0) { + fprintf (stderr, "write/read entry tty mismatch: written: %s, got: %s\n", + on_tty, res_tty); + exit (1); + } + + if (rhost != NULL) { + fprintf (stderr, "write/read entry rhost mismatch: written: NULL, got: %s\n", + res_rhost); + exit (1); + } + + if (strcmp (service, res_service) != 0) { + fprintf (stderr, "write/read entry service mismatch: written: %s, got: %s\n", + service, res_service); + exit (1); + } + + if (error != NULL) { + fprintf (stderr, "got error: %s\n", + error); + exit (1); + } + + return 0; +} + +int +main(void) +{ + struct ll2_context *context = ll2_new_context("y2038-ll2_read_all.db"); + char *error = NULL; + + remove (context->lastlog2_path); + + printf ("Big time value is: %lld\n", (long long int)BIG_TIME_VALUE); + + if (ll2_write_entry (context, user, BIG_TIME_VALUE, on_tty, rhost, service, + &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } + else + fprintf (stderr, "ll2_write_entry failed\n"); + ll2_unref_context(context); + return 1; + } + + if (ll2_read_all (context, check_y2038, &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } else + fprintf (stderr, "Couldn't read entries for all users\n"); + ll2_unref_context(context); + return 1; + } + + /* Checking errno if the db file does not exist */ + remove (context->lastlog2_path); + + if (ll2_read_all (context, check_y2038, &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } else + fprintf (stderr, "Couldn't read entries for all users\n"); + + if(errno) { + if (errno == ENOENT) + { + fprintf (stderr, "Returning the correct errno: %s\n", + strerror (errno)); + ll2_unref_context(context); + return 0; + } + fprintf (stderr, "errno: %s\n", + strerror (errno)); + } else { + fprintf (stderr, "errno: NULL\n"); + } + + ll2_unref_context(context); + return 1; + } + + ll2_unref_context(context); + return 0; +} diff --git a/liblastlog2/src/tests/tst_y2038_sqlite3_time.c b/liblastlog2/src/tests/tst_y2038_sqlite3_time.c new file mode 100644 index 0000000..a296634 --- /dev/null +++ b/liblastlog2/src/tests/tst_y2038_sqlite3_time.c @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: BSD-2-Clause + + Copyright (c) 2023, Thorsten Kukuk <kukuk@suse.com> + + 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. +*/ + +/* Test case: + Create an entry with an INT64_MAX-1000 timestamp, store that, + read that again and make sure the timestamp is correct. +*/ + +#include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> + +#include "lastlog2P.h" + +#define BIG_TIME_VALUE (INT64_MAX - 1000) + +int +main(void) +{ + const char *user = "y2038"; + struct ll2_context *context = ll2_new_context("y2038-sqlite3-time.db"); + int64_t ll_time = 0; + char *error = NULL; + + printf ("Big time value is: %lld\n", (long long int)BIG_TIME_VALUE); + + if (ll2_write_entry (context, user, BIG_TIME_VALUE, NULL, NULL, + NULL, &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } else + fprintf (stderr, "ll2_write_entry failed\n"); + ll2_unref_context(context); + return 1; + } + + if (ll2_read_entry (context, user, &ll_time, NULL, NULL, NULL, + &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } else + fprintf (stderr, "Unknown error reading database %s", context->lastlog2_path); + ll2_unref_context(context); + return 1; + } + + if (ll_time != BIG_TIME_VALUE) { + fprintf (stderr, "write/read entry time mismatch: written: %lld, got: %lld\n", + (long long int)BIG_TIME_VALUE, (long long int)ll_time); + ll2_unref_context(context); + return 1; + } + + ll2_unref_context(context); + return 0; +} |