/* * topuser.c * kreator, 1998. * * 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. */ #undef PRECISE_TIMING #define ONLY_CURRENT_MONTH #define NEW_UTMP_CODE #undef OLD_UTMP #include #include #include #include #include #include #include #include #include #ifndef OLD_UTMP_H #define OLD_UTMP_H #define OLD_LINESIZE 12 #define OLD_NAMESIZE 8 #define OLD_HOSTSIZE 16 struct oldutmp { short ut_type; int ut_pid; char ut_line[OLD_LINESIZE]; char ut_id[4]; long ut_oldtime; char ut_user[OLD_NAMESIZE]; char ut_host[OLD_HOSTSIZE]; long ut_oldaddr; }; #endif #ifndef SHUTDOWN_TIME # define SHUTDOWN_TIME 254 #endif #define UCHUNKSIZE 16384 #define R_CRASH 1 #define R_DOWN 2 #define R_NORMAL 3 #define R_NOW 4 #define R_REBOOT 5 #define ut_name ut_user #ifndef _NO_UT_TIME #define ut_time ut_tv.tv_sec #endif #define ut_xtime ut_tv.tv_sec #define ut_addr ut_addr_v6[0] #define IGN_USERS "www-data backup dos operator games man lp mail news uucp proxy majordom postgres www-data backup dos operator list irc gnats alias qmaild qmails qmailr qmailq qmaill qmailp nobody daemon bin sys sync" #ifdef PRECISE_TIMING #define REQ_TYPE "pty tty" #endif struct utmplist { struct utmp ut; struct utmplist *next; struct utmplist *prev; }; struct utmplist *utmplist=NULL; int user_counter; #ifdef ONLY_CURRENT_MONTH int tmp_month; #endif struct users { char pw_name[UT_NAMESIZE], pw_gecos[42+1]; int days, hours, mins, logins; } *users; time_t lastdate, begintime, tmp_time; typedef int (*sort_func) (const void *, const void *); void fatal(const char *msg) { puts(msg); exit(EXIT_FAILURE); } void getallusers(void) { int i; struct passwd *temp; char *pos; setpwent(); for (user_counter=0; getpwent()!=NULL; ++user_counter); endpwent(); if (user_counter) { users=(struct users *) calloc(user_counter, sizeof(struct users)); if (users==NULL) fatal("out of memory"); setpwent(); for (i=0; ipw_name, temp->pw_name, UT_NAMESIZE); if ((pos=strstr(temp->pw_gecos, ",")) && *pos) *pos=0; memcpy((&users[i])->pw_gecos, temp->pw_gecos, 42); } endpwent(); } else fatal("no users in passwd file"); } void uconv(struct oldutmp *oldut, struct utmp *utn) { memset(utn, 0, sizeof(struct utmp)); utn->ut_type=oldut->ut_type; utn->ut_pid=oldut->ut_pid; utn->ut_time=oldut->ut_oldtime; utn->ut_addr=oldut->ut_oldaddr; strncpy(utn->ut_line, oldut->ut_line, OLD_LINESIZE); strncpy(utn->ut_user, oldut->ut_user, OLD_NAMESIZE); strncpy(utn->ut_host, oldut->ut_host, OLD_HOSTSIZE); } #ifdef NEW_UTMP_CODE int uread(FILE *fp, struct utmp *u, int *quit) { static int utsize; static char buf[UCHUNKSIZE]; char tmp[1024]; static off_t fpos; static int bpos; #ifdef OLD_UTMP struct oldutmp uto; #endif int r; off_t o; if (quit==NULL && u!=NULL) { #ifdef OLD_UTMP r=fread(&uto, sizeof(uto), 1, fp); uconv(&uto, u); #else r=fread(u, sizeof(struct utmp), 1, fp); #endif return r; } if (u==NULL) { #ifdef OLD_UTMP utsize=sizeof(uto); #else utsize=sizeof(struct utmp); #endif fseek(fp, 0L, SEEK_END); fpos=ftell(fp); if (fpos==0) return 0; o=((fpos-1)/UCHUNKSIZE)*UCHUNKSIZE; if (fseek(fp, o, SEEK_SET)<0) { fprintf(stderr, "seek failed!\n"); return 0; } bpos=fpos-o; if (fread(buf, bpos, 1, fp)!=1) { fprintf(stderr, "read failed!\n"); return 0; } fpos=o; return 1; } bpos-=utsize; if (bpos>=0) { #ifdef OLD_UTMP uconv((struct oldutmp *)(buf+bpos), u); #else memcpy(u, buf+bpos, sizeof(struct utmp)); #endif return 1; } fpos-=UCHUNKSIZE; if (fpos<0) return 0; memcpy(tmp+(-bpos), buf, utsize+bpos); if (fseek(fp, fpos, SEEK_SET)<0) { perror("fseek"); return 0; } if (fread(buf, UCHUNKSIZE, 1, fp)!=1) { perror("fread"); return 0; } memcpy(tmp, buf+UCHUNKSIZE+bpos, -bpos); bpos+=UCHUNKSIZE; #ifdef OLD_UTMP uconv((struct oldutmp *)tmp, u); #else memcpy(u, tmp, sizeof(struct utmp)); #endif return 1; } #else int uread(FILE *fp, struct utmp *u, int *quit) { struct oldutmp uto; int r; if (u==NULL) { #ifdef OLD_UTMP r=sizeof(struct oldutmp); #else r=sizeof(struct utmp); #endif fseek(fp, -1L*r, SEEK_END); return 1; } #ifndef OLD_UTMP r = fread(u, sizeof(struct utmp), 1, fp); if (r==1) { if (fseek(fp, -2L*sizeof(struct utmp), SEEK_CUR)<0) if (quit) *quit=1; } return r; #else r=fread(&uto, sizeof(struct oldutmp), 1, fp); if (r==1) { if (fseek(fp, -2L*sizeof(struct oldutmp), SEEK_CUR)<0) if (quit) *quit=1; uconv(&uto, u); } return r; #endif } #endif #ifndef ONLY_CURRENT_MONTH int find_user(const char *name, int days, int hours, int mins, char *utline) #else int find_user(const char *name, int days, int hours, int mins, char *utline, time_t *logintime, time_t *logouttime) #endif { int i=0,tmp,cf=1; struct users *tmp_user; for(; ipw_name, name)==0)) { #ifdef ONLY_CURRENT_MONTH if ((localtime(logintime)->tm_mon!=tmp_month) && (localtime(logouttime)->tm_mon!=tmp_month)) break; #endif #ifdef PRECISE_TIMING utline[3]=0; if (strstr(REQ_TYPE, utline)==NULL) break; #endif tmp_user=&users[i]; cf=0; tmp=(tmp_user->mins+=mins)/60; tmp_user->mins%=60; tmp=(tmp_user->hours+=hours+tmp)/24; tmp_user->hours%=24; tmp_user->days+=days+tmp; tmp_user->logins++; break; } return cf; } int list(struct utmp *p, time_t t, int what) { time_t secs; int mins, hours, days, found; char utline[12]; #ifdef ONLY_CURRENT_MONTH time_t tmp, logintime, logouttime; tmp=(time_t)p->ut_time; logintime=tmp; logouttime=t; #endif strncpy(utline, p->ut_line, sizeof(utline)); if (strncmp(utline, "ftp", 3)==0) utline[3] = 0; if (strncmp(utline, "uucp", 4)==0) utline[4] = 0; secs=t-p->ut_time; mins=(secs/60)%60; hours=(secs/3600)%24; days=secs/86400; #ifndef ONLY_CURRENT_MONTH found=find_user(p->ut_name, days, hours, mins, utline); #else found=find_user(p->ut_name, days, hours, mins, utline, &logintime, &logouttime); #endif return 0; } void scan_wtmp(void) { struct utmp ut; struct utmp oldut; struct utmplist *p; struct utmplist *next; FILE *fp; time_t lastboot=0, lastrch=0, lastdown; int whydown=0, c, x, quit=0, down=0, lastb=0; struct stat st; time(&lastdown); lastrch=lastdown; lastdate=lastdown; if ((fp=fopen(WTMP_FILE,"r"))==NULL) fatal("cannot open wtmp file"); setvbuf(fp, NULL, _IOFBF, UCHUNKSIZE); if (uread(fp, &ut, NULL)==1) begintime=ut.ut_time; else { fstat(fileno(fp), &st); begintime=st.st_ctime; quit=1; } uread(fp, NULL, NULL); while(!quit) { if (uread(fp, &ut, &quit)!=1) break; if (memcmp(&ut, &oldut, sizeof(struct utmp))==0) continue; memcpy(&oldut, &ut, sizeof(struct utmp)); lastdate=ut.ut_time; if (lastb) { quit=list(&ut, ut.ut_time, R_NORMAL); continue; } if (!strncmp(ut.ut_line, "~", 1)) { if (strncmp(ut.ut_user, "shutdown", 8)==0) ut.ut_type=SHUTDOWN_TIME; else if (strncmp(ut.ut_user, "reboot", 6)==0) ut.ut_type=BOOT_TIME; else if (strncmp(ut.ut_user, "runlevel", 7)==0) ut.ut_type=RUN_LVL; } else { if (ut.ut_type!=DEAD_PROCESS && ut.ut_name[0] && ut.ut_line[0] && strcmp(ut.ut_name, "LOGIN")!=0) ut.ut_type=USER_PROCESS; if (ut.ut_name[0]==0) ut.ut_type=DEAD_PROCESS; } switch (ut.ut_type) { case SHUTDOWN_TIME: lastdown=lastrch=ut.ut_time; down=1; break; case BOOT_TIME: strcpy(ut.ut_line, "system boot"); quit=list(&ut, lastdown, R_REBOOT); down=1; break; case RUN_LVL: x=ut.ut_pid & 255; if (x=='0' || x=='6') { lastdown=ut.ut_time; down=1; } lastrch=ut.ut_time; break; case USER_PROCESS: c=0; for (p=utmplist; p; p=next) { next=p->next; if (strncmp(p->ut.ut_line, ut.ut_line, 12)==0) { if (c==0) { quit=list(&ut, p->ut.ut_time, R_NORMAL); c=1; } if (p->next) p->next->prev=p->prev; if (p->prev) p->prev->next=p->next; else utmplist=p->next; free(p); } } if (c==0) { if (lastboot==0) quit=list(&ut, time(NULL), R_NOW); else quit=list(&ut, lastboot, whydown); } case DEAD_PROCESS: if (ut.ut_line[0] == 0) break; if ((p=malloc(sizeof(struct utmplist)))==NULL) fatal("out of memory"); memcpy(&p->ut, &ut, sizeof(struct utmp)); p->next=utmplist; p->prev=NULL; if (utmplist) utmplist->prev=p; utmplist=p; break; } if (down) { lastboot=ut.ut_time; whydown=(ut.ut_type==SHUTDOWN_TIME)?R_DOWN:R_CRASH; for (p=utmplist; p; p=next) { next=p->next; free(p); } utmplist=NULL; down=0; } } fclose(fp); } int users_compare(struct users *a, struct users *b) { struct users *tmp1=a; struct users *tmp2=b; int temp; if ((temp=tmp2->days-tmp1->days)) return temp; if ((temp=tmp2->hours-tmp1->hours)) return temp; if ((temp=tmp2->mins-tmp1->mins)) return temp; return 0; } void output(void) { int i=0; struct users *tmp; qsort(users, user_counter, sizeof(struct users), (sort_func)users_compare); #ifndef ONLY_CURRENT_MONTH printf("Statistics since %s", ctime(&begintime)); #else printf("Statistics for current month\n"); #endif printf("Scanned on %s\n%5s %-10s %-42s %9s - %-6s\n", ctime(&tmp_time), "No.", "Username", "Realname", "ddd:hh:mm", "logins"); for(; i<79; printf("-"), ++i); puts(""); for(i=0; ipw_name)==NULL) printf("%4d. %-10s %-42s %03d:%02d:%02d - %6d\n", i+1, tmp->pw_name, tmp->pw_gecos, tmp->days, tmp->hours, tmp->mins, tmp->logins); } printf("\nProgramming by kreator, 1999\nCompile date %s\n", __DATE__); } int main(void) { tmp_time=time(NULL); #ifdef ONLY_CURRENT_MONTH tmp_month=localtime(&tmp_time)->tm_mon; #endif getallusers(); scan_wtmp(); output(); free(users); return(EXIT_SUCCESS); }