mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-11-04 11:45:06 +00:00 
			
		
		
		
	Merge pull request #1020 from opensourcerouting/ferr
"ferr" error reporting extensions
This commit is contained in:
		
						commit
						dfd8f05f97
					
				@ -1190,13 +1190,13 @@ void isis_circuit_af_set(struct isis_circuit *circuit, bool ip_router,
 | 
			
		||||
		lsp_regenerate_schedule(circuit->area, circuit->is_type, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int isis_circuit_passive_set(struct isis_circuit *circuit, bool passive)
 | 
			
		||||
ferr_r isis_circuit_passive_set(struct isis_circuit *circuit, bool passive)
 | 
			
		||||
{
 | 
			
		||||
	if (circuit->is_passive == passive)
 | 
			
		||||
		return 0;
 | 
			
		||||
		return ferr_ok();
 | 
			
		||||
 | 
			
		||||
	if (if_is_loopback(circuit->interface) && !passive)
 | 
			
		||||
		return -1;
 | 
			
		||||
		return ferr_cfg_invalid("loopback is always passive");
 | 
			
		||||
 | 
			
		||||
	if (circuit->state != C_STATE_UP) {
 | 
			
		||||
		circuit->is_passive = passive;
 | 
			
		||||
@ -1207,30 +1207,33 @@ int isis_circuit_passive_set(struct isis_circuit *circuit, bool passive)
 | 
			
		||||
		isis_csm_state_change(ISIS_ENABLE, circuit, area);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
	return ferr_ok();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int isis_circuit_metric_set(struct isis_circuit *circuit, int level, int metric)
 | 
			
		||||
ferr_r isis_circuit_metric_set(struct isis_circuit *circuit, int level,
 | 
			
		||||
			       int metric)
 | 
			
		||||
{
 | 
			
		||||
	assert(level == IS_LEVEL_1 || level == IS_LEVEL_2);
 | 
			
		||||
	if (metric > MAX_WIDE_LINK_METRIC)
 | 
			
		||||
		return -1;
 | 
			
		||||
		return ferr_cfg_invalid("metric %d too large for wide metric",
 | 
			
		||||
					metric);
 | 
			
		||||
	if (circuit->area && circuit->area->oldmetric
 | 
			
		||||
	    && metric > MAX_NARROW_LINK_METRIC)
 | 
			
		||||
		return -1;
 | 
			
		||||
		return ferr_cfg_invalid("metric %d too large for narrow metric",
 | 
			
		||||
					metric);
 | 
			
		||||
 | 
			
		||||
	circuit->te_metric[level - 1] = metric;
 | 
			
		||||
	circuit->metric[level - 1] = metric;
 | 
			
		||||
 | 
			
		||||
	if (circuit->area)
 | 
			
		||||
		lsp_regenerate_schedule(circuit->area, level, 0);
 | 
			
		||||
	return 0;
 | 
			
		||||
	return ferr_ok();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int isis_circuit_passwd_unset(struct isis_circuit *circuit)
 | 
			
		||||
ferr_r isis_circuit_passwd_unset(struct isis_circuit *circuit)
 | 
			
		||||
{
 | 
			
		||||
	memset(&circuit->passwd, 0, sizeof(circuit->passwd));
 | 
			
		||||
	return 0;
 | 
			
		||||
	return ferr_ok();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int isis_circuit_passwd_set(struct isis_circuit *circuit,
 | 
			
		||||
@ -1239,50 +1242,50 @@ static int isis_circuit_passwd_set(struct isis_circuit *circuit,
 | 
			
		||||
	int len;
 | 
			
		||||
 | 
			
		||||
	if (!passwd)
 | 
			
		||||
		return -1;
 | 
			
		||||
		return ferr_code_bug("no circuit password given");
 | 
			
		||||
 | 
			
		||||
	len = strlen(passwd);
 | 
			
		||||
	if (len > 254)
 | 
			
		||||
		return -1;
 | 
			
		||||
		return ferr_code_bug(
 | 
			
		||||
			"circuit password too long (max 254 chars)");
 | 
			
		||||
 | 
			
		||||
	circuit->passwd.len = len;
 | 
			
		||||
	strncpy((char *)circuit->passwd.passwd, passwd, 255);
 | 
			
		||||
	circuit->passwd.type = passwd_type;
 | 
			
		||||
	return 0;
 | 
			
		||||
	return ferr_ok();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int isis_circuit_passwd_cleartext_set(struct isis_circuit *circuit,
 | 
			
		||||
				      const char *passwd)
 | 
			
		||||
ferr_r isis_circuit_passwd_cleartext_set(struct isis_circuit *circuit,
 | 
			
		||||
					 const char *passwd)
 | 
			
		||||
{
 | 
			
		||||
	return isis_circuit_passwd_set(circuit, ISIS_PASSWD_TYPE_CLEARTXT,
 | 
			
		||||
				       passwd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int isis_circuit_passwd_hmac_md5_set(struct isis_circuit *circuit,
 | 
			
		||||
				     const char *passwd)
 | 
			
		||||
ferr_r isis_circuit_passwd_hmac_md5_set(struct isis_circuit *circuit,
 | 
			
		||||
					const char *passwd)
 | 
			
		||||
{
 | 
			
		||||
	return isis_circuit_passwd_set(circuit, ISIS_PASSWD_TYPE_HMAC_MD5,
 | 
			
		||||
				       passwd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct cmd_node interface_node = {
 | 
			
		||||
	INTERFACE_NODE, "%s(config-if)# ", 1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int isis_circuit_circ_type_set(struct isis_circuit *circuit, int circ_type)
 | 
			
		||||
ferr_r isis_circuit_circ_type_set(struct isis_circuit *circuit, int circ_type)
 | 
			
		||||
{
 | 
			
		||||
	if (circuit->circ_type == circ_type)
 | 
			
		||||
		return ferr_ok();
 | 
			
		||||
 | 
			
		||||
	/* Changing the network type to/of loopback or unknown interfaces
 | 
			
		||||
	 * is not supported. */
 | 
			
		||||
	if (circ_type == CIRCUIT_T_UNKNOWN || circ_type == CIRCUIT_T_LOOPBACK
 | 
			
		||||
	    || circuit->circ_type == CIRCUIT_T_LOOPBACK) {
 | 
			
		||||
		if (circuit->circ_type != circ_type)
 | 
			
		||||
			return -1;
 | 
			
		||||
		else
 | 
			
		||||
			return 0;
 | 
			
		||||
		return ferr_cfg_invalid(
 | 
			
		||||
			"cannot change network type on unknown interface");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (circuit->circ_type == circ_type)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (circuit->state != C_STATE_UP) {
 | 
			
		||||
		circuit->circ_type = circ_type;
 | 
			
		||||
		circuit->circ_type_config = circ_type;
 | 
			
		||||
@ -1290,14 +1293,15 @@ int isis_circuit_circ_type_set(struct isis_circuit *circuit, int circ_type)
 | 
			
		||||
		struct isis_area *area = circuit->area;
 | 
			
		||||
		if (circ_type == CIRCUIT_T_BROADCAST
 | 
			
		||||
		    && !if_is_broadcast(circuit->interface))
 | 
			
		||||
			return -1;
 | 
			
		||||
			return ferr_cfg_reality(
 | 
			
		||||
				"cannot configure non-broadcast interface for broadcast operation");
 | 
			
		||||
 | 
			
		||||
		isis_csm_state_change(ISIS_DISABLE, circuit, area);
 | 
			
		||||
		circuit->circ_type = circ_type;
 | 
			
		||||
		circuit->circ_type_config = circ_type;
 | 
			
		||||
		isis_csm_state_change(ISIS_ENABLE, circuit, area);
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
	return ferr_ok();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int isis_circuit_mt_enabled_set(struct isis_circuit *circuit, uint16_t mtid,
 | 
			
		||||
 | 
			
		||||
@ -27,6 +27,7 @@
 | 
			
		||||
#include "if.h"
 | 
			
		||||
#include "qobj.h"
 | 
			
		||||
#include "prefix.h"
 | 
			
		||||
#include "ferr.h"
 | 
			
		||||
 | 
			
		||||
#include "isis_constants.h"
 | 
			
		||||
#include "isis_common.h"
 | 
			
		||||
@ -178,18 +179,18 @@ struct isis_circuit *isis_circuit_create(struct isis_area *area,
 | 
			
		||||
					 struct interface *ifp);
 | 
			
		||||
void isis_circuit_af_set(struct isis_circuit *circuit, bool ip_router,
 | 
			
		||||
			 bool ipv6_router);
 | 
			
		||||
int isis_circuit_passive_set(struct isis_circuit *circuit, bool passive);
 | 
			
		||||
ferr_r isis_circuit_passive_set(struct isis_circuit *circuit, bool passive);
 | 
			
		||||
void isis_circuit_is_type_set(struct isis_circuit *circuit, int is_type);
 | 
			
		||||
int isis_circuit_circ_type_set(struct isis_circuit *circuit, int circ_type);
 | 
			
		||||
ferr_r isis_circuit_circ_type_set (struct isis_circuit *circuit, int circ_type);
 | 
			
		||||
 | 
			
		||||
int isis_circuit_metric_set(struct isis_circuit *circuit, int level,
 | 
			
		||||
			    int metric);
 | 
			
		||||
ferr_r isis_circuit_metric_set(struct isis_circuit *circuit, int level,
 | 
			
		||||
			       int metric);
 | 
			
		||||
 | 
			
		||||
int isis_circuit_passwd_unset(struct isis_circuit *circuit);
 | 
			
		||||
int isis_circuit_passwd_cleartext_set(struct isis_circuit *circuit,
 | 
			
		||||
				      const char *passwd);
 | 
			
		||||
int isis_circuit_passwd_hmac_md5_set(struct isis_circuit *circuit,
 | 
			
		||||
				     const char *passwd);
 | 
			
		||||
ferr_r isis_circuit_passwd_unset(struct isis_circuit *circuit);
 | 
			
		||||
ferr_r isis_circuit_passwd_cleartext_set(struct isis_circuit *circuit,
 | 
			
		||||
					 const char *passwd);
 | 
			
		||||
ferr_r isis_circuit_passwd_hmac_md5_set(struct isis_circuit *circuit,
 | 
			
		||||
					const char *passwd);
 | 
			
		||||
 | 
			
		||||
int isis_circuit_mt_enabled_set(struct isis_circuit *circuit, uint16_t mtid,
 | 
			
		||||
				bool enabled);
 | 
			
		||||
 | 
			
		||||
@ -164,7 +164,8 @@ DEFUN (isis_passive,
 | 
			
		||||
	if (!circuit)
 | 
			
		||||
		return CMD_ERR_NO_MATCH;
 | 
			
		||||
 | 
			
		||||
	isis_circuit_passive_set(circuit, 1);
 | 
			
		||||
	CMD_FERR_RETURN(isis_circuit_passive_set(circuit, 1),
 | 
			
		||||
			"Cannot set passive: $ERR");
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -179,12 +180,8 @@ DEFUN (no_isis_passive,
 | 
			
		||||
	if (!circuit)
 | 
			
		||||
		return CMD_ERR_NO_MATCH;
 | 
			
		||||
 | 
			
		||||
	if (if_is_loopback(circuit->interface)) {
 | 
			
		||||
		vty_out(vty, "Can't set no passive for loopback interface\n");
 | 
			
		||||
		return CMD_WARNING_CONFIG_FAILED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	isis_circuit_passive_set(circuit, 0);
 | 
			
		||||
	CMD_FERR_RETURN(isis_circuit_passive_set(circuit, 0),
 | 
			
		||||
			"Cannot set no passive: $ERR");
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -301,7 +298,8 @@ DEFUN (isis_passwd,
 | 
			
		||||
	int idx_encryption = 2;
 | 
			
		||||
	int idx_word = 3;
 | 
			
		||||
	struct isis_circuit *circuit = isis_circuit_lookup(vty);
 | 
			
		||||
	int rv;
 | 
			
		||||
	ferr_r rv;
 | 
			
		||||
 | 
			
		||||
	if (!circuit)
 | 
			
		||||
		return CMD_ERR_NO_MATCH;
 | 
			
		||||
 | 
			
		||||
@ -311,11 +309,8 @@ DEFUN (isis_passwd,
 | 
			
		||||
	else
 | 
			
		||||
		rv = isis_circuit_passwd_cleartext_set(circuit,
 | 
			
		||||
						       argv[idx_word]->arg);
 | 
			
		||||
	if (rv) {
 | 
			
		||||
		vty_out(vty, "Too long circuit password (>254)\n");
 | 
			
		||||
		return CMD_WARNING_CONFIG_FAILED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CMD_FERR_RETURN(rv, "Failed to set circuit password: $ERR");
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -333,8 +328,8 @@ DEFUN (no_isis_passwd,
 | 
			
		||||
	if (!circuit)
 | 
			
		||||
		return CMD_ERR_NO_MATCH;
 | 
			
		||||
 | 
			
		||||
	isis_circuit_passwd_unset(circuit);
 | 
			
		||||
 | 
			
		||||
	CMD_FERR_RETURN(isis_circuit_passwd_unset(circuit),
 | 
			
		||||
			"Failed to unset circuit password: $ERR");
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -507,8 +502,10 @@ DEFUN (isis_metric,
 | 
			
		||||
		return CMD_WARNING_CONFIG_FAILED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	isis_circuit_metric_set(circuit, IS_LEVEL_1, met);
 | 
			
		||||
	isis_circuit_metric_set(circuit, IS_LEVEL_2, met);
 | 
			
		||||
	CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_1, met),
 | 
			
		||||
			"Failed to set L1 metric: $ERR");
 | 
			
		||||
	CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_2, met),
 | 
			
		||||
			"Failed to set L2 metric: $ERR");
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -525,8 +522,12 @@ DEFUN (no_isis_metric,
 | 
			
		||||
	if (!circuit)
 | 
			
		||||
		return CMD_ERR_NO_MATCH;
 | 
			
		||||
 | 
			
		||||
	isis_circuit_metric_set(circuit, IS_LEVEL_1, DEFAULT_CIRCUIT_METRIC);
 | 
			
		||||
	isis_circuit_metric_set(circuit, IS_LEVEL_2, DEFAULT_CIRCUIT_METRIC);
 | 
			
		||||
	CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_1,
 | 
			
		||||
						DEFAULT_CIRCUIT_METRIC),
 | 
			
		||||
			"Failed to set L1 metric: $ERR");
 | 
			
		||||
	CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_2,
 | 
			
		||||
						DEFAULT_CIRCUIT_METRIC),
 | 
			
		||||
			"Failed to set L2 metric: $ERR");
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -546,28 +547,8 @@ DEFUN (isis_metric_l1,
 | 
			
		||||
		return CMD_ERR_NO_MATCH;
 | 
			
		||||
 | 
			
		||||
	met = atoi(argv[idx_number]->arg);
 | 
			
		||||
 | 
			
		||||
	/* RFC3787 section 5.1 */
 | 
			
		||||
	if (circuit->area && circuit->area->oldmetric == 1
 | 
			
		||||
	    && met > MAX_NARROW_LINK_METRIC) {
 | 
			
		||||
		vty_out(vty,
 | 
			
		||||
			"Invalid metric %d - should be <0-63> "
 | 
			
		||||
			"when narrow metric type enabled\n",
 | 
			
		||||
			met);
 | 
			
		||||
		return CMD_WARNING_CONFIG_FAILED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* RFC4444 */
 | 
			
		||||
	if (circuit->area && circuit->area->newmetric == 1
 | 
			
		||||
	    && met > MAX_WIDE_LINK_METRIC) {
 | 
			
		||||
		vty_out(vty,
 | 
			
		||||
			"Invalid metric %d - should be <0-16777215> "
 | 
			
		||||
			"when wide metric type enabled\n",
 | 
			
		||||
			met);
 | 
			
		||||
		return CMD_WARNING_CONFIG_FAILED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	isis_circuit_metric_set(circuit, IS_LEVEL_1, met);
 | 
			
		||||
	CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_1, met),
 | 
			
		||||
			"Failed to set L1 metric: $ERR");
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -585,7 +566,9 @@ DEFUN (no_isis_metric_l1,
 | 
			
		||||
	if (!circuit)
 | 
			
		||||
		return CMD_ERR_NO_MATCH;
 | 
			
		||||
 | 
			
		||||
	isis_circuit_metric_set(circuit, IS_LEVEL_1, DEFAULT_CIRCUIT_METRIC);
 | 
			
		||||
	CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_1,
 | 
			
		||||
						DEFAULT_CIRCUIT_METRIC),
 | 
			
		||||
			"Failed to set L1 metric: $ERR");
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -605,28 +588,8 @@ DEFUN (isis_metric_l2,
 | 
			
		||||
		return CMD_ERR_NO_MATCH;
 | 
			
		||||
 | 
			
		||||
	met = atoi(argv[idx_number]->arg);
 | 
			
		||||
 | 
			
		||||
	/* RFC3787 section 5.1 */
 | 
			
		||||
	if (circuit->area && circuit->area->oldmetric == 1
 | 
			
		||||
	    && met > MAX_NARROW_LINK_METRIC) {
 | 
			
		||||
		vty_out(vty,
 | 
			
		||||
			"Invalid metric %d - should be <0-63> "
 | 
			
		||||
			"when narrow metric type enabled\n",
 | 
			
		||||
			met);
 | 
			
		||||
		return CMD_WARNING_CONFIG_FAILED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* RFC4444 */
 | 
			
		||||
	if (circuit->area && circuit->area->newmetric == 1
 | 
			
		||||
	    && met > MAX_WIDE_LINK_METRIC) {
 | 
			
		||||
		vty_out(vty,
 | 
			
		||||
			"Invalid metric %d - should be <0-16777215> "
 | 
			
		||||
			"when wide metric type enabled\n",
 | 
			
		||||
			met);
 | 
			
		||||
		return CMD_WARNING_CONFIG_FAILED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	isis_circuit_metric_set(circuit, IS_LEVEL_2, met);
 | 
			
		||||
	CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_2, met),
 | 
			
		||||
			"Failed to set L2 metric: $ERR");
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -644,7 +607,9 @@ DEFUN (no_isis_metric_l2,
 | 
			
		||||
	if (!circuit)
 | 
			
		||||
		return CMD_ERR_NO_MATCH;
 | 
			
		||||
 | 
			
		||||
	isis_circuit_metric_set(circuit, IS_LEVEL_2, DEFAULT_CIRCUIT_METRIC);
 | 
			
		||||
	CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_2,
 | 
			
		||||
						DEFAULT_CIRCUIT_METRIC),
 | 
			
		||||
			"Failed to set L2 metric: $ERR");
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										147
									
								
								lib/ferr.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								lib/ferr.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,147 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2015-16  David Lamparter, for NetDEF, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * Permission to use, copy, modify, and distribute this software for any
 | 
			
		||||
 * purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
 * copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
 | 
			
		||||
#include "ferr.h"
 | 
			
		||||
#include "vty.h"
 | 
			
		||||
#include "jhash.h"
 | 
			
		||||
#include "memory.h"
 | 
			
		||||
 | 
			
		||||
DEFINE_MTYPE_STATIC(LIB, ERRINFO, "error information")
 | 
			
		||||
 | 
			
		||||
static pthread_key_t errkey;
 | 
			
		||||
 | 
			
		||||
static void ferr_free(void *arg)
 | 
			
		||||
{
 | 
			
		||||
	XFREE(MTYPE_ERRINFO, arg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void err_key_init(void) __attribute__((_CONSTRUCTOR(500)));
 | 
			
		||||
static void err_key_init(void)
 | 
			
		||||
{
 | 
			
		||||
	pthread_key_create(&errkey, ferr_free);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void err_key_fini(void) __attribute__((_DESTRUCTOR(500)));
 | 
			
		||||
static void err_key_fini(void)
 | 
			
		||||
{
 | 
			
		||||
	pthread_key_delete(errkey);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct ferr *ferr_get_last(ferr_r errval)
 | 
			
		||||
{
 | 
			
		||||
	struct ferr *last_error = pthread_getspecific(errkey);
 | 
			
		||||
	if (!last_error || last_error->kind == 0)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	return last_error;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ferr_r ferr_clear(void)
 | 
			
		||||
{
 | 
			
		||||
	struct ferr *last_error = pthread_getspecific(errkey);
 | 
			
		||||
	if (last_error)
 | 
			
		||||
		last_error->kind = 0;
 | 
			
		||||
	return ferr_ok();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ferr_r ferr_set_va(const char *file, int line, const char *func,
 | 
			
		||||
		enum ferr_kind kind, const char *pathname, int errno_val,
 | 
			
		||||
		const char *text, va_list va)
 | 
			
		||||
{
 | 
			
		||||
	struct ferr *error = pthread_getspecific(errkey);
 | 
			
		||||
 | 
			
		||||
	if (!error) {
 | 
			
		||||
		error = XCALLOC(MTYPE_ERRINFO, sizeof(*error));
 | 
			
		||||
		if (!error) {
 | 
			
		||||
			/* we're screwed */
 | 
			
		||||
			zlog_err("out of memory while allocating error info");
 | 
			
		||||
			raise(SIGSEGV);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pthread_setspecific(errkey, error);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	error->file = file;
 | 
			
		||||
	error->line = line;
 | 
			
		||||
	error->func = func;
 | 
			
		||||
	error->kind = kind;
 | 
			
		||||
 | 
			
		||||
	error->unique_id = jhash(text, strlen(text),
 | 
			
		||||
			jhash(file, strlen(file), 0xd4ed0298));
 | 
			
		||||
 | 
			
		||||
	error->errno_val = errno_val;
 | 
			
		||||
	if (pathname)
 | 
			
		||||
		snprintf(error->pathname, sizeof(error->pathname),
 | 
			
		||||
				"%s", pathname);
 | 
			
		||||
	else
 | 
			
		||||
		error->pathname[0] = '\0';
 | 
			
		||||
 | 
			
		||||
	vsnprintf(error->message, sizeof(error->message), text, va);
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ferr_r ferr_set_internal(const char *file, int line, const char *func,
 | 
			
		||||
		enum ferr_kind kind, const char *text, ...)
 | 
			
		||||
{
 | 
			
		||||
	ferr_r rv;
 | 
			
		||||
	va_list va;
 | 
			
		||||
	va_start(va, text);
 | 
			
		||||
	rv = ferr_set_va(file, line, func, kind, NULL, 0, text, va);
 | 
			
		||||
	va_end(va);
 | 
			
		||||
	return rv;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ferr_r ferr_set_internal_ext(const char *file, int line, const char *func,
 | 
			
		||||
		enum ferr_kind kind, const char *pathname, int errno_val,
 | 
			
		||||
		const char *text, ...)
 | 
			
		||||
{
 | 
			
		||||
	ferr_r rv;
 | 
			
		||||
	va_list va;
 | 
			
		||||
	va_start(va, text);
 | 
			
		||||
	rv = ferr_set_va(file, line, func, kind, pathname, errno_val, text, va);
 | 
			
		||||
	va_end(va);
 | 
			
		||||
	return rv;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define REPLACE "$ERR"
 | 
			
		||||
void vty_print_error(struct vty *vty, ferr_r err, const char *msg, ...)
 | 
			
		||||
{
 | 
			
		||||
	char tmpmsg[512], *replacepos;
 | 
			
		||||
	const struct ferr *last_error = ferr_get_last(err);
 | 
			
		||||
 | 
			
		||||
	va_list va;
 | 
			
		||||
	va_start(va, msg);
 | 
			
		||||
	vsnprintf(tmpmsg, sizeof(tmpmsg), msg, va);
 | 
			
		||||
	va_end(va);
 | 
			
		||||
 | 
			
		||||
	replacepos = strstr(tmpmsg, REPLACE);
 | 
			
		||||
	if (!replacepos)
 | 
			
		||||
		vty_out(vty, "%s\n", tmpmsg);
 | 
			
		||||
	else {
 | 
			
		||||
		replacepos[0] = '\0';
 | 
			
		||||
		replacepos += sizeof(REPLACE) - 1;
 | 
			
		||||
		vty_out(vty, "%s%s%s\n",
 | 
			
		||||
			tmpmsg,
 | 
			
		||||
			last_error ? last_error->message : "(no error?)",
 | 
			
		||||
			replacepos);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										191
									
								
								lib/ferr.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								lib/ferr.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,191 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2015-16  David Lamparter, for NetDEF, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * Permission to use, copy, modify, and distribute this software for any
 | 
			
		||||
 * purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
 * copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _FRR_FERR_H
 | 
			
		||||
#define _FRR_FERR_H
 | 
			
		||||
 | 
			
		||||
/***********************************************************
 | 
			
		||||
 * scroll down to the end of this file for a full example! *
 | 
			
		||||
 ***********************************************************/
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
/* return type when this error indication stuff is used.
 | 
			
		||||
 *
 | 
			
		||||
 * guaranteed to have boolean evaluation to "false" when OK, "true" when error
 | 
			
		||||
 * (i.e. can be changed to pointer in the future if neccessary)
 | 
			
		||||
 *
 | 
			
		||||
 * For checking, always use "if (value)", nothing else.
 | 
			
		||||
 * Do _NOT_ use any integer constant (!= 0), or sign check (< 0).
 | 
			
		||||
 */
 | 
			
		||||
typedef int ferr_r;
 | 
			
		||||
 | 
			
		||||
/* rough category of error indication */
 | 
			
		||||
enum ferr_kind {
 | 
			
		||||
	/* no error */
 | 
			
		||||
	FERR_OK = 0,
 | 
			
		||||
 | 
			
		||||
	/* something isn't the way it's supposed to be.
 | 
			
		||||
	 * (things that might otherwise be asserts, really)
 | 
			
		||||
	 */
 | 
			
		||||
	FERR_CODE_BUG,
 | 
			
		||||
 | 
			
		||||
	/* user-supplied parameters don't make sense or is inconsistent
 | 
			
		||||
	 * if you can express a rule for it (e.g. "holdtime > 2 * keepalive"),
 | 
			
		||||
	 * it's this category.
 | 
			
		||||
	 */
 | 
			
		||||
	FERR_CONFIG_INVALID,
 | 
			
		||||
 | 
			
		||||
	/* user-supplied parameters don't line up with reality
 | 
			
		||||
	 * (IP address or interface not available, etc.)
 | 
			
		||||
	 * NB: these are really TODOs where the code needs to be fixed to
 | 
			
		||||
	 * respond to future changes!
 | 
			
		||||
	 */
 | 
			
		||||
	FERR_CONFIG_REALITY,
 | 
			
		||||
 | 
			
		||||
	/* out of some system resource (probably memory)
 | 
			
		||||
	 * aka "you didn't spend enough money error" */
 | 
			
		||||
	FERR_RESOURCE,
 | 
			
		||||
 | 
			
		||||
	/* system error (permission denied, etc.) */
 | 
			
		||||
	FERR_SYSTEM,
 | 
			
		||||
 | 
			
		||||
	/* error return from some external library
 | 
			
		||||
	 * (FERR_SYSTEM and FERR_LIBRARY are not strongly distinct) */
 | 
			
		||||
	FERR_LIBRARY,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ferr {
 | 
			
		||||
	/* code location */
 | 
			
		||||
	const char *file;
 | 
			
		||||
	const char *func;
 | 
			
		||||
	int line;
 | 
			
		||||
 | 
			
		||||
	enum ferr_kind kind;
 | 
			
		||||
 | 
			
		||||
	/* unique_id is calculated as a checksum of source filename and error
 | 
			
		||||
	 * message format (*before* calling vsnprintf).  Line number and
 | 
			
		||||
	 * function name are not used; this keeps the number reasonably static
 | 
			
		||||
	 * across changes.
 | 
			
		||||
	 */
 | 
			
		||||
	uint32_t unique_id;
 | 
			
		||||
 | 
			
		||||
	char message[384];
 | 
			
		||||
 | 
			
		||||
	/* valid if != 0.  note "errno" might be preprocessor foobar. */
 | 
			
		||||
	int errno_val;
 | 
			
		||||
	/* valid if pathname[0] != '\0' */
 | 
			
		||||
	char pathname[PATH_MAX];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* get error details.
 | 
			
		||||
 *
 | 
			
		||||
 * NB: errval/ferr_r does NOT carry the full error information.  It's only
 | 
			
		||||
 * passed around for future API flexibility.  ferr_get_last always returns
 | 
			
		||||
 * the last error set in the current thread.
 | 
			
		||||
 */
 | 
			
		||||
const struct ferr *ferr_get_last(ferr_r errval);
 | 
			
		||||
 | 
			
		||||
/* can optionally be called at strategic locations.
 | 
			
		||||
 * always returns 0. */
 | 
			
		||||
ferr_r ferr_clear(void);
 | 
			
		||||
 | 
			
		||||
/* do NOT call these functions directly.  only for macro use! */
 | 
			
		||||
ferr_r ferr_set_internal(const char *file, int line, const char *func,
 | 
			
		||||
		enum ferr_kind kind, const char *text, ...);
 | 
			
		||||
ferr_r ferr_set_internal_ext(const char *file, int line, const char *func,
 | 
			
		||||
		enum ferr_kind kind, const char *pathname, int errno_val,
 | 
			
		||||
		const char *text, ...);
 | 
			
		||||
 | 
			
		||||
#define ferr_ok() \
 | 
			
		||||
	0
 | 
			
		||||
 | 
			
		||||
/* Report an error.
 | 
			
		||||
 *
 | 
			
		||||
 * If you need to do cleanup (free memory, etc.), save the return value in a
 | 
			
		||||
 * variable of type ferr_r.
 | 
			
		||||
 *
 | 
			
		||||
 * Don't put a \n at the end of the error message.
 | 
			
		||||
 */
 | 
			
		||||
#define ferr_code_bug(...) \
 | 
			
		||||
	ferr_set_internal(__FILE__, __LINE__, __func__, FERR_CODE_BUG, \
 | 
			
		||||
			__VA_ARGS__)
 | 
			
		||||
#define ferr_cfg_invalid(...) \
 | 
			
		||||
	ferr_set_internal(__FILE__, __LINE__, __func__, FERR_CONFIG_INVALID, \
 | 
			
		||||
			__VA_ARGS__)
 | 
			
		||||
#define ferr_cfg_reality(...) \
 | 
			
		||||
	ferr_set_internal(__FILE__, __LINE__, __func__, FERR_CONFIG_REALITY, \
 | 
			
		||||
			__VA_ARGS__)
 | 
			
		||||
#define ferr_cfg_resource(...) \
 | 
			
		||||
	ferr_set_internal(__FILE__, __LINE__, __func__, FERR_RESOURCE, \
 | 
			
		||||
			__VA_ARGS__)
 | 
			
		||||
#define ferr_system(...) \
 | 
			
		||||
	ferr_set_internal(__FILE__, __LINE__, __func__, FERR_SYSTEM, \
 | 
			
		||||
			__VA_ARGS__)
 | 
			
		||||
#define ferr_library(...) \
 | 
			
		||||
	ferr_set_internal(__FILE__, __LINE__, __func__, FERR_LIBRARY, \
 | 
			
		||||
			__VA_ARGS__)
 | 
			
		||||
 | 
			
		||||
/* extended information variants */
 | 
			
		||||
#define ferr_system_errno(...) \
 | 
			
		||||
	ferr_set_internal_ext(__FILE__, __LINE__, __func__, FERR_SYSTEM, \
 | 
			
		||||
			NULL, errno, __VA_ARGS__)
 | 
			
		||||
#define ferr_system_path_errno(path, ...) \
 | 
			
		||||
	ferr_set_internal_ext(__FILE__, __LINE__, __func__, FERR_SYSTEM, \
 | 
			
		||||
			path, errno, __VA_ARGS__)
 | 
			
		||||
 | 
			
		||||
#include "vty.h"
 | 
			
		||||
/* print error message to vty;  $ERR is replaced by the error's message */
 | 
			
		||||
void vty_print_error(struct vty *vty, ferr_r err, const char *msg, ...);
 | 
			
		||||
 | 
			
		||||
#define CMD_FERR_DO(func, action, ...) \
 | 
			
		||||
	do { ferr_r cmd_retval = func; \
 | 
			
		||||
		if (cmd_retval) { \
 | 
			
		||||
			vty_print_error(vty, cmd_retval, __VA_ARGS__); \
 | 
			
		||||
			action; \
 | 
			
		||||
		} \
 | 
			
		||||
	} while (0)
 | 
			
		||||
 | 
			
		||||
#define CMD_FERR_RETURN(func, ...) \
 | 
			
		||||
	CMD_FERR_DO(func, return CMD_WARNING, __VA_ARGS__)
 | 
			
		||||
#define CMD_FERR_GOTO(func, label, ...) \
 | 
			
		||||
	CMD_FERR_DO(func, goto label, __VA_ARGS__)
 | 
			
		||||
 | 
			
		||||
/* example:
 | 
			
		||||
 | 
			
		||||
ferr_r foo_bar_set(struct object *obj, int bar)
 | 
			
		||||
{
 | 
			
		||||
	if (bar < 1 || bar >= 100)
 | 
			
		||||
		return ferr_config_invalid("bar setting (%d) must be 0<x<100", bar);
 | 
			
		||||
	obj->bar = bar;
 | 
			
		||||
	if (ioctl (obj->fd, bar))
 | 
			
		||||
		return ferr_system_errno("couldn't set bar to %d", bar);
 | 
			
		||||
 | 
			
		||||
	return ferr_ok();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN("bla")
 | 
			
		||||
{
 | 
			
		||||
	CMD_FERR_RETURN(foo_bar_set(obj, atoi(argv[1])),
 | 
			
		||||
		"command failed: $ERR\n");
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#endif /* _FERR_H */
 | 
			
		||||
@ -17,6 +17,7 @@ lib_libfrr_la_SOURCES = \
 | 
			
		||||
	lib/csv.c \
 | 
			
		||||
	lib/distribute.c \
 | 
			
		||||
	lib/event_counter.c \
 | 
			
		||||
	lib/ferr.c \
 | 
			
		||||
	lib/filter.c \
 | 
			
		||||
	lib/frr_pthread.c \
 | 
			
		||||
	lib/getopt.c \
 | 
			
		||||
@ -89,6 +90,7 @@ pkginclude_HEADERS += \
 | 
			
		||||
	lib/csv.h \
 | 
			
		||||
	lib/distribute.h \
 | 
			
		||||
	lib/event_counter.h \
 | 
			
		||||
	lib/ferr.h \
 | 
			
		||||
	lib/fifo.h \
 | 
			
		||||
	lib/filter.h \
 | 
			
		||||
	lib/freebsd-queue.h \
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user