]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/lastlog2.c
Merge branch 'getwc' of https://github.com/t-8ch/util-linux
[thirdparty/util-linux.git] / misc-utils / lastlog2.c
CommitLineData
c2e299d0
SS
1/* SPDX-License-Identifier: BSD-2-Clause
2
3 Copyright (c) 2023, Thorsten Kukuk <kukuk@suse.com>
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8 1. Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10
11 2. Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in the
13 documentation and/or other materials provided with the distribution.
14
15 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 POSSIBILITY OF SUCH DAMAGE.
26*/
27
28#include <pwd.h>
29#include <time.h>
30#include <errno.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <getopt.h>
35#include <limits.h>
36
37#include "nls.h"
38#include "c.h"
39#include "strutils.h"
40#include "lastlog2.h"
41
42static char *lastlog2_path = LL2_DEFAULT_DATABASE;
43
c71f3828
KZ
44static int bflg;
45static time_t b_days;
46static int tflg;
47static time_t t_days;
48static int sflg;
49
50static int print_entry(const char *user, int64_t ll_time,
51 const char *tty, const char *rhost,
52 const char *pam_service, const char *error)
c2e299d0
SS
53{
54 static int once = 0;
55 char *datep;
56 struct tm *tm, tm_buf;
57 char datetime[80];
58 /* IPv6 address is at maximum 39 characters.
59 But for LL-addresses (fe80+only) the interface should be set,
60 so LL-address + % + IFNAMSIZ. */
61 const int maxIPv6Addrlen = 42;
62
63 /* Print only if older than b days */
64 if (bflg && ((time (NULL) - ll_time) < b_days))
65 return 0;
66
67 /* Print only if newer than t days */
68 if (tflg && ((time (NULL) - ll_time) > t_days))
69 return 0;
70 /* this is necessary if you compile this on architectures with
71 a 32bit time_t type. */
72 time_t t_time = ll_time;
d2203ae3 73 tm = localtime_r(&t_time, &tm_buf);
c2e299d0
SS
74 if (tm == NULL)
75 datep = "(unknown)";
76 else {
c71f3828 77 strftime(datetime, sizeof(datetime), "%a %b %e %H:%M:%S %z %Y", tm);
c2e299d0
SS
78 datep = datetime;
79 }
80
81 if (ll_time == 0)
82 datep = "**Never logged in**";
83
84 if (!once) {
85 printf("Username Port From%*s Latest%*s%s\n",
d2203ae3
KZ
86 maxIPv6Addrlen - 4, " ",
87 sflg ? (int) strlen(datep) -5 : 0,
88 " ", sflg ? "Service" : "");
c2e299d0
SS
89 once = 1;
90 }
91 printf("%-16s %-8.8s %*s %s%*s%s\n", user, tty ? tty : "",
92 -maxIPv6Addrlen, rhost ? rhost : "", datep,
d2203ae3
KZ
93 sflg ? 31 - (int) strlen(datep) : 0,
94 (sflg && pam_service) ? " " : "",
95 sflg ? (pam_service ? pam_service : "") : "");
c2e299d0
SS
96
97 if (error)
98 printf("\nError: %s\n", error);
99
100 return 0;
101}
102
c71f3828 103static void __attribute__((__noreturn__)) usage(void)
c2e299d0
SS
104{
105 FILE *output = stdout;
106
107 fputs(USAGE_HEADER, output);
108 fprintf(output, _(" %s [options]\n"), program_invocation_short_name);
109
110 fputs(USAGE_OPTIONS, output);
111 fputs(_(" -b, --before DAYS Print only records older than DAYS\n"), output);
112 fputs(_(" -C, --clear Clear record of a user (requires -u)\n"), output);
113 fputs(_(" -d, --database FILE Use FILE as lastlog2 database\n"), output);
114 fputs(_(" -i, --import FILE Import data from old lastlog file\n"), output);
115 fputs(_(" -r, --rename NEWNAME Rename existing user to NEWNAME (requires -u)\n"), output);
116 fputs(_(" -s, --service Display PAM service\n"), output);
117 fputs(_(" -S, --set ySet lastlog record to current time (requires -u)\n"), output);
118 fputs(_(" -t, --time DAYS Print only lastlog records more recent than DAYS\n"), output);
119 fputs(_(" -u, --user LOGIN Print lastlog record of the specified LOGIN\n"), output);
120
121 fputs(USAGE_SEPARATOR, output);
122 fprintf(output, USAGE_HELP_OPTIONS(25));
123 fprintf(output, USAGE_MAN_TAIL("lastlog2(8)"));
124
125 exit(EXIT_SUCCESS);
126}
127
af93603e
KZ
128/* Check if an user exists on the system */
129#define has_user(_x) (getpwnam(_x) != NULL)
c2e299d0 130
c71f3828 131int main(int argc, char **argv)
c2e299d0 132{
76fcb1c6 133 static const struct option longopts[] = {
c2e299d0
SS
134 {"before", required_argument, NULL, 'b'},
135 {"clear", no_argument, NULL, 'C'},
136 {"database", required_argument, NULL, 'd'},
137 {"help", no_argument, NULL, 'h'},
138 {"import", required_argument, NULL, 'i'},
139 {"rename", required_argument, NULL, 'r'},
140 {"service", no_argument, NULL, 's'},
141 {"set", no_argument, NULL, 'S'},
142 {"time", required_argument, NULL, 't'},
143 {"user", required_argument, NULL, 'u'},
144 {"version", no_argument, NULL, 'v'},
145 {NULL, 0, NULL, '\0'}
146 };
147 char *error = NULL;
148 int Cflg = 0;
149 int iflg = 0;
150 int rflg = 0;
151 int Sflg = 0;
152 int uflg = 0;
153 const char *user = NULL;
154 const char *newname = NULL;
155 const char *lastlog_file = NULL;
156 struct ll2_context *db_context = NULL;
157
158 int c;
159
160 while ((c = getopt_long(argc, argv, "b:Cd:hi:r:sSt:u:v", longopts, NULL)) != -1) {
161 switch (c) {
162 case 'b': /* before DAYS; Print only records older than DAYS */
163 {
164 unsigned long days;
165 errno = 0;
166 days = strtoul_or_err(optarg, _("Cannot parse days"));
d2203ae3 167 b_days = (time_t) days * (24L * 3600L) /* seconds/DAY */;
c2e299d0
SS
168 bflg = 1;
169 }
170 break;
171 case 'C': /* clear; Clear record of a user (requires -u) */
172 Cflg = 1;
173 break;
174 case 'd': /* database <FILE>; Use FILE as lastlog2 database */
175 lastlog2_path = optarg;
176 break;
177 case 'h': /* help; Display this help message and exit */
178 usage();
179 break;
180 case 'i': /* import <FILE>; Import data from old lastlog file */
181 lastlog_file = optarg;
182 iflg = 1;
183 break;
184 case 'r': /* rename <NEWNAME>; Rename existing user to NEWNAME (requires -u) */
185 rflg = 1;
186 newname = optarg;
187 break;
188 case 's': /* service; Display PAM service */
189 sflg = 1;
190 break;
191 case 'S': /* set; Set lastlog record to current time (requires -u) */
192 /* Set lastlog record of a user to the current time. */
193 Sflg = 1;
194 break;
195 case 't': /* time <DAYS>; Print only lastlog records more recent than DAYS */
196 {
197 unsigned long days;
198 errno = 0;
199 days = strtoul_or_err(optarg, _("Cannot parse days"));
d2203ae3 200 t_days = (time_t) days * (24L * 3600L) /* seconds/DAY */;
c2e299d0
SS
201 tflg = 1;
202 }
203 break;
204 case 'u': /* user <LOGIN>; Print lastlog record of the specified LOGIN */
205 uflg = 1;
206 user = optarg;
207 break;
208 case 'v': /* version; Print version number and exit */
209 print_version(EXIT_SUCCESS);
210 break;
211 default:
212 errtryhelp(EXIT_FAILURE);
213 }
214 }
215
4e86ac6f 216 if ((Cflg + Sflg + iflg) > 1)
d2203ae3 217 errx(EXIT_FAILURE, _("Option -C, -i and -S cannot be used together"));
c2e299d0
SS
218
219 db_context = ll2_new_context(lastlog2_path);
220 if (!db_context)
4e86ac6f 221 errx(EXIT_FAILURE, _("Couldn't initialize lastlog2 environment"));
c2e299d0
SS
222
223 if (iflg) {
224 /* Importing entries */
225 if (ll2_import_lastlog(db_context, lastlog_file, &error) != 0) {
4e86ac6f
KZ
226 warnx(_("Couldn't import entries from '%s'"), lastlog_file);
227 goto err;
c2e299d0 228 }
4e86ac6f 229 goto done;
c2e299d0
SS
230 }
231
232 if (Cflg || Sflg || rflg) {
233 /* udpating, inserting and removing entries */
234 if (!uflg || strlen(user) == 0) {
4e86ac6f
KZ
235 warnx(_("Options -C, -r and -S require option -u to specify the user"));
236 goto err;
c2e299d0
SS
237 }
238
af93603e 239 if ((Cflg || Sflg) && !has_user(user)) {
4e86ac6f
KZ
240 warnx(_("User '%s' does not exist."), user);
241 goto err;
c2e299d0
SS
242 }
243
244 if (Cflg) {
245 if (ll2_remove_entry(db_context, user, &error) != 0) {
4e86ac6f
KZ
246 warnx(_("Couldn't remove entry for '%s'"), user);
247 goto err;
c2e299d0
SS
248 }
249 }
250
251 if (Sflg) {
252 time_t ll_time = 0;
253
254 if (time(&ll_time) == -1) {
4e86ac6f
KZ
255 warn(_("Could not determine current time"));
256 goto err;
c2e299d0
SS
257 }
258
259 if (ll2_update_login_time(db_context, user, ll_time, &error) != 0) {
4e86ac6f
KZ
260 warnx(_("Couldn't update login time for '%s'"), user);
261 goto err;
c2e299d0 262 }
c2e299d0
SS
263 }
264
265 if (rflg) {
266 if (ll2_rename_user(db_context, user, newname, &error) != 0) {
4e86ac6f
KZ
267 warnx(_("Couldn't rename entry '%s' to '%s'"), user, newname);
268 goto err;
c2e299d0
SS
269 }
270 }
271
4e86ac6f 272 goto done;
c2e299d0
SS
273 }
274
275 if (user) {
276 /* print user specific information */
277 int64_t ll_time = 0;
278 char *tty = NULL;
279 char *rhost = NULL;
280 char *service = NULL;
281
af93603e 282 if (!has_user(user)) {
4e86ac6f
KZ
283 warnx(_("User '%s' does not exist."), user);
284 goto err;
c2e299d0
SS
285 }
286
287 /* We ignore errors, if the user is not in the database he did never login */
288 ll2_read_entry(db_context, user, &ll_time, &tty, &rhost,
289 &service, NULL);
290
291 print_entry(user, ll_time, tty, rhost, service, NULL);
4e86ac6f 292 goto done;
c2e299d0
SS
293 }
294
295 /* print all information */
296 if (ll2_read_all(db_context, print_entry, &error) != 0) {
4e86ac6f
KZ
297 warnx(_("Couldn't read entries for all users"));
298 goto err;
c2e299d0
SS
299 }
300
4e86ac6f 301done:
c2e299d0
SS
302 ll2_unref_context(db_context);
303 exit(EXIT_SUCCESS);
4e86ac6f
KZ
304err:
305 ll2_unref_context(db_context);
306 if (error)
307 errx(EXIT_FAILURE, "%s", error);
308 exit(EXIT_FAILURE);
c2e299d0 309}