[Date Prev][Date Next] [Chronological] [Thread] [Top]

new module overlay modtrigger



Hey,

a few years back i wrote an overlay module "modtrigger", that executes a
script on modifications.

Some of the comments i got was that it should really require to work with
the new config structure.

Finally i've done that, but i'm sure it could be improved.

So, i'm kind of asking if someone can review it and give some pointers to
improve.

and next if it's possible to include in the openldap distribution, in the
overlay section or contrib section.
/* modtrigger.c - returns last modification info */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
 * Copyright 2004-2012 The OpenLDAP Foundation.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */
/* ACKNOWLEDGEMENTS:
 * This work was initially developed by Pierangelo Masarati for inclusion in
 * OpenLDAP Software.
 */

#include "portable.h"

#ifndef SLAPD_OVER_MODTRIGGER
#define SLAPD_OVER_MODTRIGGER SLAPD_MOD_DYNAMIC
#endif

#ifdef SLAPD_OVER_MODTRIGGER

#include <stdio.h>
#include <unistd.h>

#include <ac/string.h>
#include <ac/socket.h>

#include <errno.h>

#include "slap.h"
#include "lutil.h"
#include "config.h"

typedef struct modtrigger_info_t {
	char*			tri_exec;
	int			tri_enabled;
} modtrigger_info_t;

static ConfigTable modtriggercfg[] = {
	{ "modtrigger-exec", "filename", 2, 0, 0,
	  ARG_STRING|ARG_OFFSET,
	  (void *)offsetof(modtrigger_info_t, tri_exec),
	  "( OLcfgOvAt:137.1 NAME 'olcModtriggerExec' "
	  "DESC 'script to be triggered on modification' "
	  "SYNTAX OMsDirectoryString )", NULL, NULL },
	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
};

static ConfigOCs modtriggerocs[] = {
	{ "( OLcfgOvOc:137.1 "
	  "NAME 'olcModtriggerConfig' "
	  "DESC 'modtrigger overlay configuration' "
	  "SUP olcOverlayConfig "
	  "MAY ( olcModtriggerExec ) )",
	  Cft_Overlay, modtriggercfg },
	{ NULL, 0, NULL }
};

static int
modtrigger_op2str( Operation *op, char **op_strp )
{
	switch ( op->o_tag ) {
	case LDAP_REQ_MODIFY:
		*op_strp = "MODIFY";
		break;
	case LDAP_REQ_ADD:
		*op_strp = "ADD";
		break;
	case LDAP_REQ_DELETE:
		*op_strp = "DELETE";
		break;
	case LDAP_REQ_MODRDN:
		*op_strp = "MODRDN";
		break;
	case LDAP_REQ_EXTENDED:
		*op_strp = "EXTENDED";
		break;
	default:
		*op_strp = "UNKNOWN";
	}
	return 0;				
}

static int
modtrigger_op_func( Operation *op, SlapReply *rs )
{
	return SLAP_CB_CONTINUE;
}

static void
modtrigger_exec( Operation *op )
{
	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
	modtrigger_info_t	*tri = (modtrigger_info_t *)on->on_bi.bi_private;
	char*			argp[ 5 ] = { "modtrigger", NULL, NULL, NULL, NULL };
	char*			envp[ 1 ] = { NULL };
       struct berval           bv_name = BER_BVNULL;
       struct berval           pdn = BER_BVNULL;

	modtrigger_op2str( op, &argp[ 2 ] );
	fprintf(stderr, "modtrigger_exec op='%s'.\n", argp[ 2 ]);

	/* FIXME: timestamp? modifier? */
	switch ( op->o_tag ) {
	case LDAP_REQ_ADD:
		argp[ 1 ] = op->ora_e->e_name.bv_val;
		break;

	case LDAP_REQ_DELETE:
	case LDAP_REQ_EXTENDED:
	case LDAP_REQ_MODIFY:
		argp[ 1 ] = op->o_req_dn.bv_val;
		break;

	case LDAP_REQ_MODRDN:
		if ( op->orr_newSup && !BER_BVISNULL( op->orr_newSup ) ) {
			build_new_dn( &bv_name, op->orr_newSup, &op->orr_newrdn, NULL );

		} else {
			dnParent( &op->o_req_dn, &pdn );
			build_new_dn( &bv_name, &pdn, &op->orr_newrdn, NULL );
		}

		argp[ 1 ] = bv_name.bv_val;
		// old one
		argp[ 3 ] = op->o_req_dn.bv_val;

		break;

	default:
		return ;
	}
	
	// fork
	pid_t pid = vfork();
	if (pid < 0) {
		// log error, we cannot fork
		Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_INFO, "%s modtrigger ERROR: cannot fork dn='%s'.\n", op->o_log_prefix, argp[ 1 ] );
		return ;
	}
	if (pid == 0) {
		int r = execve( tri->tri_exec, argp, envp );
		Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_INFO, "%s modtrigger ERROR: execve failed dn='%s'.\n", op->o_log_prefix, argp[ 1 ] );
		fprintf(stderr, "execve failed with %d and errno %d: '%s'\n", r, errno, strerror(errno));
		exit(r);
	}
	fprintf(stderr, "forked with pid %d\n", pid);
}

