last: fix utmplist usage

last(1) uses a global list of entries, this is unnecessary and it's
also mistake because the pointer to the list is not set to NULL when
last(1) opens another utmp file. For example:

 last -f /var/log/wtmp -f /var/log/wtmp-20150220

ends with unexpected free() call or sometimes with never ending loop.

Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1201033
Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
Karel Zak 2015-03-13 11:13:26 +01:00
parent 2a4b073e8b
commit da1a8ed0b0
1 changed files with 21 additions and 16 deletions

View File

@ -107,7 +107,6 @@ struct utmplist {
struct utmplist *next;
struct utmplist *prev;
};
struct utmplist *utmplist = NULL;
/* Types of listing */
enum {
@ -622,8 +621,10 @@ static int is_phantom(const struct last_control *ctl, struct utmp *ut)
static void process_wtmp_file(const struct last_control *ctl)
{
FILE *fp; /* Filepointer of wtmp file */
char *filename;
struct utmp ut; /* Current utmp entry */
struct utmplist *ulist = NULL; /* All entries */
struct utmplist *p; /* Pointer into utmplist */
struct utmplist *next; /* Pointer into utmplist */
@ -640,6 +641,7 @@ static void process_wtmp_file(const struct last_control *ctl)
time(&lastdown);
lastrch = lastdown;
filename = ctl->altv[ctl->alti];
/*
* Fill in 'lastdate'
@ -655,8 +657,8 @@ static void process_wtmp_file(const struct last_control *ctl)
/*
* Open the utmp file
*/
if ((fp = fopen(ctl->altv[ctl->alti], "r")) == NULL)
err(EXIT_FAILURE, _("cannot open %s"), ctl->altv[ctl->alti]);
if ((fp = fopen(filename, "r")) == NULL)
err(EXIT_FAILURE, _("cannot open %s"), filename);
/*
* Optimize the buffer size.
@ -670,7 +672,7 @@ static void process_wtmp_file(const struct last_control *ctl)
begintime = ut.UL_UT_TIME;
else {
if (fstat(fileno(fp), &st) != 0)
err(EXIT_FAILURE, _("stat of %s failed"), ctl->altv[ctl->alti]);
err(EXIT_FAILURE, _("stat of %s failed"), filename);
begintime = st.st_ctime;
quit = 1;
}
@ -787,7 +789,7 @@ static void process_wtmp_file(const struct last_control *ctl)
* the same ut_line.
*/
c = 0;
for (p = utmplist; p; p = next) {
for (p = ulist; p; p = next) {
next = p->next;
if (strncmp(p->ut.ut_line, ut.ut_line,
UT_LINESIZE) == 0) {
@ -796,11 +798,12 @@ static void process_wtmp_file(const struct last_control *ctl)
quit = list(ctl, &ut, p->ut.UL_UT_TIME, R_NORMAL);
c = 1;
}
if (p->next) p->next->prev = p->prev;
if (p->next)
p->next->prev = p->prev;
if (p->prev)
p->prev->next = p->next;
else
utmplist = p->next;
ulist = p->next;
free(p);
}
}
@ -829,10 +832,11 @@ static void process_wtmp_file(const struct last_control *ctl)
break;
p = xmalloc(sizeof(struct utmplist));
memcpy(&p->ut, &ut, sizeof(struct utmp));
p->next = utmplist;
p->next = ulist;
p->prev = NULL;
if (utmplist) utmplist->prev = p;
utmplist = p;
if (ulist)
ulist->prev = p;
ulist = p;
break;
case EMPTY:
@ -848,23 +852,24 @@ static void process_wtmp_file(const struct last_control *ctl)
/*
* If we saw a shutdown/reboot record we can remove
* the entire current utmplist.
* the entire current ulist.
*/
if (down) {
lastboot = ut.UL_UT_TIME;
whydown = (ut.ut_type == SHUTDOWN_TIME) ? R_DOWN : R_CRASH;
for (p = utmplist; p; p = next) {
for (p = ulist; p; p = next) {
next = p->next;
free(p);
}
utmplist = NULL;
ulist = NULL;
down = 0;
}
}
printf(_("\n%s begins %s"), basename(ctl->altv[ctl->alti]), ctime(&begintime));
printf(_("\n%s begins %s"), basename(filename), ctime(&begintime));
fclose(fp);
for (p = utmplist; p; p = next) {
for (p = ulist; p; p = next) {
next = p->next;
free(p);
}
@ -1001,7 +1006,7 @@ int main(int argc, char **argv)
ctl.altc++;
}
for (; ctl.alti < ctl.altc; ctl.alti++) {
for (ctl.alti = 0; ctl.alti < ctl.altc; ctl.alti++) {
get_boot_time(&ctl.boot_time);
process_wtmp_file(&ctl);
free(ctl.altv[ctl.alti]);