/* $Id: uptimec.c,v 1.57.2.13 2004/08/18 19:53:33 ola Exp $ */ /*- * Copyright (c) 2003-2004, Ola Eriksson * All rights reserved. * * 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. * 3. Neither the name of the mrEriksson-Network nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * 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 OWNER 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 "config.h" #include #include #include #include #include #include #include #include #include #include #ifndef sbsize_t #define sbsize_t int32_t #endif #ifndef bsize_t #define bsize_t int64_t #endif #include #include /* * Figure out how to extract uptime */ #undef GETUPTIME_METHOD_FOUND /* NetBSD, FreeBSD, OpenBSD and MacOS X */ #ifndef GETUPTIME_METHOD_FOUND # if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \ defined(__APPLE__) || defined(__DARWIN__) # define GETUPTIME_BSDLIKE # define GETUPTIME_METHOD_FOUND # endif #endif /* Used by most linux-systems */ #ifndef GETUPTIME_METHOD_FOUND # ifdef HAVE__PROC_UPTIME # define GETUPTIME_PROCUPTIME # define GETUPTIME_METHOD_FOUND # endif #endif /* Solaris, Tru64 and HP/UX */ #ifndef GETUPTIME_METHOD_FOUND # if defined(sun) || defined(__hpux) || defined(__hppa) || \ defined(HPUX) || defined(hpux) # define GETUPTIME_UTMPX # define GETUPTIME_METHOD_FOUND # ifndef HPUX # define HPUX 1 # endif # endif #endif /* Tru64 */ #ifndef GETUPTIME_METHOD_FOUND # if defined(__osf__) || defined(__digital__) # define GETUPTIME_TBLSYSINFO # define GETUPTIME_METHOD_FOUND # endif #endif /* Look for Irix */ #if defined(irix) || defined(sgi) || defined(_sgi) || defined (__sgi) # define IRIX #endif /* Ultrix & Irix*/ #ifndef GETUPTIME_METHOD_FOUND # if defined(ultrix) || defined(IRIX) # define GETUPTIME_KMEM # define GETUPTIME_METHOD_FOUND # if defined(HAVE__VMUNIX) # define NLIST_KERNEL "/vmunix" # endif # if defined(HAVE__UNIX) # define NLIST_KERNEL "/unix" # endif # ifndef NLIST_KERNEL # error I could not find the kernel-file to use with nlist # endif # if defined(IRIX) && defined(HAVE_NLIST64) # define NLIST nlist64 # else # define NLIST nlist # endif # endif #endif /* Solve problem with err() not existing everywhere */ #if defined(HAVE_ERR) && defined(HAVE_ERR_H) # include #else # ifdef __GNUC__ # define err(a, b, args...) { fprintf(stderr, "uptimec: " b "\n", ##args); exit(a); } # else # define err(...) { fprintf(stderr, "uptimec: Internal error at line %i\n", __LINE__); do { abort(); } while(0); } # endif #endif /* * Includes used on some systems */ /* Pull in select.h if availible */ #ifdef HAVE_SYS_SELECT_H #include #include #endif /* Figure out if we can use select() on this system */ /* Notes: HP/UX does not require select.h to be included, the prototype for select() comes from socket.h */ #if defined(HAVE_SYS_SELECT_H) || defined(HPUX) #define USE_SELECT #endif /* We need some MD5-functions to use MD5-digested passwords */ #if !defined(HAVE_MD5INIT) || !defined(HAVE_MD5UPDATE) || !defined(HAVE_MD5FINAL) #define USE_PLAINTEXT_PASSWORD #endif /* Figure out where to get usable md5-headers */ #ifndef USE_PLAINTEXT_PASSWORD # ifdef HAVE_MD5_H # define PASSWORD_USE_MD5 # include # elif defined(HAVE_OPENSSL_MD5_H) # define PASSWORD_USE_MD5 # include # else # define USE_PLAINTEXT_PASSWORD # endif /* HAVE_MD5_H */ #endif /* ! USE_PLAINTEXT_PASSWORD */ /* We need syslog.h and the syslog()-function to send messages to syslog */ #if defined(HAVE_SYSLOG_H) && defined(HAVE_SYSLOG) # include # define DO_SYSLOG #endif /* * Pull in include-files based on OS */ /* Used by some of the BSD-like systems to extract uptime */ #ifdef GETUPTIME_BSDLIKE # include #endif /* Get access to utmpx- and time-functions */ #ifdef GETUPTIME_UTMPX # include # include #endif /* Needed to get uptime via kmem */ #ifdef GETUPTIME_KMEM # include # include # include #endif /* Needed on Tru64 */ #if defined(__osf__) || defined(__digital__) # include #endif /* Try to solve problems with getloadavg() */ #ifndef HAVE_GETLOADAVG /* * Insert os-specific solutions with #ifdef's here */ #if defined(__osf__) || defined(__digital__) /* Tru64 */ int getloadavg(double loadavg[], int nelem) { struct tbl_loadavg tbl_la; int i; if(nelem < 0 || nelem > 3) return -1; tbl_la.tl_lscale=1000; if(table(TBL_LOADAVG, 0, &tbl_la, 1, sizeof(tbl_la)) == -1) return -1; for(i=0; i int getloadavg(double loadavg[], int nelem) { int i; struct pst_dynamic info; if( nelem < 0 || nelem > 3 ) return -1; if(pstat_getdynamic(&info, sizeof(info), 0, 0) < 0) return -1; for(i=0; i #endif int getloadavg(double loadavg[], int nelem) { /* Get average load using kmem */ static struct NLIST nl[2]; static int nlist_loaded = 0; static int f = -1; int i; int l[3]; if(nelem < 0 || nelem > 3) return -1; /* Figure out where in /dev/kmem to fetch avg load */ if( ! nlist_loaded ) { nlist_loaded = 1; nl[0].n_name = "avenrun"; nl[1].n_name = NULL; if(NLIST(NLIST_KERNEL, nl) != 0) err(EXIT_FAILURE, "Failed to get avenrun-offset from %s.", NLIST_KERNEL); } /* Open /dev/kmem */ if(f == -1) if((f = open("/dev/kmem", O_RDONLY)) < 0) err(EXIT_FAILURE, "Failed to open /dev/kmem"); /* Extract load from kmem */ if(lseek(f, nl[0].n_value, SEEK_SET) == -1) err(EXIT_FAILURE, "Failed to access /dev/kmem"); if(read(f, &l, sizeof(l)) == -1) err(EXIT_FAILURE, "Failed to read average load from /dev/kmem"); /* Store average load in return-array */ for(i=0; i PASSWORD_MAX - 1) { printf("Enter a shorter password.\n"); exit(EXIT_FAILURE); } strncpy((char*)password, optarg, PASSWORD_MAX - 1); # ifndef HAVE_SETPROCTITLE /* Remove password from ps-list if we have no setproctitle() */ /* This should work on most systems */ strncpy(argv[optind-1], "****************", strlen(optarg)); # endif break; /* Specify server */ case 's': if(strlen(optarg) > SERVER_MAX - 1) { printf("Enter a shorter servername.\n"); exit(EXIT_FAILURE); } strncpy(server, optarg, SERVER_MAX - 1); break; /* Specify Server Port */ case 'P': serverport = atoi(optarg); if( serverport == 0 ) { printf("Bad server port: %s\n", optarg); exit(EXIT_FAILURE); } break; /* Show usage */ case '?': default: usage(); } argc -= optind; argc += optind; /* Make sure that we got all required arguments */ if(strlen((char*)password) == 0 || hostid == 0) usage(); /* * Detach from console */ if( detach ) { switch(fork()) { case -1: /* Error forking */ err(EXIT_FAILURE, "Unable to fork into background"); break; case 0: /* We are the child, continue as normal after switch-block */ break; default: /* We are the parent, exit and allow child to continue our work for us. Use _exit() to quit but leave resources availible for forked child. */ _exit(EXIT_SUCCESS); } # ifdef HAVE_SETSID /* Create new session without a controlling terminal */ if(setsid() == -1) err(EXIT_FAILURE, "Failed to create new session."); # endif } /* We do not want password etc given on commandline to be visible when listing processes with the ps-command */ # if defined(HAVE_SETPROCTITLE) # if defined(__FreeBSD__) setproctitle("-uptimec"); # else setproctitle(NULL); # endif /* __FreeBSD__ */ # endif /* HAVE_SETPROCTITLE */ /* Output startup-info to syslog */ # ifdef DO_SYSLOG if( VER_PATCH == 0 ) syslog(LOG_INFO, "Launching uptimec v%i.%i", VER_MAJOR, VER_MINOR); else syslog(LOG_INFO, "Launching uptimec v%i.%ip%i", VER_MAJOR, VER_MINOR, VER_PATCH); # endif /* * Digest password */ # if defined(PASSWORD_USE_MD5) && ! defined(USE_PLAINTEXT_PASSWORD) MD5Init(&md); MD5Update((MD5_CTX*)&md, password, strlen(password)); MD5Final(md5password, &md); # else /* Use plaintext-passwords as a last way out */ memset(md5password, '\0', 16); strncpy((char*) md5password, (const char*) password, 15); # endif #if 0 /* crypt-passwords */ /* XXX Broken */ s = crypt(password, "$1"); memcpy(md5password, s, 16); # endif /* PASSWORD_USE_MD5 */ /* * Init send-buffer */ initbuf(buf, hostid, md5password); /* * Figure out where to send packages */ do { if( (srvhe = gethostbyname(server)) == NULL ) { # ifdef DO_SYSLOG syslog(LOG_INFO, "Failed to resolve host '%s', retrying in 10 minutes.", server); # endif sleep(600); } } while( srvhe == NULL ); srvaddr.sin_family = AF_INET; srvaddr.sin_port = htons(serverport); memcpy(&srvaddr.sin_addr.s_addr, srvhe->h_addr, srvhe->h_length); memset(&(srvaddr.sin_zero), '\0', 8); /* Set rest of content to zero */ /* * Create socket */ if((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { err(EXIT_FAILURE, "Error creating socket"); exit(EXIT_FAILURE); } /* * Bind socket (Required in some environments) */ memset(&localaddr, '\0', sizeof(struct sockaddr_in)); /* Init to zero */ localaddr.sin_family = AF_INET; localaddr.sin_port = 0; if(bind(sock, (struct sockaddr*)&localaddr, sizeof(struct sockaddr_in)) == -1) { err(EXIT_FAILURE, "Error binding socket"); exit(EXIT_FAILURE); } /* * Do login */ for(;;) { /* Extract system information */ if((uname(&un)) == -1) { err(EXIT_FAILURE, "Could not extract system information"); exit(EXIT_FAILURE); } /* XXX Should just cut strings that are too long instead of failing */ if(strlen(un.sysname) > MAXLEN_SYSNAME || strlen(un.release) > MAXLEN_RELEASE || strlen(un.version) > MAXLEN_VERSION || strlen(un.machine) > MAXLEN_MACHINE ) { err(EXIT_FAILURE, "Bad system information extracted"); exit(EXIT_FAILURE); } /* Create block of data for login command */ tmpbuf[0] = CLIENTID; tmpbuf[1] = VER_MAJOR; tmpbuf[2] = VER_MINOR; tmpbuf[3] = VER_PATCH; for(i=0,j=6; i < 4; i++) switch(i) { /* Sysname */ case 0: strcpy((char*)tmpbuf+j, un.sysname); j+=strlen(un.sysname) + 1; break; /* Release */ case 1: strcpy((char*)tmpbuf+j, un.release); j+=strlen(un.release) + 1; break; /* Version */ case 2: strcpy((char*)tmpbuf+j, un.version); j+=strlen(un.version) + 1; break; /* Machine */ case 3: strcpy((char*)tmpbuf+j, un.machine); j+=strlen(un.machine) + 1; break; /* This should not happen */ default: err(EXIT_FAILURE, "Internal error"); exit(EXIT_FAILURE); } buflen = j; j-=6; tmpbuf[5] = j & 255; j >>= 8; /* Argument length */ tmpbuf[4] = j & 255; /* XXX Needs to handle situations where the entire package can't be sent at once, or received with just one recvfrom(); */ # ifndef USE_SELECT if( setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) { err(EXIT_FAILURE, "Error when modifying socket with setsockopt()"); exit(EXIT_FAILURE); } # endif for(i=0;;i++) { if(i==3) /* Three tries has failed, sleep for 1800 secs */ { # ifdef DO_SYSLOG syslog(LOG_INFO, "Failed to login three times in a row, sleeping 30 minutes before next try"); # endif sleep(1800); i=0; } /* Update send-buffer */ updatebuf(buf, CCMD_LOGIN, tmpbuf, buflen); /* Send login package */ l = sendto(sock, buf, HEADER_SIZE + buflen, 0, (struct sockaddr*) &srvaddr, sizeof(struct sockaddr_in)); # ifdef DEBUG printf("DEBUG: Sent %i bytes to ip %s with command %i.\n", l, inet_ntoa(srvaddr.sin_addr), (int) buf[1]); # endif /* Send login and wait for respons */ tv.tv_sec=30; tv.tv_usec=0; /* Wait for a reply from server */ /* XXX Should make sure that the received sockaddr-structure contains an address that is of the type we expect. */ sl = sizeof(remoteaddr); /* Required on some platforms */ # ifdef USE_SELECT FD_ZERO(&readfds); FD_SET(sock, &readfds); if((r = select(sock+1, &readfds, NULL, NULL, &tv)) > 0 ) { if((l = recvfrom(sock, recvbuf, BUF_SIZE, 0, (struct sockaddr*) &remoteaddr, &sl)) == -1) { err(EXIT_FAILURE, "Error reading from socket"); exit(EXIT_FAILURE); } # else if((l = recvfrom(sock, recvbuf, BUF_SIZE, 0, (struct sockaddr*) &remoteaddr, &sl)) != -1) { # endif /* Process data */ /* XXX We should allow for some other commands from the server here, for example, REQUESTSHUTDOWN and RELOGIN commands */ if((j = validatepackage(recvbuf)) == SCMD_LOGINOK) break; else if(j == SCMD_LOGINFAILED) { # ifdef DO_SYSLOG syslog(LOG_ERR, "Authorization failed, bailing out!"); # endif printf("uptimec: Authorization failed, bailing out.\n"); exit(EXIT_FAILURE); } # ifdef DEBUG else printf("DEBUG: Received unexpected command (%i), discarding...\n", j); # endif } else # ifdef USE_SELECT if( r < 0 ) { err(EXIT_FAILURE, "Error in select()"); exit(EXIT_FAILURE); } # else if(errno != EAGAIN) { err(EXIT_FAILURE, "Error reading from socket"); exit(EXIT_FAILURE); } # endif } # ifdef DO_SYSLOG syslog(LOG_INFO, "Did successful login against server %s, entering update-mode.", server); # endif /* Run main loop */ relogin=0; while(!relogin) { buflen = 10; /* Static */ /* Extract system information */ uptime = get_uptime(); get_load(loadavg); #ifdef DEBUG printf("DEBUG: Sending - Uptime: %i secs\n", uptime); printf(" Load1: %i\n", loadavg[0]); printf(" Load2: %i\n", loadavg[1]); printf(" Load3: %i\n", loadavg[2]); #endif /* * Insert data into buffer */ /* Uptime */ tmpbuf[3] = uptime & 255; uptime >>= 8; tmpbuf[2] = uptime & 255; uptime >>= 8; tmpbuf[1] = uptime & 255; uptime >>= 8; tmpbuf[0] = uptime & 255; /* Load */ tmpbuf[5] = loadavg[0] & 255; loadavg[0] >>= 8; tmpbuf[4] = loadavg[0] & 255; /* 1 min avg */ tmpbuf[7] = loadavg[1] & 255; loadavg[1] >>= 8; tmpbuf[6] = loadavg[1] & 255; /* 5 min avg */ tmpbuf[9] = loadavg[2] & 255; loadavg[2] >>= 8; tmpbuf[8] = loadavg[2] & 255; /* 15 min avg */ /* Update send-buffer */ updatebuf(buf, CCMD_UPDATE, tmpbuf, buflen); /* * Send login package */ l = sendto(sock, buf, HEADER_SIZE + buflen, 0, (struct sockaddr*) &srvaddr, sizeof(struct sockaddr_in)); #ifdef DEBUG printf("DEBUG: Sent %i bytes to ip %s with command %i.\n", l, inet_ntoa(srvaddr.sin_addr), (int) buf[1]); #endif /* XXX All packages received from the server should make sure that they have the same remote address as the server we logged in to */ /* * Listen for packages from the server */ /* Configure delay between each update */ /* The tv- and tl-variables are reset to these values at the end of each loop below. */ tv.tv_sec=30; /* Twenty periods of 30 seconds give 10 minutes */ tv.tv_usec=0; tl = time(NULL); /* Register time when loop started so that we know how long to sleep before next package in case we receive something from the server before our socket-read times out. */ /* Some systems seems to have problems handling longer timeouts than 30 seconds for SO_RCVTIMEO (setsockopt), so we'll go with one minute and loop ten twenty */ for(k=0; k < 20 && !relogin;) { /* tc.tc_secs should never become zero, or the client will hang forever, waiting for a package that it will never receive. So, just to be sure, we'll check it's value before configuring the socket based on it */ if( tv.tv_sec < 1 ) break; # ifndef USE_SELECT /* Instruct the socket on the maximum time to wait for next package */ if( setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) { err(EXIT_FAILURE, "Error when modifying socket with setsockopt()"); exit(EXIT_FAILURE); } # endif sl = sizeof(remoteaddr); /* Required on some platforms */ # ifdef USE_SELECT FD_ZERO(&readfds); FD_SET(sock, &readfds); if((r = select(sock+1, &readfds, NULL, NULL, &tv)) > 0 ) { if((l = recvfrom(sock, recvbuf, BUF_SIZE, 0, (struct sockaddr*) &remoteaddr, &sl)) == -1) { err(EXIT_FAILURE, "Error reading from socket"); exit(EXIT_FAILURE); } # else if((l = recvfrom(sock, recvbuf, BUF_SIZE, 0, (struct sockaddr*) &remoteaddr, &sl)) != -1) { # endif /* XXX We shouldn't even do this if we haven't received enough data to fill a header-block */ if( (i = validatepackage(recvbuf)) != -1 ) { /* We received a valid package */ /* Perform action based on packages command */ switch(i) { case SCMD_UPDATEOK: /* Just smile and be happy */ # ifdef DEBUG printf("DEBUG: Server validated our last update.\n"); # endif break; case SCMD_UPDATEFAILED: /* Not really much we can do if we receive this, so we'll just continue as normal */ # ifdef DEBUG printf("DEBUG: Server said that the last update failed.\n"); # endif break; case SCMD_REQUESTSHUTDOWN: # ifdef DEBUG printf("DEBUG: Server asked us to shutdown."); # endif # ifdef DO_SYSLOG syslog(LOG_ALERT, "The server requested that we shutdown"); # endif exit(EXIT_SUCCESS); break; /* We will never reach this */ case SCMD_REQUESTRELOGIN: # ifdef DEBUG printf("DEBUG: Server asked us to login again."); # endif # ifdef DO_SYSLOG syslog(LOG_ALERT, "The server requested that we do a new login"); # endif relogin=1; break; default: /* We received a command that we did not expect here, ignore it */ # ifdef DEBUG printf("DEBUG: We received an unexpected command from server (%i)\n", i); # endif break; } } /* Figure out how long we should wait before next update */ if((j = time(NULL) - tl) < 30) { /* Set time to configure socket to timeout on before we send the next update */ tv.tv_sec = 30 - j; continue; } } else # ifdef USE_SELECT if( r < 0 ) { err(EXIT_FAILURE, "Error in select()"); exit(EXIT_FAILURE); } # else if(errno != EAGAIN) { /* Failed to read from socket */ err(EXIT_FAILURE, "Error reading from socket"); exit(EXIT_FAILURE); } /* Else: Nothing received, continue with next loop */ # endif /* If we reach this part, we did not receive any data under the last receive period, so we should reset our timeout- numbers and go on with next loop */ tv.tv_sec=30; tv.tv_usec=0; tl = time(NULL); k++; /* Increase loop-counter */ } } /* while(!relogin) - The main loop */ } /* for(;;) - Will exit to this loop for relogins */ } void usage(void) { printf("Usage: uptimec [-d] [-s server] [-P port] -i hostid -p password\n"); printf(" uptimec -v\n"); printf(" uptimec -h\n"); exit(EXIT_FAILURE); } void help(void) { printf("Help\n\n"); /* XXX Add help message */ printf("Options:\n"); printf(" -d Do not detach.\n"); printf(" -i hostid Numeric uptime-id for this host.\n"); printf(" -p password Password to use when talking to server.\n"); printf(" -s server Specify server to send updates to.\n"); printf(" -P port Specify server port.\n"); printf(" -v Display client version.\n"); printf(" -h View this information.\n\n"); exit(EXIT_SUCCESS); } void version(void) { if( VER_PATCH == 0 ) printf("Uptimec version %i.%i\n", VER_MAJOR, VER_MINOR); else printf("Uptimec version %i.%ip%i\n", VER_MAJOR, VER_MINOR, VER_PATCH); printf("Copyright (c) %s Ola Eriksson, All Rights Reserved\n", COPYRIGHT_YEAR); printf("This is free software; see the source for copying conditions.\n\n"); printf("Visit http://www.mrEriksson.net/ for more information.\n"); exit(EXIT_SUCCESS); } void initbuf(unsigned char* buf, int hostid, unsigned char* md5password) { long l = hostid; /* Construct package */ buf[0] = PROTVERSION; buf[1] = CCMD_VOID; /* Init as no command */ buf[2] = 0; /* Init to zero */ buf[3] = 0; /* -"- */ /* Insert hosts id */ buf[7] = l & 255; l >>= 8; buf[6] = l & 255; l >>= 8; buf[5] = l & 255; l >>= 8; buf[4] = l & 255; /* Insert digested password into header */ memcpy(buf + 8, md5password, 16); } void updatebuf(unsigned char* buf, int cmd, unsigned char* data, unsigned int datalen) { static unsigned char seq = 0; /* Insert command */ buf[1] = (unsigned char)cmd; /* Insert sequence number */ buf[2] = seq; /* Calculate checksum */ buf[3] = (buf[0] ^ buf[1]) ^ buf[2]; /* Add data, if any */ if(data != NULL) { /* Make sure we didn't receive a too large block of data */ if(datalen > BUF_SIZE - HEADER_SIZE) { printf("Internal error occured.\n"); exit(EXIT_FAILURE); } memcpy(buf + HEADER_SIZE, data, datalen); } /* Update sequence number */ if(seq == 255) seq = 0; else seq++; } int validatepackage(unsigned char* buf) { int i; int j; #ifdef DEBUG_DUMPHEADER printf("Dumping header:\n"); for(i=0; i < 4; i++) printf("Byte %i: %i\n", i, (int)buf[i]); printf("---[END OF DUMP]---\n"); #endif /* Validate checksum */ j = (buf[0] ^ buf[1]) ^ buf[2]; if(j != buf[3]) { #ifdef DEBUG printf("DEBUG: Received package with invalid checksum, discarding...\n"); #endif return -1; } j = -1; /* Make sure that this is a valid command */ for(i=0; i < sizeof(VALID_SCMDS) / sizeof(VALID_SCMDS[0]); i++) if( buf[1] == VALID_SCMDS[i] ) j = buf[1]; if(j == -1) { #ifdef DEBUG printf("DEBUG: Received unknown command (%i), discarding...\n", buf[1]); #endif return -1; } /* Valid package, return packages command */ return buf[1]; } time_t get_uptime(void) { time_t uptime = 0; # define GETUPTIME_NO_METHOD_FOUND /* NetBSD, FreeBSD, OpenBSD and MacOS X */ # ifdef GETUPTIME_BSDLIKE # undef GETUPTIME_NO_METHOD_FOUND int mib[2]; size_t size; struct timeval boottime; time_t now; mib[0] = CTL_KERN; mib[1] = KERN_BOOTTIME; size = sizeof(boottime); time(&now); if( sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 ) if( boottime.tv_sec != 0 ) { uptime = now - boottime.tv_sec; uptime += 30; } else { errx(1, "Bad boottime value"); } # endif /* defined(GETUPTIME_BSDLIKE) */ /* Most Linux-systems */ # ifdef GETUPTIME_PROCUPTIME # undef GETUPTIME_NO_METHOD_FOUND /* Try to get uptime from /proc/uptime */ FILE *f; double duptime = 0; f = fopen("/proc/uptime", "r"); if(!f) { err(EXIT_FAILURE, "Failed to open /proc/uptime"); exit(EXIT_FAILURE); } if(fscanf(f, "%lf", &duptime) != 1) { err(EXIT_FAILURE, "Failed to get valid uptime from /proc/uptime"); exit(EXIT_FAILURE); } fclose(f); /* XXX Some Linuxes do uptime wraparounds, we should compensate for this */ uptime = (time_t) duptime; # endif /* defined(GETUPTIME_PROCUPTIME) */ /* Solaris and HP/UX */ # ifdef GETUPTIME_UTMPX # undef GETUPTIME_NO_METHOD_FOUND /* Extract uptime from utmpx */ struct utmpx id; struct utmpx* u; /* Rewind utmpx entry pointer */ setutxent(); id.ut_type = BOOT_TIME; u = getutxid(&id); if( u == NULL ) err(EXIT_FAILURE, "Failed to extract uptime"); uptime = time(NULL) - u->ut_tv.tv_sec; # endif /* defined(GETUPTIME_UTMPX) */ /* Ultrix & Irix */ # ifdef GETUPTIME_KMEM # undef GETUPTIME_NO_METHOD_FOUND /* Extract uptime from kmem */ static struct NLIST nl[2]; static nlist_loaded = 0; static int f = -1; time_t boottime; /* Figure out where in kmem we can find the boottime */ if(!nlist_loaded) { nlist_loaded = 1; nl[0].n_name = "boottime"; nl[1].n_name = NULL; if(NLIST(NLIST_KERNEL, nl) != 0) err(EXIT_FAILURE, "Unable to get boottime-offset from %s.", NLIST_KERNEL); } /* Open /dev/kmem */ if(f == -1) if((f = open("/dev/kmem", O_RDONLY)) < 0) err(EXIT_FAILURE, "Failed to open /dev/kmem"); /* Read boottime from kmem */ if(lseek(f, nl[0].n_value, SEEK_SET) == -1) err(EXIT_FAILURE, "Failed to access /dev/kmem"); if(read(f, &boottime, sizeof(boottime)) == -1) err(EXIT_FAILURE, "Failed to read boottime from /dev/kmem"); /* Calculate uptime */ uptime = time(NULL) - boottime; #endif /* Tru64 */ # ifdef GETUPTIME_TBLSYSINFO # undef GETUPTIME_NO_METHOD_FOUND struct tbl_sysinfo tbl_si; if(table(TBL_SYSINFO, 0, &tbl_si, 1, sizeof(tbl_si)) == -1 ) err(EXIT_FAILURE, "Failed to get boottime from TBL_SYSINFO"); uptime = time(NULL) - tbl_si.si_boottime; # endif /* Make sure that we know how to extract uptime */ # ifdef GETUPTIME_NO_METHOD_FOUND # error I do not know how to extract uptime from this system! # endif return uptime; } int get_load(int ret[]) { double loadavg[3]; int i; if(getloadavg(loadavg, sizeof(loadavg) / sizeof(loadavg[0])) == -1) { for(i=0; i<3; i++) ret[i] = 65535; return 0; } for(i=0; i < 3; i++) if(loadavg[i] < 655) ret[i] = (int) (loadavg[i] * 100.0); else ret[i] = 65500; return 1; }