diff --git a/create/src/schema.tmpl b/create/src/schema.tmpl
index de5dfeb..f7c8772 100644
--- a/create/src/schema.tmpl
+++ b/create/src/schema.tmpl
@@ -1127,7 +1127,8 @@ FIELD		|nextcheck	|t_time		|'0'	|NOT NULL	|0
 FIELD		|esc_step	|t_integer	|'0'	|NOT NULL	|0
 FIELD		|status		|t_integer	|'0'	|NOT NULL	|0
 FIELD		|itemid		|t_id		|	|NULL		|0			|-|items
-UNIQUE		|1		|actionid,triggerid,itemid,escalationid
+FIELD		|serviceid	|t_id		|	|NULL		|0			|-|services
+UNIQUE		|1		|actionid,triggerid,itemid,serviceid,escalationid
 
 TABLE|globalvars|globalvarid|0
 FIELD		|globalvarid	|t_id		|	|NOT NULL	|0
@@ -1303,4 +1304,4 @@ INDEX		|2		|templateid
 TABLE|dbversion||
 FIELD		|mandatory	|t_integer	|'0'	|NOT NULL	|
 FIELD		|optional	|t_integer	|'0'	|NOT NULL	|
-ROW		|2020000	|2020000
+ROW		|2020002	|2020002
diff --git a/frontends/php/actionconf.php b/frontends/php/actionconf.php
index 6d43739..16135a7 100644
--- a/frontends/php/actionconf.php
+++ b/frontends/php/actionconf.php
@@ -36,7 +36,7 @@ $fields = array(
 	'actionid' =>			array(T_ZBX_INT, O_OPT, P_SYS,	DB_ID,		'isset({form})&&{form}=="update"'),
 	'name' =>				array(T_ZBX_STR, O_OPT, null,	NOT_EMPTY,	'isset({save})', _('Name')),
 	'eventsource' =>		array(T_ZBX_INT, O_OPT, null,
-		IN(array(EVENT_SOURCE_TRIGGERS, EVENT_SOURCE_DISCOVERY, EVENT_SOURCE_AUTO_REGISTRATION, EVENT_SOURCE_INTERNAL)),
+		IN(array(EVENT_SOURCE_TRIGGERS, EVENT_SOURCE_DISCOVERY, EVENT_SOURCE_AUTO_REGISTRATION, EVENT_SOURCE_INTERNAL, EVENT_SOURCE_SERVICES)),
 		null
 	),
 	'evaltype' =>			array(T_ZBX_INT, O_OPT, null,
@@ -423,6 +423,12 @@ if (hasRequest('form')) {
 				$data['action']['def_shortdata'] = get_request('def_shortdata', ACTION_DEFAULT_SUBJ_AUTOREG);
 				$data['action']['def_longdata'] = get_request('def_longdata', ACTION_DEFAULT_MSG_AUTOREG);
 			}
+			elseif ($data['eventsource'] == EVENT_SOURCE_SERVICES) {
+				$data['action']['def_shortdata'] = get_request('def_shortdata', ACTION_DEFAULT_SUBJ_SERVICE);
+				$data['action']['def_longdata'] = get_request('def_longdata', ACTION_DEFAULT_MSG_SERVICE);
+				$data['action']['r_shortdata'] = get_request('r_shortdata', ACTION_DEFAULT_SUBJ_SERVICE);
+				$data['action']['r_longdata'] = get_request('r_longdata', ACTION_DEFAULT_MSG_SERVICE);
+			}
 			else {
 				$data['action']['def_shortdata'] = get_request('def_shortdata');
 				$data['action']['def_longdata'] = get_request('def_longdata');
diff --git a/frontends/php/api/classes/CAction.php b/frontends/php/api/classes/CAction.php
index bf23c18..69482d6 100644
--- a/frontends/php/api/classes/CAction.php
+++ b/frontends/php/api/classes/CAction.php
@@ -1493,6 +1493,7 @@ class CAction extends CZBXAPI {
 		$discoveryRuleIdsAll = array();
 		$proxyIdsAll = array();
 		$proxyidsAll = array();
+		$serviceIdsAll = array();
 
 		// build validators
 		$timePeriodValidator = new CTimePeriodValidator();
@@ -1679,6 +1680,13 @@ class CAction extends CZBXAPI {
 						}
 						break;
 
+					case CONDITION_TYPE_SERVICE:
+						if (!$condition['value']) {
+							self::exception(ZBX_API_ERROR_PARAMETERS, _('Empty action condition.'));
+						}
+						$serviceIdsAll[$condition['value']] = $condition['value'];
+						break;
+
 					default:
 						self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action condition type.'));
 						break;
@@ -1707,6 +1715,9 @@ class CAction extends CZBXAPI {
 		if (!API::Proxy()->isWritable($proxyidsAll)) {
 			self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action condition proxy. Proxy does not exist or you have no access to it.'));
 		}
+		if (!API::Service()->isWritable($serviceIdsAll)) {
+			self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action condition service. IT service does not exist or you have no access to it.'));
+		}
 
 		return true;
 	}
diff --git a/frontends/php/include/actions.inc.php b/frontends/php/include/actions.inc.php
index 5ffb5a1..7cb37eb 100644
--- a/frontends/php/include/actions.inc.php
+++ b/frontends/php/include/actions.inc.php
@@ -94,6 +94,8 @@ function condition_type2str($conditionType) {
 			return _('Event type');
 		case CONDITION_TYPE_HOST_METADATA:
 			return _('Host metadata');
+		case CONDITION_TYPE_SERVICE:
+			return _('IT service');
 		default:
 			return _('Unknown');
 	}
@@ -261,6 +263,21 @@ function condition_value2str($conditiontype, $value) {
 		case CONDITION_TYPE_EVENT_TYPE:
 			$str_val = eventType($value);
 			break;
+		case CONDITION_TYPE_SERVICE:
+			$srvs = API::Service()->get(array(
+				'serviceids' => $value,
+				'output' => array('name'),
+				'limit' => 1
+			));
+
+			if ($srvs) {
+				$srv = reset($srvs);
+				$str_val = $srv['name'];
+			}
+			else {
+				return _('Unknown');
+			}
+			break;
 		default:
 			return _('Unknown');
 	}
@@ -570,6 +587,11 @@ function get_conditions_by_eventsource($eventsource) {
 		CONDITION_TYPE_TEMPLATE,
 		CONDITION_TYPE_HOST
 	);
+	$conditions[EVENT_SOURCE_SERVICES] = array(
+		CONDITION_TYPE_SERVICE,
+		CONDITION_TYPE_TIME_PERIOD,
+		CONDITION_TYPE_MAINTENANCE
+	);
 
 	if (ZBX_DISTRIBUTED) {
 		array_push($conditions[EVENT_SOURCE_TRIGGERS], CONDITION_TYPE_NODE);
@@ -622,6 +644,10 @@ function get_operations_by_eventsource($eventsource) {
 	$operations[EVENT_SOURCE_INTERNAL] = array(
 		OPERATION_TYPE_MESSAGE
 	);
+	$operations[EVENT_SOURCE_SERVICES] = array(
+		OPERATION_TYPE_MESSAGE,
+		OPERATION_TYPE_COMMAND
+	);
 
 	if (isset($operations[$eventsource])) {
 		return $operations[$eventsource];
@@ -785,6 +811,10 @@ function get_operators_by_conditiontype($conditiontype) {
 		CONDITION_OPERATOR_LIKE,
 		CONDITION_OPERATOR_NOT_LIKE
 	);
+	$operators[CONDITION_TYPE_SERVICE] = array(
+		CONDITION_OPERATOR_EQUAL,
+		CONDITION_OPERATOR_NOT_EQUAL
+	);
 
 	if (isset($operators[$conditiontype])) {
 		return $operators[$conditiontype];
diff --git a/frontends/php/include/defines.inc.php b/frontends/php/include/defines.inc.php
index 36d9a8f..7023d0d 100644
--- a/frontends/php/include/defines.inc.php
+++ b/frontends/php/include/defines.inc.php
@@ -20,7 +20,7 @@
 
 define('ZABBIX_VERSION',     '2.2.3rc1');
 define('ZABBIX_API_VERSION', '2.2.3');
-define('ZABBIX_DB_VERSION',	 2020000);
+define('ZABBIX_DB_VERSION',	 2020002);
 
 define('ZABBIX_COPYRIGHT_FROM', '2001');
 define('ZABBIX_COPYRIGHT_TO',   '2014');
@@ -208,6 +208,7 @@ define('CONDITION_TYPE_DOBJECT',			21);
 define('CONDITION_TYPE_HOST_NAME',			22);
 define('CONDITION_TYPE_EVENT_TYPE',			23);
 define('CONDITION_TYPE_HOST_METADATA',		24);
+define('CONDITION_TYPE_SERVICE',		25);
 
 define('CONDITION_OPERATOR_EQUAL',		0);
 define('CONDITION_OPERATOR_NOT_EQUAL',	1);
@@ -453,6 +454,7 @@ define('EZ_TEXTING_LIMIT_CANADA',	1);
 define('ACTION_DEFAULT_SUBJ_TRIGGER', '{TRIGGER.STATUS}: {TRIGGER.NAME}');
 define('ACTION_DEFAULT_SUBJ_AUTOREG', 'Auto registration: {HOST.HOST}');
 define('ACTION_DEFAULT_SUBJ_DISCOVERY', 'Discovery: {DISCOVERY.DEVICE.STATUS} {DISCOVERY.DEVICE.IPADDRESS}');
+define('ACTION_DEFAULT_SUBJ_SERVICE', '{SERVICE.STATUS}: {SERVICE.NAME}');
 
 define('ACTION_DEFAULT_MSG_TRIGGER', "Trigger: {TRIGGER.NAME}\nTrigger status: {TRIGGER.STATUS}\n".
 		"Trigger severity: {TRIGGER.SEVERITY}\nTrigger URL: {TRIGGER.URL}\n\nItem values:\n\n".
@@ -468,6 +470,7 @@ define('ACTION_DEFAULT_MSG_DISCOVERY', "Discovery rule: {DISCOVERY.RULE.NAME}\n\
 		"Device service port: {DISCOVERY.SERVICE.PORT}\nDevice service status: {DISCOVERY.SERVICE.STATUS}\n".
 		"Device service uptime: {DISCOVERY.SERVICE.UPTIME}"
 );
+define('ACTION_DEFAULT_MSG_SERVICE', "Service: {SERVICE.NAME}\nService status: {SERVICE.STATUS}\n");
 
 define('ACTION_STATUS_ENABLED',		0);
 define('ACTION_STATUS_DISABLED',	1);
@@ -647,6 +650,7 @@ define('EVENT_SOURCE_TRIGGERS',				0);
 define('EVENT_SOURCE_DISCOVERY',			1);
 define('EVENT_SOURCE_AUTO_REGISTRATION',	2);
 define('EVENT_SOURCE_INTERNAL', 			3);
+define('EVENT_SOURCE_SERVICES',				4);
 
 define('EVENT_OBJECT_TRIGGER',			0);
 define('EVENT_OBJECT_DHOST',			1);
diff --git a/frontends/php/include/views/configuration.action.edit.php b/frontends/php/include/views/configuration.action.edit.php
index 158c07e..fd30151 100644
--- a/frontends/php/include/views/configuration.action.edit.php
+++ b/frontends/php/include/views/configuration.action.edit.php
@@ -47,7 +47,7 @@ $actionFormList->addRow(_('Name'), $nameTextBox);
 $actionFormList->addRow(_('Default subject'), new CTextBox('def_shortdata', $this->data['action']['def_shortdata'], ZBX_TEXTBOX_STANDARD_SIZE));
 $actionFormList->addRow(_('Default message'), new CTextArea('def_longdata', $this->data['action']['def_longdata']));
 
-if ($this->data['eventsource'] == EVENT_SOURCE_TRIGGERS || $this->data['eventsource'] == EVENT_SOURCE_INTERNAL) {
+if ($this->data['eventsource'] == EVENT_SOURCE_TRIGGERS || $this->data['eventsource'] == EVENT_SOURCE_INTERNAL || $this->data['eventsource'] == EVENT_SOURCE_SERVICES) {
 	$actionFormList->addRow(_('Recovery message'), new CCheckBox('recovery_msg', $this->data['action']['recovery_msg'], 'javascript: submit();', 1));
 	if ($this->data['action']['recovery_msg']) {
 		$actionFormList->addRow(_('Recovery subject'), new CTextBox('r_shortdata', $this->data['action']['r_shortdata'], ZBX_TEXTBOX_STANDARD_SIZE));
@@ -308,6 +308,17 @@ switch ($this->data['new_condition']['conditiontype']) {
 		$condition = new CTextBox('new_condition[value]', '', ZBX_TEXTBOX_STANDARD_SIZE);
 		break;
 
+	case CONDITION_TYPE_SERVICE:
+		$condition = new CMultiSelect(array(
+			'name' => 'new_condition[value][]',
+			'objectName' => 'services',
+			'objectOptions' => array(
+				'editable' => true
+			),
+			'defaultValue' => 0
+		));
+		break;
+
 	default:
 		$condition = null;
 }
@@ -323,7 +334,7 @@ $conditionFormList->addRow(_('New condition'), new CDiv($conditionTable, 'object
  */
 $operationFormList = new CFormList('operationlist');
 
-if ($this->data['eventsource'] == EVENT_SOURCE_TRIGGERS || $this->data['eventsource'] == EVENT_SOURCE_INTERNAL) {
+if ($this->data['eventsource'] == EVENT_SOURCE_TRIGGERS || $this->data['eventsource'] == EVENT_SOURCE_INTERNAL || $this->data['eventsource'] == EVENT_SOURCE_SERVICES) {
 	$operationFormList->addRow(_('Default operation step duration'), array(
 		new CNumericBox('esc_period', $this->data['action']['esc_period'], 6, 'no'),
 		' ('._('minimum 60 seconds').')')
@@ -333,7 +344,7 @@ if ($this->data['eventsource'] == EVENT_SOURCE_TRIGGERS || $this->data['eventsou
 // create operation table
 $operationsTable = new CTable(_('No operations defined.'), 'formElementTable');
 $operationsTable->attr('style', 'min-width: 600px;');
-if ($this->data['eventsource'] == EVENT_SOURCE_TRIGGERS || $this->data['eventsource'] == EVENT_SOURCE_INTERNAL) {
+if ($this->data['eventsource'] == EVENT_SOURCE_TRIGGERS || $this->data['eventsource'] == EVENT_SOURCE_INTERNAL || $this->data['eventsource'] == EVENT_SOURCE_SERVICES) {
 	$operationsTable->setHeader(array(_('Steps'), _('Details'), _('Start in'), _('Duration (sec)'), _('Action')));
 	$delay = count_operations_delay($this->data['action']['operations'], $this->data['action']['esc_period']);
 }
@@ -355,7 +366,7 @@ foreach ($this->data['action']['operations'] as $operationid => $operation) {
 	$details = new CSpan(get_operation_descr(SHORT_DESCRIPTION, $operation));
 	$details->setHint(get_operation_descr(LONG_DESCRIPTION, $operation));
 
-	if ($this->data['eventsource'] == EVENT_SOURCE_TRIGGERS || $this->data['eventsource'] == EVENT_SOURCE_INTERNAL) {
+	if ($this->data['eventsource'] == EVENT_SOURCE_TRIGGERS || $this->data['eventsource'] == EVENT_SOURCE_INTERNAL || $this->data['eventsource'] == EVENT_SOURCE_SERVICES) {
 		$esc_steps_txt = null;
 		$esc_period_txt = null;
 		$esc_delay_txt = null;
@@ -431,7 +442,7 @@ if (!empty($this->data['new_operation'])) {
 		$newOperationsTable->addItem(new CVar('new_operation[operationid]', $this->data['new_operation']['operationid']));
 	}
 
-	if ($this->data['eventsource'] == EVENT_SOURCE_TRIGGERS || $this->data['eventsource'] == EVENT_SOURCE_INTERNAL) {
+	if ($this->data['eventsource'] == EVENT_SOURCE_TRIGGERS || $this->data['eventsource'] == EVENT_SOURCE_INTERNAL || $this->data['eventsource'] == EVENT_SOURCE_SERVICES) {
 		$stepFrom = new CNumericBox('new_operation[esc_step_from]', $this->data['new_operation']['esc_step_from'], 5);
 		$stepFrom->attr('size', 6);
 		$stepFrom->addAction(
@@ -505,6 +516,10 @@ if (!empty($this->data['new_operation'])) {
 					$this->data['new_operation']['opmessage']['subject'] = ACTION_DEFAULT_SUBJ_AUTOREG;
 					$this->data['new_operation']['opmessage']['message'] = ACTION_DEFAULT_MSG_AUTOREG;
 				}
+				elseif ($this->data['eventsource'] == EVENT_SOURCE_SERVICES) {
+					$this->data['new_operation']['opmessage']['subject'] = ACTION_DEFAULT_SUBJ_SERVICE;
+					$this->data['new_operation']['opmessage']['message'] = ACTION_DEFAULT_MSG_SERVICE;
+				}
 				else {
 					$this->data['new_operation']['opmessage']['subject'] = '';
 					$this->data['new_operation']['opmessage']['message'] = '';
diff --git a/frontends/php/include/views/configuration.action.list.php b/frontends/php/include/views/configuration.action.list.php
index 4ce5559..7c95d41 100644
--- a/frontends/php/include/views/configuration.action.list.php
+++ b/frontends/php/include/views/configuration.action.list.php
@@ -34,6 +34,7 @@ $sourceComboBox->addItem(EVENT_SOURCE_TRIGGERS, _('Triggers'));
 $sourceComboBox->addItem(EVENT_SOURCE_DISCOVERY, _('Discovery'));
 $sourceComboBox->addItem(EVENT_SOURCE_AUTO_REGISTRATION, _('Auto registration'));
 $sourceComboBox->addItem(EVENT_SOURCE_INTERNAL, _x('Internal', 'event source'));
+$sourceComboBox->addItem(EVENT_SOURCE_SERVICES, _('IT services'));
 $filterForm = new CForm('get');
 $filterForm->addItem(array(_('Event source'), SPACE, $sourceComboBox));
 
diff --git a/frontends/php/jsrpc.php b/frontends/php/jsrpc.php
index e37e4cf..cf44fb7 100644
--- a/frontends/php/jsrpc.php
+++ b/frontends/php/jsrpc.php
@@ -413,6 +413,34 @@ switch ($data['method']) {
 					}
 				}
 				break;
+
+			case 'services':
+				$services = API::Service()->get(array(
+					'editable' => isset($data['editable']) ? $data['editable'] : null,
+					'output' => array('name'),
+					'search' => isset($data['search']) ? array('name' => $data['search']) : null,
+					'filter' => isset($data['filter']) ? $data['filter'] : null,
+					'limit' => isset($data['limit']) ? $data['limit'] : null
+				));
+
+				if ($services) {
+
+					$sortFields[] = array('field' => 'name', 'order' => ZBX_SORT_UP);
+					CArrayHelper::sort($services, $sortFields);
+
+					if (isset($data['limit'])) {
+						$services = array_slice($services, 0, $data['limit']);
+					}
+
+					foreach ($services as $service) {
+						$result[] = array(
+							'id' => $service['serviceid'],
+							'prefix' => '',
+							'name' => $service['name']
+						);
+					}
+				}
+				break;
 		}
 		break;
 
diff --git a/frontends/php/locale/en_US/LC_MESSAGES/frontend.po b/frontends/php/locale/en_US/LC_MESSAGES/frontend.po
index f7c6985..29897bc 100644
--- a/frontends/php/locale/en_US/LC_MESSAGES/frontend.po
+++ b/frontends/php/locale/en_US/LC_MESSAGES/frontend.po
@@ -2,7 +2,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Zabbix 2.1\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-02-13 01:48+0200\n"
+"POT-Creation-Date: 2014-04-11 15:12+0400\n"
 "PO-Revision-Date: 2013-12-17 21:07+0300\n"
 "Last-Translator: richlv <richlv@nakts.net>\n"
 "Language-Team: \n"
@@ -6876,7 +6876,11 @@ msgstr "Incorrect action condition port \"%1$s\"."
 msgid "Incorrect action condition proxy. Proxy does not exist or you have no access to it."
 msgstr "Incorrect action condition proxy. Proxy does not exist or you have no access to it."
 
-#: api/classes/CAction.php:1696
+#: api/classes/CAction.php:1719
+msgid "Incorrect action condition service. IT service does not exist or you have no access to it."
+msgstr "Incorrect action condition service. IT service does not exist or you have no access to it."
+
+#: api/classes/CAction.php:1704
 msgid "Incorrect action condition template. Template does not exist or you have no access to it."
 msgstr "Incorrect action condition template. Template does not exist or you have no access to it."
 
diff --git a/frontends/php/locale/ru/LC_MESSAGES/frontend.po b/frontends/php/locale/ru/LC_MESSAGES/frontend.po
index 7f9f342..683ac8c 100644
--- a/frontends/php/locale/ru/LC_MESSAGES/frontend.po
+++ b/frontends/php/locale/ru/LC_MESSAGES/frontend.po
@@ -2,7 +2,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Zabbix 2.1\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-02-13 01:48+0200\n"
+"POT-Creation-Date: 2014-04-11 15:12+0400\n"
 "PO-Revision-Date: 2014-01-26 15:22+0300\n"
 "Last-Translator: dotneft <dotneft@gmail.com>\n"
 "Language-Team: Zabbix translation team\n"
@@ -6921,7 +6921,11 @@ msgstr "Некорректный порт \"%1$s\" в условии дейст
 msgid "Incorrect action condition proxy. Proxy does not exist or you have no access to it."
 msgstr "Некорректный прокси в условии действия. Прокси не существует или у вас нет прав доступа к нему."
 
-#: api/classes/CAction.php:1696
+#: api/classes/CAction.php:1719
+msgid "Incorrect action condition service. IT service does not exist or you have no access to it."
+msgstr "Некорректная услуга в условии действия. IT-услуга не существует или у вас нет прав доступа к ней."
+
+#: api/classes/CAction.php:1704
 msgid "Incorrect action condition template. Template does not exist or you have no access to it."
 msgstr "Некорректный шаблон в условии действия. Шаблон не существует или у вас нет прав доступа к нему."
 
diff --git a/include/common.h b/include/common.h
index 2b5c247..070327a 100644
--- a/include/common.h
+++ b/include/common.h
@@ -160,6 +160,7 @@ zbx_item_authtype_t;
 #define EVENT_SOURCE_DISCOVERY		1
 #define EVENT_SOURCE_AUTO_REGISTRATION	2
 #define EVENT_SOURCE_INTERNAL		3
+#define EVENT_SOURCE_SERVICES		4
 
 /* event objects */
 #define EVENT_OBJECT_TRIGGER		0
@@ -168,6 +169,7 @@ zbx_item_authtype_t;
 #define EVENT_OBJECT_ZABBIX_ACTIVE	3
 #define EVENT_OBJECT_ITEM		4
 #define EVENT_OBJECT_LLDRULE		5
+#define EVENT_OBJECT_SERVICE		6
 
 /* acknowledged flags */
 #define EVENT_NOT_ACKNOWLEDGED		0
@@ -303,6 +305,7 @@ const char	*zbx_dservice_type_string(zbx_dservice_type_t service);
 #define CONDITION_TYPE_HOST_NAME		22
 #define CONDITION_TYPE_EVENT_TYPE		23
 #define CONDITION_TYPE_HOST_METADATA		24
+#define CONDITION_TYPE_SERVICE			25
 
 /* condition operators */
 #define CONDITION_OPERATOR_EQUAL		0
diff --git a/include/db.h b/include/db.h
index 8dee42b..36d17b5 100644
--- a/include/db.h
+++ b/include/db.h
@@ -408,6 +408,7 @@ typedef struct
 	zbx_uint64_t		actionid;
 	zbx_uint64_t		triggerid;
 	zbx_uint64_t		itemid;
+	zbx_uint64_t		serviceid;
 	zbx_uint64_t		eventid;
 	zbx_uint64_t		r_eventid;
 	int			nextcheck;
diff --git a/src/libs/zbxdbhigh/host.c b/src/libs/zbxdbhigh/host.c
index 8583635..89cf546 100644
--- a/src/libs/zbxdbhigh/host.c
+++ b/src/libs/zbxdbhigh/host.c
@@ -24,6 +24,7 @@
 #include "dbcache.h"
 #include "zbxserver.h"
 #include "mutexs.h"
+#include "events.h"
 
 #define LOCK_SERVICES	zbx_mutex_lock(&services_lock)
 #define UNLOCK_SERVICES	zbx_mutex_unlock(&services_lock)
@@ -916,6 +917,7 @@ static int	latest_service_alarm(zbx_uint64_t serviceid, int status)
 static void	DBadd_service_alarm(zbx_uint64_t serviceid, int status, int clock)
 {
 	const char	*__function_name = "DBadd_service_alarm";
+        zbx_timespec_t ts;
 
 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
 
@@ -924,6 +926,10 @@ static void	DBadd_service_alarm(zbx_uint64_t serviceid, int status, int clock)
 		DBexecute("insert into service_alarms (servicealarmid,serviceid,clock,value)"
 			" values(" ZBX_FS_UI64 "," ZBX_FS_UI64 ",%d,%d)",
 			DBget_maxid("service_alarms"), serviceid, clock, status);
+                ts.sec = clock;
+                ts.ns = 0;
+                add_event(0, EVENT_SOURCE_SERVICES, EVENT_OBJECT_SERVICE, serviceid, &ts,
+                          status, NULL, NULL, 0, 0);
 	}
 
 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
diff --git a/src/libs/zbxdbupgrade/dbupgrade.c b/src/libs/zbxdbupgrade/dbupgrade.c
index 8064c15..4d6df4d 100644
--- a/src/libs/zbxdbupgrade/dbupgrade.c
+++ b/src/libs/zbxdbupgrade/dbupgrade.c
@@ -2396,6 +2396,19 @@ static int	DBpatch_2020000(void)
 	return SUCCEED;
 }
 
+static int	DBpatch_2020002(void)
+{
+	const ZBX_FIELD	field = {"serviceid", NULL, "services", "serviceid", 0, ZBX_TYPE_ID, 0, 0};
+        int ret;
+
+        ret = DBadd_field("escalations", &field);
+
+        if (ret == SUCCEED)
+          DBadd_foreign_key("escalations", 1, &field);
+
+        return ret;
+}
+
 #define DBPATCH_START()					zbx_dbpatch_t	patches[] = {
 #define DBPATCH_ADD(version, duplicates, mandatory)	{DBpatch_##version, version, duplicates, mandatory},
 #define DBPATCH_END()					{NULL}};
@@ -2644,6 +2657,7 @@ int	DBcheck_version(void)
 	DBPATCH_ADD(2010199, 0, 1)
 	DBPATCH_ADD(2020000, 0, 1)
 	/* Patch 2020001 is reserved for ZBXNEXT-2124 */
+	DBPATCH_ADD(2020002, 0, 1)
 
 	DBPATCH_END()
 
diff --git a/src/libs/zbxserver/expression.c b/src/libs/zbxserver/expression.c
index b5e613e..0d1fc8d 100644
--- a/src/libs/zbxserver/expression.c
+++ b/src/libs/zbxserver/expression.c
@@ -2030,6 +2030,11 @@ static int	get_autoreg_value_by_event(const DB_EVENT *event, char **replace_to,
 #define MVAR_DISCOVERY_DEVICE_STATUS	"{DISCOVERY.DEVICE.STATUS}"
 #define MVAR_DISCOVERY_DEVICE_UPTIME	"{DISCOVERY.DEVICE.UPTIME}"
 
+/* IT Services */
+#define MVAR_SERVICE_ID			"{SERVICE.ID}"
+#define MVAR_SERVICE_NAME		"{SERVICE.NAME}"
+#define MVAR_SERVICE_STATUS		"{SERVICE.STATUS}"
+
 #define STR_UNKNOWN_VARIABLE		"*UNKNOWN*"
 
 static const char	*ex_macros[] =
@@ -2483,6 +2488,57 @@ static void	cache_trigger_hostids(zbx_vector_uint64_t *hostids, const char *expr
 
 /******************************************************************************
  *                                                                            *
+ * Function: DBget_service_name                                               *
+ *                                                                            *
+ * Purpose: get the name of the given IT service                              *
+ *                                                                            *
+ * Return value: upon successful completion return SUCCEED                    *
+ *               otherwise FAIL                                               *
+ *                                                                            *
+ ******************************************************************************/
+static int	DBget_service_name(zbx_uint64_t serviceid, char **replace_to)
+{
+	const char	*__function_name = "DBget_service_name";
+
+	DB_RESULT	result;
+	DB_ROW		row;
+	int		ret = FAIL;
+	char		*sql = NULL;
+	size_t		replace_to_alloc = 64, replace_to_offset = 0,
+			sql_alloc = 256, sql_offset = 0;
+
+	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
+
+	*replace_to = zbx_realloc(*replace_to, replace_to_alloc);
+	**replace_to = '\0';
+
+	sql = zbx_malloc(sql, sql_alloc);
+
+	zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
+			"select name"
+			" from services"
+			" where serviceid=" ZBX_FS_UI64,
+			serviceid);
+	zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, " order by name");
+
+	result = DBselect("%s", sql);
+
+	zbx_free(sql);
+
+	if (NULL != (row = DBfetch(result)))
+	{
+		zbx_strcpy_alloc(replace_to, &replace_to_alloc, &replace_to_offset, row[0]);
+		ret = SUCCEED;
+	}
+	DBfree_result(result);
+out:
+	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
+
+	return ret;
+}
+
+/******************************************************************************
+ *                                                                            *
  * Function: substitute_simple_macros                                         *
  *                                                                            *
  * Purpose: substitute simple macros in data string with real values          *
@@ -3321,6 +3377,25 @@ int	substitute_simple_macros(zbx_uint64_t *actionid, const DB_EVENT *event, DB_E
 					replace_to = zbx_strdup(replace_to, zbx_time2str(time(NULL)));
 				}
 			}
+                        else if (EVENT_SOURCE_SERVICES == c_event->source)
+			{
+                          if (0 == strcmp(m, MVAR_SERVICE_ID))
+                          {
+                            replace_to = zbx_dsprintf(replace_to, ZBX_FS_UI64, c_event->objectid);
+                          }
+                          else if (0 == strcmp(m, MVAR_SERVICE_NAME))
+                          {
+                            ret = DBget_service_name(c_event->objectid, &replace_to);
+                          }
+                          else if (0 == strcmp(m, MVAR_SERVICE_STATUS) || 0 == strcmp(m, MVAR_STATUS))
+                          {
+                            if (c_event->value) {
+                              DCget_trigger_severity_name(c_event->value, &replace_to);
+                            } else {
+                              replace_to = zbx_strdup(replace_to, "OK");
+                            }
+                          }
+                        }
 		}
 		else if (0 != (macro_type & (MACRO_TYPE_TRIGGER_DESCRIPTION | MACRO_TYPE_TRIGGER_COMMENTS)))
 		{
diff --git a/src/zabbix_server/actions.c b/src/zabbix_server/actions.c
index 3f6a506..91c573a 100644
--- a/src/zabbix_server/actions.c
+++ b/src/zabbix_server/actions.c
@@ -1269,6 +1269,68 @@ out:
 
 /******************************************************************************
  *                                                                            *
+ * Function: check_service_condition                                          *
+ *                                                                            *
+ * Purpose: check if event matches single condition                           *
+ *                                                                            *
+ * Parameters: event - service event to check                                 *
+ *                                  (event->source == EVENT_SOURCE_SERVICES)  *
+ *             condition - condition for matching                             *
+ *                                                                            *
+ * Return value: SUCCEED - matches, FAIL - otherwise                          *
+ *                                                                            *
+ * Author: Paul Wolneykien                                                    *
+ *                                                                            *
+ ******************************************************************************/
+static int	check_service_condition(const DB_EVENT *event, DB_CONDITION *condition)
+{
+	const char	*__function_name = "check_service_condition";
+	zbx_uint64_t	condition_value;
+	int		ret = FAIL;
+
+	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
+
+
+	if (CONDITION_TYPE_SERVICE == condition->conditiontype)
+	{
+		ZBX_STR2UINT64(condition_value, condition->value);
+
+		switch (condition->operator)
+		{
+			case CONDITION_OPERATOR_EQUAL:
+			case CONDITION_OPERATOR_NOT_EQUAL:
+				if (event->objectid == condition_value)
+				{
+					ret = SUCCEED;
+				}
+
+				if (CONDITION_OPERATOR_NOT_EQUAL == condition->operator)
+					ret = (SUCCEED == ret) ? FAIL : SUCCEED;
+				break;
+			default:
+				ret = NOTSUPPORTED;
+		}
+	}
+	else
+	{
+		zabbix_log(LOG_LEVEL_ERR, "unsupported condition type [%d] for condition id [" ZBX_FS_UI64 "]",
+				(int)condition->conditiontype, condition->conditionid);
+	}
+
+	if (NOTSUPPORTED == ret)
+	{
+		zabbix_log(LOG_LEVEL_ERR, "unsupported operator [%d] for condition id [" ZBX_FS_UI64 "]",
+				(int)condition->operator, condition->conditionid);
+		ret = FAIL;
+	}
+
+	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
+
+	return ret;
+}
+
+/******************************************************************************
+ *                                                                            *
  * Function: check_action_condition                                           *
  *                                                                            *
  * Purpose: check if event matches single condition                           *
@@ -1303,6 +1365,9 @@ int	check_action_condition(const DB_EVENT *event, DB_CONDITION *condition)
 		case EVENT_SOURCE_INTERNAL:
 			ret = check_internal_condition(event, condition);
 			break;
+		case EVENT_SOURCE_SERVICES:
+			ret = check_service_condition(event, condition);
+			break;
 		default:
 			zabbix_log(LOG_LEVEL_ERR, "unsupported event source [%d] for condition id [" ZBX_FS_UI64 "]",
 					event->source, condition->conditionid);
@@ -1528,9 +1593,9 @@ static void	execute_operations(const DB_EVENT *event, zbx_uint64_t actionid)
 static void	get_escalation_sql(char **sql, size_t *sql_alloc, size_t *sql_offset,
 		zbx_uint64_t actionid, const DB_EVENT *event, unsigned char recovery)
 {
-	zbx_uint64_t	escalationid, triggerid = 0, itemid = 0, eventid = 0, r_eventid = 0;
+	zbx_uint64_t	escalationid, triggerid = 0, itemid = 0, serviceid = 0, eventid = 0, r_eventid = 0;
 	const char	*ins_escalation_sql =
-			"insert into escalations (escalationid,actionid,status,triggerid,itemid,eventid,r_eventid)"
+			"insert into escalations (escalationid,actionid,status,triggerid,itemid,serviceid,eventid,r_eventid)"
 			" values ";
 
 	escalationid = DBget_maxid("escalations");
@@ -1544,6 +1609,9 @@ static void	get_escalation_sql(char **sql, size_t *sql_alloc, size_t *sql_offset
 		case EVENT_OBJECT_LLDRULE:
 			itemid = event->objectid;
 			break;
+		case EVENT_OBJECT_SERVICE:
+			serviceid = event->objectid;
+			break;
 	}
 
 	if (0 == recovery)
@@ -1562,9 +1630,9 @@ static void	get_escalation_sql(char **sql, size_t *sql_alloc, size_t *sql_offset
 #ifndef HAVE_MULTIROW_INSERT
 	zbx_strcpy_alloc(sql, sql_alloc, sql_offset, ins_escalation_sql);
 #endif
-	zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "(" ZBX_FS_UI64 "," ZBX_FS_UI64 ",%d,%s,%s,%s,%s)" ZBX_ROW_DL,
+	zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "(" ZBX_FS_UI64 "," ZBX_FS_UI64 ",%d,%s,%s,%s,%s,%s)" ZBX_ROW_DL,
 			escalationid, actionid, ESCALATION_STATUS_ACTIVE, DBsql_id_ins(triggerid), DBsql_id_ins(itemid),
-			DBsql_id_ins(eventid), DBsql_id_ins(r_eventid));
+			DBsql_id_ins(serviceid), DBsql_id_ins(eventid), DBsql_id_ins(r_eventid));
 }
 
 /******************************************************************************
@@ -1622,7 +1690,8 @@ void	process_actions(const DB_EVENT *events, size_t events_num)
 					execute_operations(event, actionid);
 				}
 			}
-			else if (EVENT_SOURCE_TRIGGERS == event->source || EVENT_SOURCE_INTERNAL == event->source)
+			else if (EVENT_SOURCE_TRIGGERS == event->source || EVENT_SOURCE_INTERNAL == event->source ||
+                                 EVENT_SOURCE_SERVICES == event->source)
 			{
 				/* Action conditions evaluated to false, but it could be a recovery */
 				/* event for this action. Remember this and check escalations later. */
@@ -1644,14 +1713,14 @@ void	process_actions(const DB_EVENT *events, size_t events_num)
 	{
 		char		*sql2 = NULL;
 		size_t		sql2_alloc = 0, sql2_offset = 0;
-		zbx_uint64_t	triggerid, itemid;
+		zbx_uint64_t	triggerid, itemid, serviceid;
 
 		zbx_vector_uint64_sort(&rec_actionids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
 		zbx_vector_uint64_uniq(&rec_actionids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
 
 		/* list of ongoing escalations matching actionids collected before */
 		zbx_strcpy_alloc(&sql2, &sql2_alloc, &sql2_offset,
-				"select actionid,triggerid,itemid"
+				"select actionid,triggerid,itemid,serviceid"
 				" from escalations"
 				" where eventid is not null"
 					" and");
@@ -1665,6 +1734,7 @@ void	process_actions(const DB_EVENT *events, size_t events_num)
 			ZBX_STR2UINT64(actionid, row[0]);
 			ZBX_DBROW2UINT64(triggerid, row[1]);
 			ZBX_DBROW2UINT64(itemid, row[2]);
+			ZBX_DBROW2UINT64(serviceid, row[3]);
 
 			for (i = 0; i < rec_mapping.values_num; i++)
 			{
@@ -1697,6 +1767,10 @@ void	process_actions(const DB_EVENT *events, size_t events_num)
 						}
 
 						break;
+					case EVENT_SOURCE_SERVICES:
+						if (serviceid != event->objectid)
+							continue;
+						break;
 					default:
 						continue;
 				}
diff --git a/src/zabbix_server/escalator/escalator.c b/src/zabbix_server/escalator/escalator.c
index e78fe7f..da18ee8 100644
--- a/src/zabbix_server/escalator/escalator.c
+++ b/src/zabbix_server/escalator/escalator.c
@@ -218,6 +218,51 @@ static int	get_item_permission(zbx_uint64_t userid, zbx_uint64_t itemid)
 	return perm;
 }
 
+/******************************************************************************
+ *                                                                            *
+ * Function: get_service_permission                                           *
+ *                                                                            *
+ * Purpose: Return user permissions for access to the given service           *
+ *                                                                            *
+ * Return value: PERM_DENY - if the specified trigger or user not found,      *
+ *                   or permission otherwise                                  *
+ *                                                                            *
+ ******************************************************************************/
+static int	get_service_permission(zbx_uint64_t userid, zbx_uint64_t serviceid)
+{
+	const char	*__function_name = "get_service_permission";
+	DB_RESULT	result;
+	DB_ROW		row;
+	int		perm = PERM_READ, trigger_perm;
+	zbx_uint64_t	triggerid;
+
+	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
+
+	result = DBselect(
+			"select triggerid"
+			" from services"
+			" where serviceid=" ZBX_FS_UI64,
+			serviceid);
+
+	while (NULL != (row = DBfetch(result)))
+	{
+          if (row[0]) {
+		ZBX_STR2UINT64(triggerid, row[0]);
+                if (triggerid) {
+                  perm = PERM_DENY;
+                  trigger_perm = get_trigger_permission(userid, triggerid);
+                  if (perm < trigger_perm)
+                    perm = trigger_perm;
+                }
+          }
+	}
+	DBfree_result(result);
+
+	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_permission_string(perm));
+
+	return perm;
+}
+
 static void	add_user_msg(zbx_uint64_t userid, zbx_uint64_t mediatypeid, ZBX_USER_MSG **user_msg,
 		const char *subject, const char *message)
 {
@@ -293,6 +338,10 @@ static void	add_object_msg(zbx_uint64_t actionid, zbx_uint64_t operationid, zbx_
 				if (PERM_READ > get_item_permission(userid, event->objectid))
 					continue;
 				break;
+			case EVENT_OBJECT_SERVICE:
+				if (PERM_READ > get_service_permission(userid, event->objectid))
+					continue;
+				break;
 		}
 
 		subject_dyn = zbx_strdup(NULL, subject);
@@ -1584,10 +1633,10 @@ static int	process_escalations(int now, int *nextcheck)
 	sql = zbx_malloc(sql, sql_alloc);
 
 	result = DBselect(
-			"select escalationid,actionid,triggerid,eventid,r_eventid,nextcheck,esc_step,status,itemid"
+			"select escalationid,actionid,triggerid,eventid,r_eventid,nextcheck,esc_step,status,itemid,serviceid"
 			" from escalations"
 			ZBX_SQL_NODE
-			" order by actionid,triggerid,itemid,escalationid",
+			" order by actionid,triggerid,itemid,serviceid,escalationid",
 			DBwhere_node_local("escalationid"));
 
 	*nextcheck = now + CONFIG_ESCALATOR_FREQUENCY;
@@ -1610,6 +1659,7 @@ static int	process_escalations(int now, int *nextcheck)
 			last_escalation.esc_step = atoi(row[6]);
 			last_escalation.status = atoi(row[7]);
 			ZBX_DBROW2UINT64(last_escalation.itemid, row[8]);
+			ZBX_DBROW2UINT64(last_escalation.serviceid, row[9]);
 
 			/* just delete on the next cycle */
 			if (0 != last_escalation.r_eventid)
@@ -1630,7 +1680,8 @@ static int	process_escalations(int now, int *nextcheck)
 		{
 			esc_superseded = (escalation.actionid == last_escalation.actionid &&
 					escalation.triggerid == last_escalation.triggerid &&
-					escalation.itemid == last_escalation.itemid);
+					escalation.itemid == last_escalation.itemid &&
+					escalation.serviceid == last_escalation.serviceid);
 
 			if (0 != esc_superseded)
 			{
diff --git a/src/zabbix_server/events.c b/src/zabbix_server/events.c
index 2b2ea52..fb67ca9 100644
--- a/src/zabbix_server/events.c
+++ b/src/zabbix_server/events.c
@@ -86,7 +86,7 @@ void	add_event(zbx_uint64_t eventid, unsigned char source, unsigned char object,
  * Purpose: flushes the events into a database                                *
  *                                                                            *
  ******************************************************************************/
-static void	save_events()
+static void	save_events(int from)
 {
 	char		*sql = NULL;
 	size_t		sql_alloc = 2 * ZBX_KIBIBYTE, sql_offset = 0, i;
@@ -100,7 +100,7 @@ static void	save_events()
 	zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, ins_event_sql);
 #endif
 
-	for (i = 0; i < events_num; i++)
+	for (i = from; i < events_num; i++)
 	{
 		if (0 == events[i].eventid)
 			events[i].eventid = DBget_maxid("events");
@@ -152,16 +152,18 @@ int	process_events(void)
 	const char	*__function_name = "process_events";
 
 	size_t		i;
+        size_t events_num0;
 
 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() events_num:" ZBX_FS_SIZE_T, __function_name, (zbx_fs_size_t)events_num);
 
 	if (0 != events_num)
 	{
-		save_events();
+		save_events(0);
 
 		process_actions(events, events_num);
 
-		for (i = 0; i < events_num; i++)
+                events_num0 = events_num;
+		for (i = 0; i < events_num0; i++)
 		{
 			if (EVENT_SOURCE_TRIGGERS == events[i].source)
 			{
@@ -170,6 +172,12 @@ int	process_events(void)
 			}
 		}
 
+                if (events_num0 < events_num) {
+                  /* A number of new events resulted from updating of IT services */
+                  save_events(events_num0);
+                  process_actions(events + events_num0, events_num - events_num0);
+                }
+
 		clean_events();
 	}
 
diff --git a/zabbix.spec b/zabbix.spec
index 089e265..ee4ccaa 100644
--- a/zabbix.spec
+++ b/zabbix.spec
@@ -11,7 +11,7 @@
 
 Name: zabbix
 Version: 2.2.2
-Release: alt1
+Release: alt2
 
 Packager: Alexei Takaseev <taf@altlinux.ru>
 
@@ -455,6 +455,9 @@ fi
 %doc misc/snmptrap/* migrate.sh
 
 %changelog
+* Mon Mar 24 2014 Paul Wolneykien <manowar@altlinux.org> 1:2.2.2-alt2
+- Actions for IT Services.
+
 * Thu Feb 13 2014 Alexei Takaseev <taf@altlinux.org> 1:2.2.2-alt1
 - 2.2.2 release
 
