/* uptimed - Copyright (c) 1998-2004 Rob Kaper - read_uptime code for BSD and Solaris platforms taken from upclient package by Martijn Broenland. 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; version 2 dated June, 1991. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave., Cambridge, MA 02139, USA. */ #include "../config.h" #include "urec.h" Urec *urec_list = NULL; static Urec *urec_last = NULL; Urec *add_urec(time_t utime, time_t btime, char *sys) { Urec *u, *tmpu, *uprev = NULL; /* Allocate memory for the new entry. */ if ((u=malloc(sizeof(Urec))) == NULL) { printf("error mallocing urec struct. this is serious shit! exiting.\n"); exit(1); } /* Copy boottime, uptime and systeminfo into memory. */ u->utime = utime; u->btime = btime; strncpy(u->sys, sys, SYSMAX); u->sys[SYSMAX]='\0'; /* Add the entry to the linked list. */ for(tmpu = urec_list; tmpu; tmpu = tmpu->next) { if (u->utime > tmpu->utime) { u->next=tmpu; if (tmpu == urec_list) return urec_list = u; return uprev->next = u; } else { uprev = tmpu; } } u->next = NULL; if (urec_last) { urec_last->next = u; } else { urec_list = u; } return urec_last = u; } void del_urec(Urec *u) { Urec *tmpu = urec_list; if (u == urec_list) { urec_list=u->next; if (!urec_list) urec_last = NULL; } else { for (tmpu = urec_list; tmpu->next && u != tmpu->next; tmpu = tmpu->next); if (!u->next) urec_last = tmpu; tmpu->next = u->next; } free(u); } void moveup(void) { /* Delete current session from the list. */ del_urec(u_current); /* Re-add it. (it should be urec_list now) */ u_current=add_urec(read_uptime(), readbootid(), read_sysinfo()); } char *read_sysinfo(void) { struct utsname temp_uname; static char sys[SYSMAX+1]; /* What kernel are we running at the moment? */ if (!uname(&temp_uname)) { /* Name and number.. */ snprintf(sys, SYSMAX, "%s %s", temp_uname.sysname, temp_uname.release); sys[SYSMAX]='\0'; return sys; } else { #ifdef PLATFORM_LINUX return "Linux"; #endif #ifdef PLATFORM_BSD return "BSD"; #endif #ifdef PLATFORM_SOLARIS return "Solaris"; #endif #ifdef PLATFORM_HPUX return "HP/UX"; #endif #ifdef PLATFORM_UNKNOWN return "unknown"; #endif } } #ifdef PLATFORM_LINUX time_t read_uptime(void) { struct sysinfo si; /* Until jiffies is declared to something different than unsigned long * in the kernel sources, this value will probably on all 32b platforms * wrap past approx. 497 days. This also applies to reading this value * through /proc/uptime. */ if (sysinfo(&si) != 0) { printf ("uptimed: error getting uptime!\n"); exit(-1); } return((time_t)si.uptime); } #endif #ifdef PLATFORM_BSD time_t read_uptime(void) { time_t now, up; struct timeval boottime; int mib[2]; size_t size; (void)time(&now); mib[0] = CTL_KERN; mib[1] = KERN_BOOTTIME; size = sizeof (boottime); if (sysctl (mib, 2, &boottime, &size, NULL, 0) != -1 && boottime.tv_sec!= 0) { up = now - boottime.tv_sec; } return up; } #endif #ifdef PLATFORM_SOLARIS time_t read_uptime(void) { int fd; struct utmp ut; int found=0; fd = open (UTMP_FILE, O_RDONLY); if (fd >= 0) { while (!found) { if (read(fd, &ut, sizeof(ut)) < 0) { found = -1; } else if (ut.ut_type==BOOT_TIME) { found = 1; } } close(fd); } if (found == 1) return time(0) - ut.ut_time; return 0; } #endif #ifdef PLATFORM_HPUX time_t read_uptime(void) { struct pst_static _pst_static; pstat_getstatic( &_pst_static, sizeof(_pst_static), (size_t)1, 0); return (time_t)(time(0) - _pst_static.boot_time); } #endif #ifdef PLATFORM_UNKNOWN time_t read_uptime(void) { /* * This is a quick and inaccurate hack calculating the uptime from the * current time and the timestamp made at boottime. * * This is inaccurate because: * 1) the boottime timestamp can be extremely delayed due to fscking etc. * 2) when the system time changes (for example timezone changes) the * boottime timestamp does not, creating even more inaccuracy. */ if (u_current) return time(0) - u_current->btime; return time(0) - readbootid(); } #endif void read_records(time_t current) { FILE *f; char str[256]; time_t utime, btime; long l_utime, l_btime; char buf[256], sys[SYSMAX+1]; f=fopen(FILE_RECORDS, "r"); if (!f) return; fgets(str, sizeof(str), f); while (!feof(f)) { /* Check for validity of input string. */ if (sscanf(str, "%ld:%ld:%[^]\n]", &l_utime, &l_btime, buf) != 3) { /* Skip this entry. Do we want feedback here? */ } else { utime = (time_t)l_utime; btime = (time_t)l_btime; strncpy(sys, buf, SYSMAX); sys[SYSMAX]='\0'; if (utime > 0 && btime != current) add_urec(utime, btime, sys); } fgets(str, sizeof(str), f); } fclose(f); } void save_records(int max, time_t log_threshold) { FILE *f; Urec *u; int i = 0; f = fopen(FILE_RECORDS".tmp", "w"); if (!f) { printf("uptimed: cannot write to %s\n", FILE_RECORDS); return; } for(u=urec_list; u; u = u->next) { /* Ignore everything below the threshold */ if (u->utime >= log_threshold) { fprintf(f, "%lu:%lu:%s\n", (unsigned long)u->utime, (unsigned long)u->btime, u->sys); /* Stop processing when we've logged the max number specified. */ if ((max > 0) && (++i >= max)) break; } } fclose(f); rename(FILE_RECORDS".tmp", FILE_RECORDS); } #ifdef PLATFORM_LINUX int createbootid(void) { FILE *f; char str[256]; time_t bootid = 0; f=fopen("/proc/stat", "r"); if (!f) { printf ("Error opening /proc file. Can not determine bootid, exiting!\n"); exit(-1); } else { fgets(str, sizeof(str), f); while (!feof(f)) { if (strstr(str, "btime")) { bootid=atoi(str+6); break; } fgets(str, sizeof(str), f); } fclose(f); } f = fopen(FILE_BOOTID, "w"); if (!f) { printf("Error writing bootid file, exiting!\n"); exit(-1); } else { fprintf(f, "%ld\n", bootid); fclose(f); } return 0; } #endif #ifdef PLATFORM_SOLARIS int createbootid(void) { FILE *f; int fd; struct utmp ut; int found = 0; time_t bootid = 0; fd = open (UTMP_FILE, O_RDONLY); if (fd >= 0) { while(!found) { if (read(fd, &ut, sizeof(ut)) < 0) { found = -1; } else if (ut.ut_type==BOOT_TIME) { found = 1; } } close(fd); } if (found == 1) bootid = ut.ut_time; f = fopen(FILE_BOOTID, "w"); if (!f) { printf("Error writing bootid file, exiting!\n"); exit(-1); } else { fprintf(f, "%ld\n", bootid); fclose(f); } return 0; } #endif #ifdef PLATFORM_HPUX int createbootid(void) { FILE *f; struct pst_static _pst_static; pstat_getstatic( &_pst_static, sizeof(_pst_static), (size_t)1, 0); f=fopen(FILE_BOOTID, "w"); if (!f) { printf("Error writing bootid file, exiting!\n"); exit(-1); } else { fprintf(f, "%ld\n", _pst_static.boot_time); fclose(f); } return 0; } #endif #ifdef PLATFORM_UNKNOWN int createbootid(void) { FILE *f; time_t bootid=0; bootid=time(0); f = fopen(FILE_BOOTID, "w"); if (!f) { printf("Error writing bootid file, exiting!\n"); exit(-1); } else { fprintf(f, "%ld\n", bootid); fclose(f); } return 0; } #endif time_t readbootid(void) { #ifdef PLATFORM_BSD time_t bootid = 0; struct timeval boottime; int mib[2]; size_t size; mib[0] = CTL_KERN; mib[1] = KERN_BOOTTIME; size = sizeof (boottime); if (sysctl (mib, 2, &boottime, &size, NULL, 0) != -1 && boottime.tv_sec != 0) { bootid = boottime.tv_sec; } return bootid; #else FILE *f; char str[256]; f=fopen(FILE_BOOTID, "r"); if (!f) { printf("Error reading boot id from file, exiting!\n\nYou probably forgot to create a bootid with with the -b option.\nYou really want the system to do this on bootup, read the INSTALL file!\n"); exit(-1); } fgets(str, sizeof(str), f); fclose(f); return atoi(str); #endif } int compare_urecs(Urec *a, Urec *b, int sort_by) { if (sort_by == 1) { return a->btime - b->btime; } else if (sort_by == -1) { return b->btime - a->btime; } else if (sort_by == 2) { return strcmp(a->sys, b->sys); } else if (sort_by == -2) { return strcmp(b->sys, a->sys); } return 0; } Urec* sort_urec(Urec* list, int sort_by) { /* * sort_by: * 0: Nothing * 1: Boottime * -1: Reverse Boottime * 2: Sysinfo * -2: Reverse Sysinfo */ Urec *p, *q, *e, *tail; int insize, nmerges, psize, qsize, i; insize = 1; for (;;) { p = list; list = NULL; tail = NULL; nmerges = 0; /* count number of merges we do in this pass */ while (p) { nmerges++; /* there exists a merge to be done */ /* step `insize' places along from p */ q = p; psize = 0; for (i = 0; i < insize; i++) { psize++; q = q->next; if (!q) break; } /* if q hasn't fallen off end, we have two lists to merge */ qsize = insize; /* now we have two lists; merge them */ while (psize > 0 || (qsize > 0 && q)) { /* decide whether next element of merge comes from p or q */ if (psize == 0) { /* p is empty; e must come from q. */ e = q; q = q->next; qsize--; } else if (qsize == 0 || !q) { /* q is empty; e must come from p. */ e = p; p = p->next; psize--; } else if (compare_urecs(p,q,sort_by) <= 0) { /* First element of p is lower (or same); * e must come from p. */ e = p; p = p->next; psize--; } else { /* First element of q is lower; e must come from q. */ e = q; q = q->next; qsize--; } /* add the next element to the merged list */ if (tail) { tail->next = e; } else { list = e; } tail = e; } /* now p has stepped `insize' places along, and q has too */ p = q; } tail->next = NULL; /* If we have done only one merge, we're finished. */ if (nmerges <= 1) return list; /* allow for nmerges==0, the empty list case */ /* Otherwise repeat, merging lists twice the size */ insize *= 2; } }