[asterisk-users] carefulwrite: write() returned error:Brokenpipe

Steve Edwards asterisk.org at sedwards.com
Thu Oct 22 14:45:02 CDT 2009


Un-top-posting...

> On Thu, 22 Oct 2009, Danny Nicholas wrote:
>
>> So this would actually be proper?
>
> [snip]
>
>>> my $envvars = <STDIN>;

> [mailto:asterisk-users-bounces at lists.digium.com] On Behalf Of Steve Edwards
>
> I don't "do" Perl, but if that statement reads everything buffered on
> STDIN -- i.e., the AGI environment, then I would guess it would work.
>
> "Proper" would be to read and parse the environment "properly." You could
> make the argument that since you're not going to use anything from the AGI
> environment, why waste the cycles.
>
> I would counter that "best practice" would be to read and parse the
> environment as well as setting up a SIGHUP handler, initializing syslog(),
> setting a STATUS channel variable to FAILURE (and setting it to SUCCESS
> upon successful completion), etc., etc., etc.

On Thu, 22 Oct 2009, Danny Nicholas wrote:

> For my (and any other lazy watchers) benefit, would you post how you would
> do my snippet in C?

I don't think it will do you much good since you don't have my AGI
library, but below is an AGI from a current project that reads a bunch
of stuff from a database and sets a bunch (hundreds) of variables.

I'm a big fan of using an established library so all the funky details
are taken care of for you and you don't get "bit" by changes like you
have. If I had found a C library when I started, I would have used it.

This wasn't written with "publication" in mind but it does show my
"framework" for all my AGIs.

Keep in mind the AGI below executes in less than a second, including the 
database accesses.

My apologies to the list for the length :)

//
//	Filename:	lookup-dnis.c
//
#define	Version		"002"
//
//	Edit date:	2009-10-16
//
//	Facility:	Asterisk
//
//	Abstract:	This AGI 'script' implements the lookup-dnis
//			application.
//
//	Environment:	Asterisk
//
//	Author:		Steven L. Edwards
//
//	Modified by
//
//	000	2009-03-23	SLE	Create.
//	001	2009-10-16	SLE	Log variables if debug.
//	002	2009-10-16	SLE	Add flags.

////////////////////////////////////////|//////////////////////////////////////
// ANSI include files
////////////////////////////////////////|//////////////////////////////////////
#include	<ctype.h>
#include	<errno.h>
#include	<signal.h>
#include	<stdarg.h>
#include	<stddef.h>
#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	<time.h>

////////////////////////////////////////|//////////////////////////////////////
// Operating system include files
////////////////////////////////////////|//////////////////////////////////////
#include	<syslog.h>

////////////////////////////////////////|//////////////////////////////////////
// Local include files
////////////////////////////////////////|//////////////////////////////////////
#include	"agi.h"
#include	"agi-mysql.h"
#include	"all.h"
#include	"getopt.h"
#include	"mysql.h"

////////////////////////////////////////|//////////////////////////////////////
// Defines
////////////////////////////////////////|//////////////////////////////////////

////////////////////////////////////////|//////////////////////////////////////
// Macros
////////////////////////////////////////|//////////////////////////////////////

////////////////////////////////////////|//////////////////////////////////////
// Typedefs
////////////////////////////////////////|//////////////////////////////////////

////////////////////////////////////////|//////////////////////////////////////
// Global constants
////////////////////////////////////////|//////////////////////////////////////

////////////////////////////////////////|//////////////////////////////////////
// Global variables
////////////////////////////////////////|//////////////////////////////////////

////////////////////////////////////////|//////////////////////////////////////
// Global functions
////////////////////////////////////////|//////////////////////////////////////

////////////////////////////////////////|//////////////////////////////////////
// External constants
////////////////////////////////////////|//////////////////////////////////////

////////////////////////////////////////|//////////////////////////////////////
// External variables
////////////////////////////////////////|//////////////////////////////////////
extern	char				*optarg;

////////////////////////////////////////|//////////////////////////////////////
// External functions
////////////////////////////////////////|//////////////////////////////////////

////////////////////////////////////////|//////////////////////////////////////
// Static constants
////////////////////////////////////////|//////////////////////////////////////

////////////////////////////////////////|//////////////////////////////////////
// Static variables
////////////////////////////////////////|//////////////////////////////////////
static	int				debug_mode;
static	MYSQL				mysql;
static	MYSQL_ROW			mysql_row;
static	int				test_mode;
static	int				verbose_mode;

////////////////////////////////////////|//////////////////////////////////////
// Static functions
////////////////////////////////////////|//////////////////////////////////////
static	int				lookup_dnis
 	(
 	  const char			*dnis_pointer
 	);
static	void				hangup
 	(
 	  void
 	);