static int
modtrigger_response( Operation *op, SlapReply *rs )
{
	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
	modtrigger_info_t	*tri = (modtrigger_info_t *)on->on_bi.bi_private;

	/* don't record failed operations */
	switch ( rs->sr_err ) {
	case LDAP_SUCCESS:
		/* FIXME: other cases? */
		break;

	default:
		return SLAP_CB_CONTINUE;
	}

	/* record only write operations */
	switch ( op->o_tag ) {
	case LDAP_REQ_ADD:
	case LDAP_REQ_MODIFY:
	case LDAP_REQ_MODRDN:
	case LDAP_REQ_DELETE:
		break;

	case LDAP_REQ_EXTENDED:
		/* if write, process */
		if ( exop_is_write( op ))
			break;

		/* fall thru */
	default:
		return SLAP_CB_CONTINUE;
	}

	/* skip if disabled */
	if ( !tri->tri_enabled ) {
		return SLAP_CB_CONTINUE;
	}

	modtrigger_exec( op );

	return SLAP_CB_CONTINUE;
}

static int
modtrigger_db_init(
	BackendDB *be,
	ConfigReply *cr
)
{
	slap_overinst		*on = (slap_overinst *)be->bd_info;
	modtrigger_info_t		*tri;

	tri = (modtrigger_info_t *)ch_malloc( sizeof( modtrigger_info_t ) );

	memset( tri, 0, sizeof( modtrigger_info_t ) );
	tri->tri_enabled = 1;
	
	on->on_bi.bi_private = tri;

	return 0;
}

static int
modtrigger_db_config(
	BackendDB	*be,
	const char	*fname,
	int		lineno,
	int		argc,
	char	**argv
)
{
	slap_overinst		*on = (slap_overinst *)be->bd_info;
	modtrigger_info_t		*tri = (modtrigger_info_t *)on->on_bi.bi_private;

	if ( strcasecmp( argv[ 0 ], "modtrigger-exec" ) == 0 ) {
		if ( tri->tri_exec ) {
			/* already defined! */
			ch_free( tri->tri_exec );
		}

		tri->tri_exec = ch_malloc( strlen( argv[ 1 ] ) + 1 );
		memcpy(tri->tri_exec, argv[ 1 ], strlen( argv[ 1 ] ) );
		tri->tri_exec[ strlen( argv[ 1 ] ) ] = 0;

	} else {
		return SLAP_CONF_UNKNOWN;
	}

	return 0;
}

static int
modtrigger_db_open(
	BackendDB *be,
	ConfigReply *cr
)
{

	/*
	 * Start
	 */
	return 0;
}

static int
modtrigger_db_destroy(
	BackendDB *be,
	ConfigReply *cr
)
{
	slap_overinst	*on = (slap_overinst *)be->bd_info;
	modtrigger_info_t	*tri = (modtrigger_info_t *)on->on_bi.bi_private;

	if ( tri ) {
		if ( tri->tri_exec ) {
			ch_free( tri->tri_exec );
		}

		ch_free( tri );
	}

	return 0;
}

/* This overlay is set up for dynamic loading via moduleload. For static
 * configuration, you'll need to arrange for the slap_overinst to be
 * initialized and registered by some other function inside slapd.
 */

static slap_overinst 		modtrigger;

int
modtrigger_initialize()
{
	modtrigger.on_bi.bi_type = "modtrigger";
	modtrigger.on_bi.bi_db_init = modtrigger_db_init;
	modtrigger.on_bi.bi_db_config = modtrigger_db_config;
	modtrigger.on_bi.bi_db_destroy = modtrigger_db_destroy;
	modtrigger.on_bi.bi_db_open = modtrigger_db_open;

	modtrigger.on_bi.bi_op_add = modtrigger_op_func;
	modtrigger.on_bi.bi_op_compare = modtrigger_op_func;
	modtrigger.on_bi.bi_op_delete = modtrigger_op_func;
	modtrigger.on_bi.bi_op_modify = modtrigger_op_func;
	modtrigger.on_bi.bi_op_modrdn = modtrigger_op_func;
	modtrigger.on_bi.bi_op_search = modtrigger_op_func;
	modtrigger.on_bi.bi_extended = modtrigger_op_func;

	modtrigger.on_response = modtrigger_response;
	
	modtrigger.on_bi.bi_cf_ocs = modtriggerocs;
	int rc = config_register_schema( modtriggercfg, modtriggerocs );
	if (rc) return rc;

	return overlay_register( &modtrigger );
}

#if SLAPD_OVER_MODTRIGGER == SLAPD_MOD_DYNAMIC
int
init_module( int argc, char *argv[] )
{
	return modtrigger_initialize();
}
#endif /* SLAPD_OVER_MODTRIGGER == SLAPD_MOD_DYNAMIC */

#endif /* defined(SLAPD_OVER_MODTRIGGER) */