/* ** ZABBIX ** Copyright (C) 2000-2005 SIA Zabbix ** ** 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. ** ** 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 "common.h" #include "sysinfo.h" #include "log.h" #include "threads.h" #include "file.h" #include "http.h" #include "net.h" #include "system.h" #if !defined(_WINDOWS) # define VFS_TEST_FILE "/etc/passwd" # define VFS_TEST_REGEXP "root" #else # define VFS_TEST_FILE "c:\\windows\\win.ini" # define VFS_TEST_REGEXP "fonts" #endif /* _WINDOWS */ static int AGENT_PING(const char *cmd, const char *param, unsigned flags, AGENT_RESULT *result); static int AGENT_VERSION(const char *cmd, const char *param, unsigned flags, AGENT_RESULT *result); static int ONLY_ACTIVE(const char *cmd, const char *param, unsigned flags, AGENT_RESULT *result); ZBX_METRIC parameters_common[]= /* KEY FLAG FUNCTION ADD_PARAM TEST_PARAM */ { {"agent.ping", 0, AGENT_PING, 0, 0}, {"agent.version", 0, AGENT_VERSION, 0, 0}, {"system.localtime", 0, SYSTEM_LOCALTIME, 0, 0}, {"system.run", CF_USEUPARAM, RUN_COMMAND, 0, "echo test"}, {"web.page.get", CF_USEUPARAM, WEB_PAGE_GET, 0, "localhost,,80"}, {"web.page.perf", CF_USEUPARAM, WEB_PAGE_PERF, 0, "localhost,,80"}, {"web.page.regexp", CF_USEUPARAM, WEB_PAGE_REGEXP, 0, "localhost,,80,OK"}, {"vfs.file.exists", CF_USEUPARAM, VFS_FILE_EXISTS, 0, VFS_TEST_FILE}, {"vfs.file.time", CF_USEUPARAM, VFS_FILE_TIME, 0, VFS_TEST_FILE ",modify"}, {"vfs.file.size", CF_USEUPARAM, VFS_FILE_SIZE, 0, VFS_TEST_FILE}, {"vfs.file.regexp", CF_USEUPARAM, VFS_FILE_REGEXP, 0, VFS_TEST_FILE "," VFS_TEST_REGEXP}, {"vfs.file.regmatch", CF_USEUPARAM, VFS_FILE_REGMATCH, 0, VFS_TEST_FILE "," VFS_TEST_REGEXP}, {"vfs.file.cksum", CF_USEUPARAM, VFS_FILE_CKSUM, 0, VFS_TEST_FILE}, {"vfs.file.md5sum", CF_USEUPARAM, VFS_FILE_MD5SUM, 0, VFS_TEST_FILE}, {"net.tcp.dns", CF_USEUPARAM, CHECK_DNS, 0, "127.0.0.1,localhost"}, {"net.tcp.port", CF_USEUPARAM, CHECK_PORT, 0, ",80"}, {"system.hostname", 0, SYSTEM_HOSTNAME, 0, 0}, {"system.uname", 0, SYSTEM_UNAME, 0, 0}, {"system.users.num", 0, SYSTEM_UNUM, 0, 0}, {"log", CF_USEUPARAM, ONLY_ACTIVE, 0, "logfile"}, {"eventlog", CF_USEUPARAM, ONLY_ACTIVE, 0, "system"}, {0} }; static int ONLY_ACTIVE(const char *cmd, const char *param, unsigned flags, AGENT_RESULT *result) { assert(result); init_result(result); SET_MSG_RESULT(result, strdup("Accessible only as active check!")); return SYSINFO_RET_FAIL; } int getPROC(char *file, int lineno, int fieldno, unsigned flags, AGENT_RESULT *result) { #ifdef HAVE_PROC FILE *f; char *t; char c[MAX_STRING_LEN]; int i; double value = 0; assert(result); init_result(result); if(NULL == (f = fopen(file,"r"))) { return SYSINFO_RET_FAIL; } for(i=1; i<=lineno; i++) { if(NULL == fgets(c,MAX_STRING_LEN,f)) { zbx_fclose(f); return SYSINFO_RET_FAIL; } } t=(char *)strtok(c," "); for(i=2; i<=fieldno; i++) { t=(char *)strtok(NULL," "); } zbx_fclose(f); sscanf(t, "%lf", &value); SET_DBL_RESULT(result, value); return SYSINFO_RET_OK; #else return SYSINFO_RET_FAIL; #endif /* HAVE_PROC */ } static int AGENT_PING(const char *cmd, const char *param, unsigned flags, AGENT_RESULT *result) { assert(result); init_result(result); SET_UI64_RESULT(result, 1); return SYSINFO_RET_OK; } static int AGENT_VERSION(const char *cmd, const char *param, unsigned flags, AGENT_RESULT *result) { static char version[]=ZABBIX_VERSION; assert(result); init_result(result); SET_STR_RESULT(result, strdup(version)); return SYSINFO_RET_OK; } int EXECUTE_STR(const char *cmd, const char *param, unsigned flags, AGENT_RESULT *result) { #if defined(_WINDOWS) STARTUPINFO si = {0}; PROCESS_INFORMATION pi = {0}; SECURITY_ATTRIBUTES sa; HANDLE hWrite=NULL, hRead=NULL; #else /* not _WINDOWS */ FILE *hRead = NULL; #endif /* _WINDOWS */ int ret = SYSINFO_RET_FAIL; char stat_buf[128]; char *cmd_result=NULL; char *command=NULL; int len; assert(result); init_result(result); cmd_result = zbx_dsprintf(cmd_result,""); memset(stat_buf, 0, sizeof(stat_buf)); #if defined(_WINDOWS) /* Set the bInheritHandle flag so pipe handles are inherited */ sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = NULL; /* Create a pipe for the child process's STDOUT */ if (! CreatePipe(&hRead, &hWrite, &sa, sizeof(cmd_result))) { zabbix_log(LOG_LEVEL_DEBUG, "Unable to create pipe [%s]", strerror_from_system(GetLastError())); ret = SYSINFO_RET_FAIL; goto lbl_exit; } /* Fill in process startup info structure */ memset(&si,0,sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.dwFlags = STARTF_USESTDHANDLES; si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); si.hStdOutput = hWrite; si.hStdError = hWrite; command = zbx_dsprintf(command, "cmd /C \"%s\"", param); /* Create new process */ if (!CreateProcess(NULL,command,NULL,NULL,TRUE,0,NULL,NULL,&si,&pi)) { zabbix_log(LOG_LEVEL_DEBUG, "Unable to create process: '%s' [%s]", command, strerror_from_system(GetLastError())); ret = SYSINFO_RET_FAIL; goto lbl_exit; } CloseHandle(hWrite); hWrite = NULL; /* Read process output */ while( ReadFile(hRead, stat_buf, sizeof(stat_buf)-1, &len, NULL) && len > 0 ) { cmd_result = zbx_strdcat(cmd_result, stat_buf); memset(stat_buf, 0, sizeof(stat_buf)); } /* Don't wait child process exiting. */ /* WaitForSingleObject( pi.hProcess, INFINITE ); */ /* Terminate child process */ /* TerminateProcess(pi.hProcess, 0); */ CloseHandle(pi.hProcess); CloseHandle(pi.hThread); CloseHandle(hRead); hRead = NULL; #else /* not _WINDOWS */ command = zbx_dsprintf(command, "%s", param); if(0 == (hRead = popen(command,"r"))) { switch (errno) { case EINTR: ret = SYSINFO_RET_TIMEOUT; default: ret = SYSINFO_RET_FAIL; } goto lbl_exit; } ; /* Read process output */ while( (len = fread(stat_buf, 1, sizeof(stat_buf)-1, hRead)) > 0 ) { cmd_result = zbx_strdcat(cmd_result, stat_buf); memset(stat_buf, 0, sizeof(stat_buf)); } if(0 != ferror(hRead)) { switch (errno) { case EINTR: ret = SYSINFO_RET_TIMEOUT; default: ret = SYSINFO_RET_FAIL; } goto lbl_exit; } if(pclose(hRead) == -1) { switch (errno) { case EINTR: ret = SYSINFO_RET_TIMEOUT; default: ret = SYSINFO_RET_FAIL; } goto lbl_exit; } hRead = NULL; #endif /* _WINDOWS */ zabbix_log(LOG_LEVEL_DEBUG, "Before"); zbx_rtrim(cmd_result,"\n\r\0"); /* We got EOL only */ if(cmd_result[0] == '\0') { ret = SYSINFO_RET_FAIL; goto lbl_exit; } zabbix_log(LOG_LEVEL_DEBUG, "Run remote command [%s] Result [%d] [%.20s]...", command, strlen(cmd_result), cmd_result); SET_TEXT_RESULT(result, strdup(cmd_result)); ret = SYSINFO_RET_OK; lbl_exit: #if defined(_WINDOWS) if ( hWrite ) { CloseHandle(hWrite); hWrite = NULL; } if ( hRead) { CloseHandle(hRead); hRead = NULL; } #else /* not _WINDOWS */ if ( hRead ) { pclose(hRead); hRead = NULL; } #endif /* _WINDOWS */ zbx_free(command) zbx_free(cmd_result); return ret; } int EXECUTE_INT(const char *cmd, const char *command, unsigned flags, AGENT_RESULT *result) { int ret = SYSINFO_RET_FAIL; ret = EXECUTE_STR(cmd,command,flags,result); if(SYSINFO_RET_OK == ret) { if( NULL == GET_DBL_RESULT(result) ) { zabbix_log(LOG_LEVEL_WARNING, "Remote command [%s] result is not double", command); ret = SYSINFO_RET_FAIL; } UNSET_RESULT_EXCLUDING(result, AR_DOUBLE); } return ret; } int RUN_COMMAND(const char *cmd, const char *param, unsigned flags, AGENT_RESULT *result) { #define MAX_FLAG_LEN 10 char command[MAX_STRING_LEN]; char flag[MAX_FLAG_LEN]; #if defined (_WINDOWS) STARTUPINFO si; PROCESS_INFORMATION pi; char full_command[MAX_STRING_LEN]; #else /* not _WINDOWS */ pid_t pid; #endif assert(result); init_result(result); if(CONFIG_ENABLE_REMOTE_COMMANDS != 1) { SET_MSG_RESULT(result, strdup("ZBX_NOTSUPPORTED")); return SYSINFO_RET_FAIL; } if(num_param(param) > 2) { return SYSINFO_RET_FAIL; } if(get_param(param, 1, command, sizeof(command)) != 0) { return SYSINFO_RET_FAIL; } if(command[0] == '\0') { return SYSINFO_RET_FAIL; } zabbix_log(LOG_LEVEL_DEBUG, "Run command '%s'",command); if(get_param(param, 2, flag, sizeof(flag)) != 0) { flag[0] = '\0'; } if(flag[0] == '\0') { zbx_snprintf(flag,sizeof(flag),"wait"); } if(strcmp(flag,"wait") == 0) { return EXECUTE_STR(cmd,command,flags,result); } else if(strcmp(flag,"nowait") != 0) { return SYSINFO_RET_FAIL; } #if defined(_WINDOWS) zbx_snprintf(full_command, sizeof(full_command), "cmd /C \"%s\"", command); GetStartupInfo(&si); zabbix_log(LOG_LEVEL_DEBUG, "Execute command '%s'",full_command); if(!CreateProcess( NULL, /* No module name (use command line) */ full_command,/* Name of app to launch */ NULL, /* Default process security attributes */ NULL, /* Default thread security attributes */ FALSE, /* Don't inherit handles from the parent */ 0, /* Normal priority */ NULL, /* Use the same environment as the parent */ NULL, /* Launch in the current directory */ &si, /* Startup Information */ &pi)) /* Process information stored upon return */ { return SYSINFO_RET_FAIL; } #else /* not _WINDOWS */ pid = zbx_fork(); /* run new thread 1 */ switch(pid) { case -1: zabbix_log(LOG_LEVEL_WARNING, "fork failed for command '%s'",command); return SYSINFO_RET_FAIL; case 0: pid = zbx_fork(); /* run new tread 2 to replace by command */ switch(pid) { case -1: zabbix_log(LOG_LEVEL_WARNING, "fork2 failed for '%s'",command); return SYSINFO_RET_FAIL; case 0: /* * DON'T REMOVE SLEEP * sleep needed to return server result as "1" * then we can run "execl" * otherwise command print result into socket with STDOUT id */ sleep(3); /**/ /* replace thread 2 by the execution of command */ if(execl("/bin/sh", "sh", "-c", command, (char *)0)) { zabbix_log(LOG_LEVEL_WARNING, "execl failed for command '%s'",command); } /* In normal case the program will never reach this point */ exit(0); default: waitpid(pid, NULL, WNOHANG); /* NO WAIT can be used for thread 2 closing */ exit(0); /* close thread 1 and transmit thread 2 to system (solve zombie state) */ break; } default: waitpid(pid, NULL, 0); /* wait thread 1 closing */ break; } #endif /* _WINDOWS */ SET_UI64_RESULT(result, 1); return SYSINFO_RET_OK; }