📄 smartd.cpp
字号:
snprintf(command, 2048, "%s 2>&1", executable); // tell SYSLOG what we are about to do... newadd=address?address:"<nomailer>"; newwarn=which?"Warning via":"Test of"; PrintOut(LOG_INFO,"%s %s to %s ...\n", which?"Sending warning via":"Executing test of", executable, newadd); // issue the command to send mail or to run the user's executable errno=0; if (!(pfp=popen(command, "r"))) // failed to popen() mail process PrintOut(LOG_CRIT,"%s %s to %s: failed (fork or pipe failed, or no memory) %s\n", newwarn, executable, newadd, errno?strerror(errno):""); else { // pipe suceeded! int len, status; char buffer[EBUFLEN]; // if unexpected output on stdout/stderr, null terminate, print, and flush if ((len=fread(buffer, 1, EBUFLEN, pfp))) { int count=0; int newlen = len<EBUFLEN ? len : EBUFLEN-1; buffer[newlen]='\0'; PrintOut(LOG_CRIT,"%s %s to %s produced unexpected output (%s%d bytes) to STDOUT/STDERR: \n%s\n", newwarn, executable, newadd, len!=newlen?"here truncated to ":"", newlen, buffer); // flush pipe if needed while (fread(buffer, 1, EBUFLEN, pfp) && count<EBUFLEN) count++; // tell user that pipe was flushed, or that something is really wrong if (count && count<EBUFLEN) PrintOut(LOG_CRIT,"%s %s to %s: flushed remaining STDOUT/STDERR\n", newwarn, executable, newadd); else if (count) PrintOut(LOG_CRIT,"%s %s to %s: more than 1 MB STDOUT/STDERR flushed, breaking pipe\n", newwarn, executable, newadd); } // if something went wrong with mail process, print warning errno=0; if (-1==(status=pclose(pfp))) PrintOut(LOG_CRIT,"%s %s to %s: pclose(3) failed %s\n", newwarn, executable, newadd, errno?strerror(errno):""); else { // mail process apparently succeeded. Check and report exit status int status8; if (WIFEXITED(status)) { // exited 'normally' (but perhaps with nonzero status) status8=WEXITSTATUS(status); if (status8>128) PrintOut(LOG_CRIT,"%s %s to %s: failed (32-bit/8-bit exit status: %d/%d) perhaps caught signal %d [%s]\n", newwarn, executable, newadd, status, status8, status8-128, strsignal(status8-128)); else if (status8) PrintOut(LOG_CRIT,"%s %s to %s: failed (32-bit/8-bit exit status: %d/%d)\n", newwarn, executable, newadd, status, status8); else PrintOut(LOG_INFO,"%s %s to %s: successful\n", newwarn, executable, newadd); } if (WIFSIGNALED(status)) PrintOut(LOG_INFO,"%s %s to %s: exited because of uncaught signal %d [%s]\n", newwarn, executable, newadd, WTERMSIG(status), strsignal(WTERMSIG(status))); // this branch is probably not possible. If subprocess is // stopped then pclose() should not return. if (WIFSTOPPED(status)) PrintOut(LOG_CRIT,"%s %s to %s: process STOPPED because it caught signal %d [%s]\n", newwarn, executable, newadd, WSTOPSIG(status), strsignal(WSTOPSIG(status))); } } #else // _WIN32 // No "here-documents" on Windows, so must use separate commandline and stdin command[0] = stdinbuf[0] = 0; boxtype = -1; boxmsgoffs = 0; newadd = "<nomailer>"; if (address) { // address "[sys]msgbox ..." => show warning (also) as [system modal ]messagebox int addroffs = (!strncmp(address, "sys", 3) ? 3 : 0); if (!strncmp(address+addroffs, "msgbox", 6) && (!address[addroffs+6] || address[addroffs+6] == ',')) { boxtype = (addroffs > 0 ? 1 : 0); addroffs += 6; if (address[addroffs]) addroffs++; } else addroffs = 0; if (address[addroffs]) { // Use "blat" parameter syntax (TODO: configure via -M for other mailers) snprintf(command, sizeof(command), "%s - -q -subject \"%s\" -to \"%s\"", executable, subject, address+addroffs); newadd = address+addroffs; } // Message for mail [0...] and messagebox [boxmsgoffs...] snprintf(stdinbuf, sizeof(stdinbuf), "This email was generated by the smartd daemon running on:\n\n" " host name: %s\n" " DNS domain: %s\n"// " NIS domain: %s\n" "\n%n" "The following warning/error was logged by the smartd daemon:\n\n" "%s\n\n" "For details see the event log or log file of smartd.\n\n" "%s%s%s" "\n", hostname, /*domainname, */ nisdomain, &boxmsgoffs, message, further, original, additional); } else snprintf(command, sizeof(command), "%s", executable); newwarn=which?"Warning via":"Test of"; if (boxtype >= 0) { // show message box daemon_messagebox(boxtype, subject, stdinbuf+boxmsgoffs); PrintOut(LOG_INFO,"%s message box\n", newwarn); } if (command[0]) { char stdoutbuf[800]; // < buffer in syslog_win32::vsyslog() int rc; // run command PrintOut(LOG_INFO,"%s %s to %s ...\n", (which?"Sending warning via":"Executing test of"), executable, newadd); rc = daemon_spawn(command, stdinbuf, strlen(stdinbuf), stdoutbuf, sizeof(stdoutbuf)); if (rc >= 0 && stdoutbuf[0]) PrintOut(LOG_CRIT,"%s %s to %s produced unexpected output (%d bytes) to STDOUT/STDERR:\n%s\n", newwarn, executable, newadd, strlen(stdoutbuf), stdoutbuf); if (rc != 0) PrintOut(LOG_CRIT,"%s %s to %s: failed, exit status %d\n", newwarn, executable, newadd, rc); else PrintOut(LOG_INFO,"%s %s to %s: successful\n", newwarn, executable, newadd); }#endif // _WIN32 // increment mail sent counter mail->logged++; // free copy of address (without commas) address=FreeNonZero(address, -1, __LINE__, filenameandversion); return;}// Printing function for watching ataprint commands, or losing them// [From GLIBC Manual: Since the prototype doesn't specify types for// optional arguments, in a call to a variadic function the default// argument promotions are performed on the optional argument// values. This means the objects of type char or short int (whether// signed or not) are promoted to either int or unsigned int, as// appropriate.]void pout(const char *fmt, ...){ va_list ap; // get the correct time in syslog() FixGlibcTimeZoneBug(); // initialize variable argument list va_start(ap,fmt); // in debug==1 mode we will print the output from the ataprint.o functions! if (debugmode && debugmode!=2)#ifdef _WIN32 if (facility == LOG_LOCAL1) // logging to stdout vfprintf(stderr,fmt,ap); else #endif vprintf(fmt,ap); // in debug==2 mode we print output from knowndrives.o functions else if (debugmode==2 || con->reportataioctl || con->reportscsiioctl || con->controller_port) { openlog("smartd", LOG_PID, facility); vsyslog(LOG_INFO, fmt, ap); closelog(); } va_end(ap); fflush(NULL); return;}// This function prints either to stdout or to the syslog as needed.// This function is also used by utility.cpp to report LOG_CRIT errors.void PrintOut(int priority, const char *fmt, ...){ va_list ap; // get the correct time in syslog() FixGlibcTimeZoneBug(); // initialize variable argument list va_start(ap,fmt); if (debugmode) #ifdef _WIN32 if (facility == LOG_LOCAL1) // logging to stdout vfprintf(stderr,fmt,ap); else #endif vprintf(fmt,ap); else { openlog("smartd", LOG_PID, facility); vsyslog(priority,fmt,ap); closelog(); } va_end(ap); return;}// Wait for the pid file to show up, this makes sure a calling program knows// that the daemon is really up and running and has a pid to kill itbool WaitForPidFile(){ int waited, max_wait = 10; struct stat stat_buf; if(!pid_file || debugmode) return true; for(waited = 0; waited < max_wait; ++waited) { if(stat(pid_file, &stat_buf) == 0) { return true; } else sleep(1); } return false;}// Forks new process, closes ALL file descriptors, redirects stdin,// stdout, and stderr. Not quite daemon(). See// http://www.iar.unlp.edu.ar/~fede/revistas/lj/Magazines/LJ47/2335.html// for a good description of why we do things this way.void DaemonInit(){#ifndef _WIN32 pid_t pid; int i; // flush all buffered streams. Else we might get two copies of open // streams since both parent and child get copies of the buffers. fflush(NULL); if (do_fork) { if ((pid=fork()) < 0) { // unable to fork! PrintOut(LOG_CRIT,"smartd unable to fork daemon process!\n"); EXIT(EXIT_STARTUP); } else if (pid) // we are the parent process, wait for pid file, then exit cleanly if(!WaitForPidFile()) { PrintOut(LOG_CRIT,"PID file %s didn't show up!\n", pid_file); EXIT(EXIT_STARTUP); } else EXIT(0); // from here on, we are the child process. setsid(); // Fork one more time to avoid any possibility of having terminals if ((pid=fork()) < 0) { // unable to fork! PrintOut(LOG_CRIT,"smartd unable to fork daemon process!\n"); EXIT(EXIT_STARTUP); } else if (pid) // we are the parent process -- exit cleanly EXIT(0); // Now we are the child's child... } // close any open file descriptors for (i=getdtablesize();i>=0;--i) close(i); #ifdef __CYGWIN__ // Cygwin's setsid() does not detach the process from Windows console FreeConsole();#endif // __CYGWIN__ // redirect any IO attempts to /dev/null for stdin i=open("/dev/null",O_RDWR); // stdout dup(i); // stderr dup(i); umask(0); chdir("/"); if (do_fork) PrintOut(LOG_INFO, "smartd has fork()ed into background mode. New PID=%d.\n", (int)getpid());#else // _WIN32 // No fork() on native Win32 // Detach this process from console fflush(NULL); if (daemon_detach("smartd")) { PrintOut(LOG_CRIT,"smartd unable to detach from console!\n"); EXIT(EXIT_STARTUP); } // stdin/out/err now closed if not redirected#endif // _WIN32 return;}// create a PID file containing the current process idvoid WritePidFile() { if (pid_file) { int error = 0; pid_t pid = getpid(); mode_t old_umask; FILE* fp; #ifndef __CYGWIN__ old_umask = umask(0077); // rwx------#else // Cygwin: smartd service runs on system account, ensure PID file can be read by admins old_umask = umask(0033); // rwxr--r--#endif fp = fopen(pid_file, "w"); umask(old_umask); if (fp == NULL) { error = 1; } else if (fprintf(fp, "%d\n", (int)pid) <= 0) { error = 1; } else if (fclose(fp) != 0) { error = 1; } if (error) { PrintOut(LOG_CRIT, "unable to write PID file %s - exiting.\n", pid_file); EXIT(EXIT_PID); } PrintOut(LOG_INFO, "file %s written containing PID %d\n", pid_file, (int)pid); } return;}// Prints header identifying version of code and homevoid PrintHead(){#ifdef HAVE_GET_OS_VERSION_STR const char * ver = get_os_version_str();#else const char * ver = SMARTMONTOOLS_BUILD_HOST;#endif PrintOut(LOG_INFO,"smartd version %s [%s] Copyright (C) 2002-8 Bruce Allen\n", PACKAGE_VERSION, ver); PrintOut(LOG_INFO,"Home page is " PACKAGE_HOMEPAGE "\n\n"); return;}// prints help info for configuration file Directivesvoid Directives() { PrintOut(LOG_INFO, "Configuration file (%s) Directives (after device name):\n" " -d TYPE Set the device type: ata, scsi, marvell, removable, sat, 3ware,N, hpt,L/M/N, cciss,N\n" " -T TYPE Set the tolerance to one of: normal, permissive\n" " -o VAL Enable/disable automatic offline tests (on/off)\n" " -S VAL Enable/disable attribute autosave (on/off)\n" " -n MODE No check if: never[,q], sleep[,q], standby[,q], idle[,q]\n" " -H Monitor SMART Health Status, report if failed\n" " -s REG Do Self-Test at time(s) given by regular expression REG\n" " -l TYPE Monitor SMART log. Type is one of: error, selftest\n" " -f Monitor 'Usage' Attributes, report failures\n" " -m ADD Send email warning to address ADD\n" " -M TYPE Modify email warning behavior (see man page)\n" " -p Report changes in 'Prefailure' Attributes\n" " -u Report changes in 'Usage' Attributes\n" " -t Equivalent to -p and -u Directives\n" " -r ID Also report Raw values of Attribute ID with -p, -u or -t\n" " -R ID Track changes in Attribute ID Raw value with -p, -u or -t\n" " -i ID Ignore Attribute ID for -f Directive\n" " -I ID Ignore Attribute ID for -p, -u or -t Directive\n" " -C ID Monitor Current Pending Sectors in Attribute ID\n" " -U ID Monitor Offline Uncorrectable Sectors in Attribute ID\n" " -W D,I,C Monitor Temperature D)ifference, I)nformal limit, C)ritical limit\n" " -v N,ST Modifies labeling of Attribute N (see man page) \n" " -P TYPE Drive-specific presets: use, ignore, show, showall\n" " -a Default: -H -f -t -l error -l selftest -C 197 -U 198\n" " -F TYPE Firmware bug workaround: none, samsung, samsung2, samsung3\n" " # Comment: text after a hash sign is ignored\n" " \\ Line continuation character\n" "Attribute ID is a decimal integer 1 <= ID <= 255\n"
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -