📄 utility.cpp
字号:
/* * utility.cpp * * Home page of code is: http://smartmontools.sourceforge.net * * Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net> * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org> * * 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, or (at your option) * any later version. * * You should have received a copy of the GNU General Public License * (for example COPYING); if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * This code was originally developed as a Senior Thesis by Michael Cornwell * at the Concurrent Systems Laboratory (now part of the Storage Systems * Research Center), Jack Baskin School of Engineering, University of * California, Santa Cruz. http://ssrc.soe.ucsc.edu/ * */// THIS FILE IS INTENDED FOR UTILITY ROUTINES THAT ARE APPLICABLE TO// BOTH SCSI AND ATA DEVICES, AND THAT MAY BE USED IN SMARTD,// SMARTCTL, OR BOTH.#include <stdio.h>#include <string.h>#include <time.h>#include <errno.h>#include <stdlib.h>#include <ctype.h>#include <syslog.h>#include <stdarg.h>#include <sys/stat.h>#ifdef _WIN32#include <mbstring.h> // _mbsinc()#endif#include "config.h"#include "int64.h"#include "utility.h"// Any local header files should be represented by a CVSIDX just below.const char* utility_c_cvsid="$Id: utility.cpp,v 1.65 2008/03/04 22:09:47 ballen4705 Exp $"CONFIG_H_CVSID INT64_H_CVSID UTILITY_H_CVSID;const char * packet_types[] = { "Direct-access (disk)", "Sequential-access (tape)", "Printer", "Processor", "Write-once (optical disk)", "CD/DVD", "Scanner", "Optical memory (optical disk)", "Medium changer", "Communications", "Graphic arts pre-press (10)", "Graphic arts pre-press (11)", "Array controller", "Enclosure services", "Reduced block command (simplified disk)", "Optical card reader/writer"};// Whenever exit() status is EXIT_BADCODE, please print this messageconst char *reportbug="Please report this bug to the Smartmontools developers at " PACKAGE_BUGREPORT ".\n";// hang on to exit code, so we can make use of more generic 'atexit()'// functionality and still check our exit codeint exitstatus = 0;// command-line argument: are we running in debug mode?.unsigned char debugmode = 0;// Solaris only: Get site-default timezone. This is called from// UpdateTimezone() when TZ environment variable is unset at startup.#if defined (__SVR4) && defined (__sun)static const char *TIMEZONE_FILE = "/etc/TIMEZONE";static char *ReadSiteDefaultTimezone(){ FILE *fp; char buf[512], *tz; int n; tz = NULL; fp = fopen(TIMEZONE_FILE, "r"); if(fp == NULL) return NULL; while(fgets(buf, sizeof(buf), fp)) { if (strncmp(buf, "TZ=", 3)) // searches last "TZ=" line continue; n = strlen(buf) - 1; if (buf[n] == '\n') buf[n] = 0; if (tz) free(tz); tz = strdup(buf); } fclose(fp); return tz;}#endif// Make sure that this executable is aware if the user has changed the// time-zone since the last time we polled devices. The cannonical// example is a user who starts smartd on a laptop, then flies across// time-zones with a laptop, and then changes the timezone, WITHOUT// restarting smartd. This is a work-around for a bug in// GLIBC. Yuk. See bug number 48184 at http://bugs.debian.org and// thanks to Ian Redfern for posting a workaround.// Please refer to the smartd manual page, in the section labeled LOG// TIMESTAMP TIMEZONE.void FixGlibcTimeZoneBug(){#if __GLIBC__ if (!getenv("TZ")) { putenv("TZ=GMT"); tzset(); putenv("TZ"); tzset(); }#elif _WIN32 if (!getenv("TZ")) { putenv("TZ=GMT"); tzset(); putenv("TZ="); // empty value removes TZ, putenv("TZ") does nothing tzset(); }#elif defined (__SVR4) && defined (__sun) // In Solaris, putenv("TZ=") sets null string and invalid timezone. // putenv("TZ") does nothing. With invalid TZ, tzset() do as if // TZ=GMT. With TZ unset, /etc/TIMEZONE will be read only _once_ at // first tzset() call. Conclusion: Unlike glibc, dynamic // configuration of timezone can be done only by changing actual // value of TZ environment value. enum tzstate { NOT_CALLED_YET, USER_TIMEZONE, TRACK_TIMEZONE }; static enum tzstate state = NOT_CALLED_YET; static struct stat prev_stat; static char *prev_tz; struct stat curr_stat; char *curr_tz; if(state == NOT_CALLED_YET) { if(getenv("TZ")) { state = USER_TIMEZONE; // use supplied timezone } else { state = TRACK_TIMEZONE; if(stat(TIMEZONE_FILE, &prev_stat)) { state = USER_TIMEZONE; // no TZ, no timezone file; use GMT forever } else { prev_tz = ReadSiteDefaultTimezone(); // track timezone file change if(prev_tz) putenv(prev_tz); } } tzset(); } else if(state == TRACK_TIMEZONE) { if(stat(TIMEZONE_FILE, &curr_stat) == 0 && (curr_stat.st_ctime != prev_stat.st_ctime || curr_stat.st_mtime != prev_stat.st_mtime)) { // timezone file changed curr_tz = ReadSiteDefaultTimezone(); if(curr_tz) { putenv(curr_tz); if(prev_tz) free(prev_tz); prev_tz = curr_tz; prev_stat = curr_stat; } } tzset(); }#endif // OTHER OS/LIBRARY FIXES SHOULD GO HERE, IF DESIRED. PLEASE TRY TO // KEEP THEM INDEPENDENT. return;}#ifdef _WIN32// Fix strings in tzname[] to avoid long names with non-ascii characters.// If TZ is not set, tzset() in the MSVC runtime sets tzname[] to the// national language timezone names returned by GetTimezoneInformation().static char * fixtzname(char * dest, int destsize, const char * src){ int i = 0, j = 0; while (src[i] && j < destsize-1) { int i2 = (const char *)_mbsinc((const unsigned char *)src+i) - src; if (i2 > i+1) i = i2; // Ignore multibyte chars else { if ('A' <= src[i] && src[i] <= 'Z') dest[j++] = src[i]; // "Pacific Standard Time" => "PST" i++; } } if (j < 2) j = 0; dest[j] = 0; return dest;}#endif // _WIN32// This value follows the peripheral device type value as defined in// SCSI Primary Commands, ANSI INCITS 301:1997. It is also used in// the ATA standard for packet devices to define the device type.const char *packetdevicetype(int type){ if (type<0x10) return packet_types[type]; if (type<0x20) return "Reserved"; return "Unknown";}// Returns 1 if machine is big endian, else zero. This is a run-time// rather than a compile-time function. We could do it at// compile-time but in principle there are architectures that can run// with either byte-ordering.int isbigendian(){ short i=0x0100; char *tmp=(char *)&i; return *tmp;}// Utility function prints date and time and timezone into a character// buffer of length>=64. All the fuss is needed to get the right// timezone info (sigh).void dateandtimezoneepoch(char *buffer, time_t tval){ struct tm *tmval; char *timezonename; char datebuffer[DATEANDEPOCHLEN]; int lenm1;#ifdef _WIN32 char tzfixbuf[6+1];#endif FixGlibcTimeZoneBug(); // Get the time structure. We need this to determine if we are in // daylight savings time or not. tmval=localtime(&tval); // Convert to an ASCII string, put in datebuffer // same as: asctime_r(tmval, datebuffer); strncpy(datebuffer, asctime(tmval), DATEANDEPOCHLEN); datebuffer[DATEANDEPOCHLEN-1]='\0'; // Remove newline lenm1=strlen(datebuffer)-1; datebuffer[lenm1>=0?lenm1:0]='\0'; // correct timezone name if (tmval->tm_isdst==0) // standard time zone timezonename=tzname[0]; else if (tmval->tm_isdst>0) // daylight savings in effect timezonename=tzname[1]; else // unable to determine if daylight savings in effect timezonename="";#ifdef _WIN32 // Fix long non-ascii timezone names if (!getenv("TZ")) timezonename=fixtzname(tzfixbuf, sizeof(tzfixbuf), timezonename);#endif // Finally put the information into the buffer as needed. snprintf(buffer, DATEANDEPOCHLEN, "%s %s", datebuffer, timezonename); return;}// Date and timezone gets printed into string pointed to by buffervoid dateandtimezone(char *buffer){ // Get the epoch (time in seconds since Jan 1 1970) time_t tval=time(NULL); dateandtimezoneepoch(buffer, tval); return;}// These are two utility functions for printing CVS IDs. Massagecvs()// returns distance that it has moved ahead in the input stringint massagecvs(char *out, const char *cvsid){ char *copy,*filename,*date,*version; int retVal=0; const char delimiters[] = " ,$"; // make a copy on the heap, go to first token, if (!(copy=strdup(cvsid))) return 0; if (!(filename=strtok(copy, delimiters))) goto endmassage; // move to first instance of "Id:" while (strcmp(filename,"Id:")) if (!(filename=strtok(NULL, delimiters))) goto endmassage; // get filename, skip "v", get version and date if (!( filename=strtok(NULL, delimiters) ) || !( strtok(NULL, delimiters) ) || !( version=strtok(NULL, delimiters) ) || !( date=strtok(NULL, delimiters) ) ) goto endmassage; sprintf(out,"%-16s revision: %-5s date: %-15s", filename, version, date); retVal = (date-copy)+strlen(date); endmassage: free(copy); return retVal;}// prints a single set of CVS idsvoid printone(char *block, const char *cvsid){ char strings[CVSMAXLEN]; const char *here=cvsid; int bi=0, len=strlen(cvsid)+1; // check that the size of the output block is sufficient if (len>=CVSMAXLEN) { pout("CVSMAXLEN=%d must be at least %d\n",CVSMAXLEN,len+1); EXIT(1); } // loop through the different strings while (bi<CVSMAXLEN && (len=massagecvs(strings,here))){ bi+=snprintf(block+bi,CVSMAXLEN-bi,"%s %s\n",(bi==0?"Module:":" uses:"),strings); here+=len; } return;}// A replacement for perror() that sends output to our choice of// printing. If errno not set then just print message.void syserror(const char *message){ if (errno) { // Get the correct system error message: const char *errormessage=strerror(errno); // Check that caller has handed a sensible string, and provide // appropriate output. See perrror(3) man page to understand better. if (message && *message) pout("%s: %s\n",message, errormessage);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -