/* ** 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 "cfg.h" #include "pid.h" #include "db.h" #include "log.h" #include "zlog.h" #include "zbxserver.h" #include "httpmacro.h" #include "httptest.h" #ifdef HAVE_LIBCURL static zbx_process_t zbx_process; static S_ZBX_HTTPPAGE page; /****************************************************************************** * * * Function: process_value * * * * Purpose: process new item value * * * * Parameters: key - item key * * host - host name * * value - new value of the item * * * * Return value: SUCCEED - new value sucesfully processed * * FAIL - otherwise * * * * Author: Alexei Vladishev * * * * Comments: can be done in process_data() * * * ******************************************************************************/ static int process_value(zbx_uint64_t itemid, AGENT_RESULT *value) { DB_RESULT result; DB_ROW row; DB_ITEM item; time_t now; INIT_CHECK_MEMORY(); zabbix_log( LOG_LEVEL_DEBUG, "In process_value(itemid:" ZBX_FS_UI64 ")", itemid); result = DBselect("select %s where h.status=%d and h.hostid=i.hostid and i.status=%d and i.type=%d and i.itemid=" ZBX_FS_UI64 DB_NODE, ZBX_SQL_ITEM_SELECT, HOST_STATUS_MONITORED, ITEM_STATUS_ACTIVE, ITEM_TYPE_HTTPTEST, itemid, DBnode_local("h.hostid")); row=DBfetch(result); if(!row) { DBfree_result(result); zabbix_log( LOG_LEVEL_DEBUG, "End process_value(result:FAIL)"); return FAIL; } DBget_item_from_db(&item,row); now = time(NULL); if (0 == CONFIG_DBSYNCER_FORKS) { DBbegin(); switch (zbx_process) { case ZBX_PROCESS_SERVER: process_new_value(&item, value, now); update_triggers(item.itemid); break; case ZBX_PROCESS_PROXY: proxy_process_new_value(&item, value, now); break; } DBcommit(); } else { switch (zbx_process) { case ZBX_PROCESS_SERVER: process_new_value(&item, value, now); break; case ZBX_PROCESS_PROXY: proxy_process_new_value(&item, value, now); break; } } DBfree_result(result); zabbix_log( LOG_LEVEL_DEBUG, "End process_value()"); CHECK_MEMORY("process_value", "end"); return SUCCEED; } static size_t WRITEFUNCTION2( void *ptr, size_t size, size_t nmemb, void *stream) { size_t r_size = size*nmemb; /* First piece of data */ if(page.data == NULL) { page.allocated=MAX(8096, r_size); page.offset=0; page.data=malloc(page.allocated); } zbx_snprintf_alloc(&page.data, &page.allocated, &page.offset, MAX(8096, r_size), "%s", ptr); return r_size; } static size_t HEADERFUNCTION2( void *ptr, size_t size, size_t nmemb, void *stream) { /* ZBX_LIM_PRINT("HEADERFUNCTION", size*nmemb, ptr, 300); zabbix_log(LOG_LEVEL_WARNING, "In HEADERFUNCTION"); */ return size*nmemb; } static void process_test_data(DB_HTTPTEST *httptest, S_ZBX_HTTPSTAT *stat) { DB_RESULT result; DB_ROW row; DB_HTTPTESTITEM httptestitem; AGENT_RESULT value; INIT_CHECK_MEMORY(); zabbix_log(LOG_LEVEL_DEBUG, "In process_test_data(test:%s,time:" ZBX_FS_DBL ",last step:%d)", httptest->name, stat->test_total_time, stat->test_last_step); result = DBselect("select httptestitemid,httptestid,itemid,type from httptestitem where httptestid=" ZBX_FS_UI64, httptest->httptestid); while((row=DBfetch(result))) { ZBX_STR2UINT64(httptestitem.httptestitemid, row[0]); ZBX_STR2UINT64(httptestitem.httptestid, row[1]); ZBX_STR2UINT64(httptestitem.itemid, row[2]); httptestitem.type=atoi(row[3]); init_result(&value); switch (httptestitem.type) { case ZBX_HTTPITEM_TYPE_TIME: SET_DBL_RESULT(&value, stat->test_total_time); process_value(httptestitem.itemid,&value); break; case ZBX_HTTPITEM_TYPE_LASTSTEP: SET_UI64_RESULT(&value, stat->test_last_step); process_value(httptestitem.itemid,&value); break; case ZBX_HTTPITEM_TYPE_SPEED: SET_UI64_RESULT(&value, stat->speed_download); process_value(httptestitem.itemid, &value); break; default: break; } free_result(&value); } DBfree_result(result); zabbix_log(LOG_LEVEL_DEBUG, "End process_test_data()"); CHECK_MEMORY("process_test_data", "end"); } static void process_step_data(DB_HTTPTEST *httptest, DB_HTTPSTEP *httpstep, S_ZBX_HTTPSTAT *stat) { DB_RESULT result; DB_ROW row; DB_HTTPSTEPITEM httpstepitem; AGENT_RESULT value; INIT_CHECK_MEMORY(); zabbix_log(LOG_LEVEL_DEBUG, "In process_step_data(step:%s,url:%s,rsp:%d,time:" ZBX_FS_DBL ",speed:" ZBX_FS_DBL ")", httpstep->name, httpstep->url, stat->rspcode, stat->total_time, stat->speed_download); result = DBselect("select httpstepitemid,httpstepid,itemid,type from httpstepitem where httpstepid=" ZBX_FS_UI64, httpstep->httpstepid); while((row=DBfetch(result))) { ZBX_STR2UINT64(httpstepitem.httpstepitemid, row[0]); ZBX_STR2UINT64(httpstepitem.httpstepid, row[1]); ZBX_STR2UINT64(httpstepitem.itemid, row[2]); httpstepitem.type=atoi(row[3]); init_result(&value); switch (httpstepitem.type) { case ZBX_HTTPITEM_TYPE_RSPCODE: SET_UI64_RESULT(&value, stat->rspcode); process_value(httpstepitem.itemid,&value); break; case ZBX_HTTPITEM_TYPE_TIME: SET_DBL_RESULT(&value, stat->total_time); process_value(httpstepitem.itemid,&value); break; case ZBX_HTTPITEM_TYPE_SPEED: SET_DBL_RESULT(&value, stat->speed_download); process_value(httpstepitem.itemid,&value); break; default: break; } free_result(&value); } DBfree_result(result); zabbix_log(LOG_LEVEL_DEBUG, "End process_step_data()"); CHECK_MEMORY("process_step_data", "end"); } /****************************************************************************** * * * Function: process_httptest * * * * Purpose: process single scenario of http test * * * * Parameters: httptestid - ID of http test * * * * Return value: * * * * Author: Alexei Vladishev * * * * Comments: SUCCEED or FAIL * * * ******************************************************************************/ static void process_httptest(DB_HTTPTEST *httptest) { DB_RESULT result; DB_ROW row; DB_HTTPSTEP httpstep; int err; char *err_str = NULL, *esc_err_str = NULL; int now; int lastfailedstep; S_ZBX_HTTPSTAT stat; double speed_download = 0; int speed_download_num = 0; CURL *easyhandle = NULL; INIT_CHECK_MEMORY(); zabbix_log(LOG_LEVEL_DEBUG, "In process_httptest(httptestid:" ZBX_FS_UI64 ",name:%s)", httptest->httptestid, httptest->name); now = time(NULL); DBexecute("update httptest set lastcheck=%d where httptestid=" ZBX_FS_UI64, now, httptest->httptestid); easyhandle = curl_easy_init(); if(easyhandle == NULL) { zabbix_log(LOG_LEVEL_ERR, "Cannot init CURL"); return; } if(CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_COOKIEFILE, ""))) { zabbix_log(LOG_LEVEL_ERR, "Cannot set CURLOPT_COOKIEFILE [%s]", curl_easy_strerror(err)); (void)curl_easy_cleanup(easyhandle); return; } if(CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_USERAGENT, httptest->agent))) { zabbix_log(LOG_LEVEL_ERR, "Cannot set CURLOPT_USERAGENT [%s]", curl_easy_strerror(err)); (void)curl_easy_cleanup(easyhandle); return; } if(CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_FOLLOWLOCATION, 1))) { zabbix_log(LOG_LEVEL_ERR, "Cannot set CURLOPT_FOLLOWLOCATION [%s]", curl_easy_strerror(err)); (void)curl_easy_cleanup(easyhandle); return; } if(CURLE_OK != (err = curl_easy_setopt(easyhandle,CURLOPT_WRITEFUNCTION ,WRITEFUNCTION2))) { zabbix_log(LOG_LEVEL_ERR, "Cannot set CURLOPT_WRITEFUNCTION [%s]", curl_easy_strerror(err)); (void)curl_easy_cleanup(easyhandle); return; } if(CURLE_OK != (err = curl_easy_setopt(easyhandle,CURLOPT_HEADERFUNCTION ,HEADERFUNCTION2))) { zabbix_log(LOG_LEVEL_ERR, "Cannot set CURLOPT_WRITEFUNCTION [%s]", curl_easy_strerror(err)); (void)curl_easy_cleanup(easyhandle); return; } /* Process self-signed certificates. Do not verify certificate. */ if(CURLE_OK != (err = curl_easy_setopt(easyhandle,CURLOPT_SSL_VERIFYPEER , 0))) { zabbix_log(LOG_LEVEL_ERR, "Cannot set CURLOPT_SSL_VERIFYPEER [%s]", curl_easy_strerror(err)); (void)curl_easy_cleanup(easyhandle); return; } /* Process certs whose hostnames do not match the queried hostname. */ if(CURLE_OK != (err = curl_easy_setopt(easyhandle,CURLOPT_SSL_VERIFYHOST , 0))) { zabbix_log(LOG_LEVEL_ERR, "Cannot set CURLOPT_SSL_VERIFYHOST [%s]", curl_easy_strerror(err)); (void)curl_easy_cleanup(easyhandle); return; } lastfailedstep=0; httptest->time = 0; result = DBselect("select httpstepid,httptestid,no,name,url,timeout,posts,required,status_codes from httpstep where httptestid=" ZBX_FS_UI64 " order by no", httptest->httptestid); now=time(NULL); while((row=DBfetch(result)) && !err_str) { /* NOTE: do not use break or return for this block! * process_step_data calling required! */ ZBX_STR2UINT64(httpstep.httpstepid, row[0]); ZBX_STR2UINT64(httpstep.httptestid, row[1]); httpstep.no=atoi(row[2]); httpstep.name=row[3]; strscpy(httpstep.url,row[4]); httpstep.timeout=atoi(row[5]); strscpy(httpstep.posts,row[6]); strscpy(httpstep.required,row[7]); strscpy(httpstep.status_codes,row[8]); DBexecute("update httptest set curstep=%d,curstate=%d where httptestid=" ZBX_FS_UI64, httpstep.no, HTTPTEST_STATE_BUSY, httptest->httptestid); memset(&stat,0,sizeof(stat)); /* Substitute macros */ http_substitute_macros(httptest,httpstep.url, sizeof(httpstep.url)); http_substitute_macros(httptest,httpstep.posts, sizeof(httpstep.posts)); /* zabbix_log(LOG_LEVEL_WARNING, "POSTS [%s]", httpstep.posts); */ if(httpstep.posts[0] != 0) { zabbix_log(LOG_LEVEL_DEBUG, "WEBMonitor: use post [%s]", httpstep.posts); if(CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_POSTFIELDS, httpstep.posts))) { zabbix_log(LOG_LEVEL_ERR, "Cannot set POST vars [%s]", curl_easy_strerror(err)); err_str = strdup(curl_easy_strerror(err)); lastfailedstep = httpstep.no; } } if( !err_str ) { zabbix_log(LOG_LEVEL_DEBUG, "WEBMonitor: Go to URL [%s]", httpstep.url); if(CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_URL, httpstep.url))) { zabbix_log(LOG_LEVEL_ERR, "Cannot set URL [%s]", curl_easy_strerror(err)); err_str = strdup(curl_easy_strerror(err)); lastfailedstep = httpstep.no; } } if( !err_str ) { if(CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_TIMEOUT, httpstep.timeout))) { zabbix_log(LOG_LEVEL_ERR, "Cannot set TIMEOUT [%s]", curl_easy_strerror(err)); err_str = strdup(curl_easy_strerror(err)); lastfailedstep = httpstep.no; } } if( !err_str ) { if(CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_CONNECTTIMEOUT, httpstep.timeout))) { zabbix_log(LOG_LEVEL_ERR, "Cannot set CONNECTTIMEOUT [%s]", curl_easy_strerror(err)); err_str = strdup(curl_easy_strerror(err)); lastfailedstep = httpstep.no; } } if( !err_str ) { memset(&page, 0, sizeof(page)); if(CURLE_OK != (err = curl_easy_perform(easyhandle))) { zabbix_log(LOG_LEVEL_ERR, "Error doing curl_easy_perform [%s]", curl_easy_strerror(err)); err_str = strdup(curl_easy_strerror(err)); lastfailedstep = httpstep.no; } else { if(httpstep.required[0]!='\0' && zbx_regexp_match(page.data,httpstep.required,NULL) == NULL) { zabbix_log(LOG_LEVEL_DEBUG, "Page didn't match [%s]", httpstep.required); err_str = strdup("Page didn't match"); lastfailedstep = httpstep.no; } } free(page.data); if( !err_str ) { if(CURLE_OK != (err = curl_easy_getinfo(easyhandle,CURLINFO_RESPONSE_CODE ,&stat.rspcode))) { zabbix_log(LOG_LEVEL_ERR, "Error getting CURLINFO_RESPONSE_CODE [%s]", curl_easy_strerror(err)); err_str = strdup(curl_easy_strerror(err)); lastfailedstep = httpstep.no; } else if(httpstep.status_codes[0]!='\0' && (int_in_list(httpstep.status_codes,stat.rspcode) == FAIL)) { zabbix_log(LOG_LEVEL_DEBUG, "Status code didn't match [%s]", httpstep.status_codes); err_str = strdup("Status code didn't match"); lastfailedstep = httpstep.no; } } if( !err_str && CURLE_OK != (err = curl_easy_getinfo(easyhandle,CURLINFO_TOTAL_TIME ,&stat.total_time))) { zabbix_log(LOG_LEVEL_ERR, "Error getting CURLINFO_TOTAL_TIME [%s]", curl_easy_strerror(err)); err_str = strdup(curl_easy_strerror(err)); lastfailedstep = httpstep.no; } if( !err_str && CURLE_OK != (err = curl_easy_getinfo(easyhandle,CURLINFO_SPEED_DOWNLOAD ,&stat.speed_download))) { zabbix_log(LOG_LEVEL_ERR, "Error getting CURLINFO_SPEED_DOWNLOAD [%s]", curl_easy_strerror(err)); err_str = strdup(curl_easy_strerror(err)); lastfailedstep = httpstep.no; } else { speed_download += stat.speed_download; speed_download_num++; } } httptest->time+=stat.total_time; process_step_data(httptest, &httpstep, &stat); } DBfree_result(result); esc_err_str = DBdyn_escape_string(err_str); zbx_free(err_str); (void)curl_easy_cleanup(easyhandle); DBexecute("update httptest set curstep=0,curstate=%d,lastcheck=%d,nextcheck=%d+delay,lastfailedstep=%d," "time=" ZBX_FS_DBL ",error='%s' where httptestid=" ZBX_FS_UI64, HTTPTEST_STATE_IDLE, now, now, lastfailedstep, httptest->time, esc_err_str, httptest->httptestid); zbx_free(esc_err_str); stat.test_total_time = httptest->time; stat.test_last_step = lastfailedstep; stat.speed_download = speed_download_num ? speed_download / speed_download_num : 0; process_test_data(httptest, &stat); zabbix_log(LOG_LEVEL_DEBUG, "End process_httptest(total time:" ZBX_FS_DBL ")", httptest->time); CHECK_MEMORY("process_httptest", "end"); } /****************************************************************************** * * * Function: process_httptests * * * * Purpose: process httptests * * * * Parameters: now - current timestamp * * * * Return value: * * * * Author: Alexei Vladishev * * * * Comments: always SUCCEED * * * ******************************************************************************/ void process_httptests(zbx_process_t p, int now) { DB_RESULT result; DB_ROW row; DB_HTTPTEST httptest; INIT_CHECK_MEMORY(); zabbix_log(LOG_LEVEL_DEBUG, "In process_httptests()"); zbx_process = p; result = DBselect("select httptestid,name,applicationid,nextcheck,status,delay,macros,agent" " from httptest where status=%d and nextcheck<=%d and " ZBX_SQL_MOD(httptestid,%d) "=%d" DB_NODE, HTTPTEST_STATUS_MONITORED, now, CONFIG_HTTPPOLLER_FORKS, httppoller_num-1, DBnode_local("httptestid")); while((row=DBfetch(result))) { ZBX_STR2UINT64(httptest.httptestid, row[0]); httptest.name=row[1]; ZBX_STR2UINT64(httptest.applicationid, row[2]); httptest.nextcheck=atoi(row[3]); httptest.status=atoi(row[4]); httptest.delay=atoi(row[5]); httptest.macros=row[6]; httptest.agent=row[7]; process_httptest(&httptest); } DBfree_result(result); zabbix_log(LOG_LEVEL_DEBUG, "End process_httptests()"); CHECK_MEMORY("process_httptests", "end"); } #endif /* HAVE_LIBCURL */