////////////////////////////////////////|//////////////////////////////////////
// Main function
////////////////////////////////////////|//////////////////////////////////////
global	int				main
 	(
 	  int				argc
 	, char				**argv
 	)
 	{
 	auto	char			dnis[256];
 	static	struct option		long_options[]
 		= {
 		  {"debug-mode", no_argument, &debug_mode, 1}
 		, {"help", no_argument, 0, '?'}
 		, {"null", no_argument, 0, 0}
 		, {"test-mode", no_argument, &test_mode, 1}
 		, {"verbose-mode", no_argument, &verbose_mode, 1}
 		, {"version", no_argument, 0, 'v'}
 		, {0, 0, 0, 0}
 		};
 	auto	int			optchar;
 	auto	char			*pointer1;
 	auto	char			*pointer2;
 	auto	int			status;

// set the syslog ident
 	setlogmask(LOG_UPTO(LOG_NOTICE));
 	openlog(argv[0], LOG_PID, LOG_USER);

// read the AGI environment
 	agi_read_environment();

// assume failure
 	agi_set_status_failure();

// get the DNIS
 	memset(dnis, 0, sizeof(dnis));
 	agi_get_variable("DNIS", dnis);

// process the command line arguments
 	while	(-1 != (optchar
 			= getopt_long(
 				  argc
 				, argv
 				, ""
 				, long_options
 				, 0
 				)))
 		{
 		switch	(optchar)
 			{
// help
 			case '?':
 			default:
 				puts("Usage:\tlookup-dnis\\");
 				puts("\t\t--debug-mode");
 				puts("\t\t--help\\");
 				puts("\t\t--null\\");
 				puts("\t\t--verbose-mode\\");
 				puts("\t\t--version");
 				exit(EXIT_SUCCESS);
 				break;
// version
 			case 'v':
 				printf("%s version %s\n"
 					, *argv
 					, Version
 					);
 				printf("Compiled on %s at %s\n"
 					, __DATE__
 					, __TIME__
 					);
 				exit(EXIT_SUCCESS);
 				break;

// something automagically handled by getopt_long()
 			case 0:
 				break;
 			}
 		}

// set verbose mode
 	if	(0 != verbose_mode)
 		{
 		setlogmask(LOG_UPTO(LOG_INFO));
 		}

// set debug mode
 	if	(0 != debug_mode)
 		{
 		setlogmask(LOG_UPTO(LOG_DEBUG));
 		}

// say who we are
 	syslog(LOG_DEBUG
 		, "Starting, version %s"
 		, Version
 		);

// trap SIGHUP -- caller hung up
 	signal(SIGHUP, (void (*)(int))(int)hangup);

// clean the DNIS
 	pointer1 = pointer2 = dnis;
 	--pointer1;
 	while	(0 != *++pointer1)
 		{
// keep letters, forcing to lower case
 		if	(0 != isalpha(*pointer1))
 			{
 			*pointer2++ = 32 | *pointer1;
 			continue;
 			}
// keep numbers
 		if	(0 != isdigit(*pointer1))
 			{
 			*pointer2++ = *pointer1;
 			continue;
 			}
 		}
// terminate the DNIS
 	*pointer2 = 0;

// drop leading 1
 	if	('1' == *dnis)
 		{
 		auto	char		temp[256];
 		strcpy(temp, dnis + 1);
 		strcpy(dnis, temp);
 		}

// log the DNIS
 	syslog(LOG_DEBUG, "DNIS = %s", dnis);

// connect to the database
 	if	(EXIT_FAILURE == (status = agi_connect_to_database(
 			  &mysql	// context
 			)))
 		{
 		syslog(LOG_ERR
 			, "%m, mysql_error = \"%s\""
 			, mysql_error(&mysql)
 			);
 		agi_verbose(
 			  "Failed to connect to the database"
 				", mysql_error = \"%s\""
 			, mysql_error(&mysql)
 			);
 		exit(EXIT_FAILURE);
 		}

// lookup the DNIS
 	if	(EXIT_FAILURE == (status = lookup_dnis(dnis)))
 		{
 		mysql_close(&mysql);
 		agi_set_status_failure();
 		return(EXIT_FAILURE);
 		}

// function exit
 	mysql_close(&mysql);
 	agi_set_status_success();
 	return(EXIT_SUCCESS);

 	}

////////////////////////////////////////|///////////////////////////////|//////
// hangup()
//
// Asterisk delivers a SIGHUP when the caller hangs up. Since we have
// already registered a process termination function, all we have to
// do is exit.
////////////////////////////////////////|///////////////////////////////|//////
static	void				hangup
 	(
 	  void
 	)
 	{
 	mysql_close(&mysql);
 	exit(EXIT_SUCCESS);
 	}

////////////////////////////////////////|///////////////////////////////|//////
// lookup_dnis()
//
// Lookup the DNIS. Exit if successful.
////////////////////////////////////////|///////////////////////////////|//////
static	int				lookup_dnis
 	(
 	  const char			*dnis_pointer
 	)
 	{
 	auto	int			column;
 	auto	MYSQL_FIELD		*fields;
 	auto	int			index;
 	auto	int			num_fields;
 	auto	int			num_rows;
 	auto	MYSQL_RES		*result_pointer;
 	static	const char		recipes[]
 		= "select"
 				"  client_id"
 				", confirm_prompt"
 				", goodbye"
 				", initial_language"
 				", outbound_dialstring"
 				", verification_prompt_a"
 				", verification_prompt_b"
 				", you_entered_prompt"
 			" from"
 				"  dnises"
 				", recipes"
 			" where	'%s' = dnis"
 			" and	dnises.recipe = recipes.recipe"
 			;
 	auto	int			status;
 	static	const char		steps[]
 		= "select"
 				"  steps.idx"
 					// idx must be the first column
 				", steps.active"
 				", steps.data"
 				", steps.flags"
 				", steps.max_length"
 				", steps.min_length"
 				", steps.prompt"
 				", steps.retry_prompt"
 				", steps.type"
 			" from"
 				"  dnises"
 				", recipes"
 				", steps"
 			" where	'%s' = dnis"
 			" and	dnises.recipe = recipes.recipe"
 			" and	recipes.recipe = steps.recipe"
 			" order	by idx"
 			;

// look for the recipe parameters
 	exec_sql(&mysql
 		, &result_pointer
 		, recipes		// select statement template
 		, dnis_pointer		// dnis
 		);

// complain if not found
 	if	((1 != mysql_num_rows(result_pointer))
 	||	 ((0 == (mysql_row = mysql_fetch_row(result_pointer)))))
 		{
 		syslog(LOG_ERR
 			, "No setup for DNIS %s in the DNISES"
 				" and RECIPES tables."
 			, dnis_pointer
 			);
 		return(EXIT_FAILURE);
 		}

// set the variables
 	num_fields = mysql_num_fields(result_pointer);
 	fields = mysql_fetch_fields(result_pointer);
 	for	(column = 0; column < num_fields; ++column)
 		{
 		auto	char		*pointer;
 		auto	char		temp[256];
 		if	((0 == mysql_row[column])
 		||	 ('(' == *mysql_row[column]))
 			{
 			continue;
 			}
 		strcpy(temp, fields[column].name);
 		pointer = temp;
 		while	(0 != *pointer)
 			{
 			if	(islower(*pointer))
 				{
 				*pointer = toupper(*pointer);
 				}
 			if	('_' == *pointer)
 				{
 				*pointer = '-';
 				}
 			++pointer;
 			}
 		syslog(LOG_DEBUG, "%s = \"%s\"", temp, mysql_row[column]);
 		agi_set_variable(temp, mysql_row[column]);
 		}

// look for the step parameters
 	status = exec_sql(&mysql
 		, &result_pointer
 		, steps			// select statement template
 		, dnis_pointer		// dnis
 		);
 	if	(0 != status)
 		{
 		syslog(LOG_ERR
 			, "%m, mysql_error = \"%s\""
 			, mysql_error(&mysql)
 			);
 		}

// complain if not found
 	if	(1 > (num_rows = mysql_num_rows(result_pointer)))
 		{
 		syslog(LOG_ERR
 			, "No setup for DNIS %s in the DNISES,"
 				" RECIPES, and STEPS tables."
 			, dnis_pointer
 			);
 		return(EXIT_FAILURE);
 		}

// walk through the rows
 	for	(index = 0; ; ++index)
 		{
 		mysql_data_seek(result_pointer, index);
 		if	(0 == (mysql_row = mysql_fetch_row(result_pointer)))
 			{
 			break;
 			}

// set the variables
 		num_fields = mysql_num_fields(result_pointer);
 		fields = mysql_fetch_fields(result_pointer);
 		for	(column = 0; column < num_fields; ++column)
 			{
 			auto	char		*pointer;
 			auto	char		temp[256];
 			if	((0 == mysql_row[column])
 			||	 ('(' == *mysql_row[column]))
 				{
 				continue;
 				}
 			sprintf(temp
 				, "STEP-%02d-%s"
 				, atoi(mysql_row[0])
 				, fields[column].name
 				);
 			pointer = temp + 5;
 			while	(0 != *pointer)
 				{
 				if	(islower(*pointer))
 					{
 					*pointer = toupper(*pointer);
 					}
 				if	('_' == *pointer)
 					{
 					*pointer = '-';
 					}
 				++pointer;
 				}
 			syslog(LOG_DEBUG, "%s = \"%s\"", temp, mysql_row[column]);
 			agi_set_variable(temp, mysql_row[column]);
 			}
 		}

// free the result set
 	mysql_free_result(result_pointer);

// set the agi status
 	agi_set_status_failure();

// function exit
 	return(EXIT_SUCCESS);

 	}

// (end of lookup-dnis.c)

-- 
Thanks in advance,
-------------------------------------------------------------------------
Steve Edwards       sedwards at sedwards.com      Voice: +1-760-468-3867 PST
Newline                                              Fax: +1-760-731-3000



More information about the asterisk-users mailing list