Add a python module for watching LDAP ipa_configd needs to be able to watch the directory for configuration changes, so add a python module which exposes a simple API to use "psearch" LDAP notifications to watch for changes. Signed-off-by: Mark McLoughlin diff -r f5d8a4a5c2c2 ipa-server/acinclude.m4 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ipa-server/acinclude.m4 Fri Jan 18 14:02:21 2008 +0000 @@ -0,0 +1,24 @@ +dnl a macro to check for ability to create python extensions +dnl AM_CHECK_PYTHON_HEADERS([ACTION-IF-POSSIBLE], [ACTION-IF-NOT-POSSIBLE]) +dnl function also defines PYTHON_INCLUDES +AC_DEFUN([AM_CHECK_PYTHON_HEADERS], +[AC_REQUIRE([AM_PATH_PYTHON]) +AC_MSG_CHECKING(for headers required to compile python extensions) +dnl deduce PYTHON_INCLUDES +py_prefix=`$PYTHON -c "import sys; print sys.prefix"` +py_exec_prefix=`$PYTHON -c "import sys; print sys.exec_prefix"` +PYTHON_INCLUDES="-I${py_prefix}/include/python${PYTHON_VERSION}" +if test "$py_prefix" != "$py_exec_prefix"; then + PYTHON_INCLUDES="$PYTHON_INCLUDES -I${py_exec_prefix}/include/python${PYTHON_VERSION}" +fi +AC_SUBST(PYTHON_INCLUDES) +dnl check if the headers exist: +save_CPPFLAGS="$CPPFLAGS" +CPPFLAGS="$CPPFLAGS $PYTHON_INCLUDES" +AC_TRY_CPP([#include ],dnl +[AC_MSG_RESULT(found) +$1],dnl +[AC_MSG_RESULT(not found) +$2]) +CPPFLAGS="$save_CPPFLAGS" +]) diff -r f5d8a4a5c2c2 ipa-server/configure.ac --- a/ipa-server/configure.ac Fri Jan 18 08:51:17 2008 +0000 +++ b/ipa-server/configure.ac Fri Jan 18 14:02:21 2008 +0000 @@ -150,6 +150,29 @@ if test "x$PYTHON" = "x" ; then if test "x$PYTHON" = "x" ; then AC_MSG_ERROR([Python not found]) fi + +# Detect if we can build Python bindings (need python and python headers) +AM_CHECK_PYTHON_HEADERS(,[AC_MSG_ERROR([Can't locate python headers])]) + +# Because of the way Python implements polymorphism, we get the following warning: +# "warning: dereferencing type-punned pointer will break strict-aliasing rules" +# -fno-strict-aliasing (as used in Python build) switches warnings off +NO_STRICT_ALIASING_CFLAGS="" +if test "x$GCC" = "xyes" ; then + AC_MSG_CHECKING(whether $CC accepts -fno-strict-aliasing) + ac_save_cc="$CC" + CC="$CC -fno-strict-aliasing" + AC_TRY_RUN([int main() { return 0; }], + ac_cv_no_strict_aliasing_ok=yes, + ac_cv_no_strict_aliasing_ok=no, + ac_cv_no_strict_aliasing_ok=no) + CC="$ac_save_cc" + AC_MSG_RESULT($ac_cv_no_strict_aliasing_ok) + if test "x$ac_cv_no_strict_aliasing_ok" = "xyes" ; then + NO_STRICT_ALIASING_CFLAGS="-fno-strict-aliasing" + fi +fi +AC_SUBST(NO_STRICT_ALIASING_CFLAGS) dnl --------------------------------------------------------------------------- dnl - Set the data install directory since we don't use pkgdatadir diff -r f5d8a4a5c2c2 ipa-server/ipa-configd/ipaconfigd/Makefile.am --- a/ipa-server/ipa-configd/ipaconfigd/Makefile.am Fri Jan 18 08:51:17 2008 +0000 +++ b/ipa-server/ipa-configd/ipaconfigd/Makefile.am Fri Jan 18 14:02:21 2008 +0000 @@ -1,4 +1,12 @@ NULL = NULL = + +ipaconfigd_LTLIBRARIES = ldapwatch.la + +ldapwatch_la_CFLAGS = $(NO_STRICT_ALIASING_CFLAGS) +ldapwatch_la_CPPFLAGS = $(PYTHON_INCLUDES) +ldapwatch_la_LDFLAGS = -module -avoid-version -fPIC -export-symbols-regex initldapwatch +ldapwatch_la_LIBADD = $(LDAP_LIBS) +ldapwatch_la_SOURCES = ldapwatch.c ipaconfigddir = $(pythondir)/ipaconfigd/ ipaconfigd_PYTHON = \ diff -r f5d8a4a5c2c2 ipa-server/ipa-configd/ipaconfigd/__init__.py --- a/ipa-server/ipa-configd/ipaconfigd/__init__.py Fri Jan 18 08:51:17 2008 +0000 +++ b/ipa-server/ipa-configd/ipaconfigd/__init__.py Fri Jan 18 14:02:21 2008 +0000 @@ -15,3 +15,17 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # + +# +# Nasty hack to allow the ldapwatch module to be loaded +# while running from the source directory - libtool puts +# the actually shared library in the ".libs" directory +# + +import os.path + +_dot_libs_path = os.path.join(os.path.dirname(__file__), ".libs") + +if os.path.exists(_dot_libs_path): + import sys + sys.path.insert(0, os.path.abspath(_dot_libs_path)) diff -r f5d8a4a5c2c2 ipa-server/ipa-configd/ipaconfigd/ldapwatch.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ipa-server/ipa-configd/ipaconfigd/ldapwatch.c Fri Jan 18 14:02:21 2008 +0000 @@ -0,0 +1,710 @@ +/* + * Copyright (C) 2006 Red Hat, Inc. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + + * Authors: + * Mark McLoughlin + */ + +/* + * Module allows us to use "psearch" LDAP notifications, the notification + * system which Fedora Directory Server implements, with a simple API: + * + * w = LDAPWatch ("ldap://mydirectory/", "dc=example,dc=com") + * + * for change in w.get_changes (): + * (dn, type, prev_dn) = change + * + * Notes: + * - you can pass a bind_dn and bind_passwd as the 3rd and 4th arguments + * to the constructor + * - get_changes() takes an optional timeout in seconds; if unspecified + * the function will block until it gets a result, if zero it will + * immediately return an empty list if no results are available + * - type is one of TYPE_ADD, TYPE_DELETE, TYPE_MODIFY, TYPE_MODDN + * - prev_dn is only valid if type == TYPE_MODDN + * + * See: + * http://www.watersprings.org/pub/id/draft-smith-psearch-ldap-01.txt + * http://www.watersprings.org/pub/id/draft-ietf-ldapext-c-api-psearch-00.txt + * http://www.watersprings.org/pub/id/draft-ietf-ldapext-ldap-c-api-05.txt + */ + +#include + +#include +#include +#include +#include +#include + +#define _LDAP_CONTROL_PERSISTENTSEARCH "2.16.840.1.113730.3.4.3" +#define _LDAP_CONTROL_ENTRYCHANGE "2.16.840.1.113730.3.4.7" + +#define _LDAP_CHANGETYPE_ADD 1 +#define _LDAP_CHANGETYPE_DELETE 2 +#define _LDAP_CHANGETYPE_MODIFY 4 +#define _LDAP_CHANGETYPE_MODDN 8 +#define _LDAP_CHANGETYPE_ANY (1|2|4|8) + +typedef struct +{ + PyObject_HEAD + + char *uri; + char *base; + char *bind_dn; + char *bind_passwd; + + LDAP *cnx; + int msgid; +} PyLDAPWatch; + +static PyObject *module = NULL; +static PyObject *LDAP_exception = NULL; + +static inline PyObject * +lookup_item_type (int type) +{ + const char *item_type_str; + PyObject *retval; + + switch (type) + { + case _LDAP_CHANGETYPE_ADD: + item_type_str = "TYPE_ADD"; + break; + + case _LDAP_CHANGETYPE_DELETE: + item_type_str = "TYPE_DELETE"; + break; + + case _LDAP_CHANGETYPE_MODIFY: + item_type_str = "TYPE_MODIFY"; + break; + + case _LDAP_CHANGETYPE_MODDN: + item_type_str = "TYPE_MODIFY"; + break; + + default: + item_type_str = "TYPE_INVALID"; + break; + } + + retval = PyDict_GetItemString (PyModule_GetDict (module), item_type_str); + Py_INCREF (retval); + + return retval; +} + +static int +parse_entrychange_controls (LDAP *cnx, + LDAPControl **controls, + int *change_type_ret, + int *change_num_ret, + char **previous_dn_ret) + +{ + int i; + ber_int_t change_type; + ber_int_t change_num; + char *previous_dn; + + if (change_type_ret != NULL) + *change_type_ret = 0; + if (change_num_ret != NULL) + change_num_ret = 0; + if (previous_dn_ret != NULL) + *previous_dn_ret = NULL; + + change_type = 0; + change_num = 0; + previous_dn = NULL; + + for (i = 0; controls[i] != NULL; i++) + { + LDAPControl *control = controls[i]; + BerElement *element; + ber_len_t len; + + if (strcmp (control->ldctl_oid, _LDAP_CONTROL_ENTRYCHANGE) != 0) + continue; + + element = ber_init (&control->ldctl_value); + if (element == NULL) + return LDAP_NO_MEMORY; + + if (ber_scanf (element, "{e", &change_type) == LBER_ERROR) + { + ber_free (element, 1); + return LDAP_DECODING_ERROR; + } + + previous_dn = NULL; + + switch (change_type) + { + case _LDAP_CHANGETYPE_ADD: + break; + + case _LDAP_CHANGETYPE_DELETE: + break; + + case _LDAP_CHANGETYPE_MODIFY: + break; + + case _LDAP_CHANGETYPE_MODDN: + if (ber_scanf (element, "a", &previous_dn) == LBER_ERROR) + { + ber_free (element, 1); + return LDAP_DECODING_ERROR; + } + break; + + default: + break; + } + + if (ber_peek_tag (element, &len) == LBER_INTEGER) + { + if (ber_scanf (element, "i", &change_num) == LBER_ERROR) + { + ber_free (element, 1); + return LDAP_DECODING_ERROR; + } + } + } + + if (change_type_ret != NULL) + *change_type_ret = change_type; + if (change_num_ret != NULL) + *change_num_ret = change_num; + if (previous_dn_ret != NULL) + *previous_dn_ret = previous_dn; + + return LDAP_SUCCESS; +} + +static int +get_received_results (LDAP *cnx, + int msgid, + struct timeval *timeout_tv, + LDAPMessage **entries_ret) +{ + LDAPMessage *entries; + int ret; + + if (entries_ret != NULL) + *entries_ret = NULL; + + entries = NULL; + ret = ldap_result (cnx, msgid, LDAP_MSG_RECEIVED, timeout_tv, &entries); + if (ret < 0) + { + int err; + + err = LDAP_SUCCESS; + ldap_get_option (cnx, LDAP_OPT_RESULT_CODE, &err); + + return err; + } + + if (ret == 0) /* timed out */ + { + ldap_msgfree (entries); + return LDAP_SUCCESS; + } + + if (entries_ret != NULL) + *entries_ret = entries; + else + ldap_msgfree (entries); + + return LDAP_SUCCESS; +} + +static int +parse_result (LDAP *cnx, + LDAPMessage *entry, + char **dn_ret, + int *type_ret, + char **prev_dn_ret) +{ + LDAPControl **ec_controls; + int ret; + int ch_type; + char *prev_dn; + + if (dn_ret != NULL) + *dn_ret = NULL; + if (type_ret != NULL) + *type_ret = 0; + if (prev_dn_ret != NULL) + *prev_dn_ret = 0; + + ec_controls = NULL; + if ((ret = ldap_get_entry_controls (cnx, entry, &ec_controls)) != LDAP_SUCCESS) + return ret; + + ch_type = 0; + prev_dn = NULL; + if ((ret = parse_entrychange_controls (cnx, ec_controls, &ch_type, NULL, &prev_dn)) != LDAP_SUCCESS) + { + ldap_controls_free (ec_controls); + + return ret; + } + + if (dn_ret != NULL) + *dn_ret = strdup (ldap_get_dn (cnx, entry)); + if (type_ret != NULL) + *type_ret = ch_type; + if (prev_dn_ret != NULL && prev_dn != NULL) + *prev_dn_ret = strdup (prev_dn); + + ldap_controls_free (ec_controls); + + return LDAP_SUCCESS; +} + +static PyObject * +pyldap_watch_get_changes (PyObject *self, + PyObject *args) +{ + PyLDAPWatch *watch; + PyObject *retval; + LDAPMessage *entries; + LDAPMessage *entry; + PyObject *timeout; + struct timeval tv; + struct timeval *tvp; + int ret; + + timeout = Py_None; + + if (args != NULL) + { + if (!PyArg_ParseTuple (args, "|O:ldapwatch.LDAPWatch.get_changes_directory", &timeout)) + return NULL; + } + + tvp = NULL; + + if (timeout != Py_None) + { + if (!PyNumber_Check (timeout)) + { + PyErr_SetString (PyExc_TypeError, + "timeout must be a float or None"); + return NULL; + } + else + { + double seconds; + + seconds = PyFloat_AsDouble (timeout); + if (PyErr_Occurred ()) + return NULL; + + if (seconds > (double) LONG_MAX) + { + PyErr_SetString (PyExc_OverflowError, + "timeout period too long"); + return NULL; + } + + tv.tv_sec = seconds; + tv.tv_usec = (seconds - tv.tv_sec) * 1000000; + + tvp = &tv; + } + } + + watch = (PyLDAPWatch *) self; + + entries = NULL; + ret = get_received_results (watch->cnx, watch->msgid, tvp, &entries); + if (ret != LDAP_SUCCESS) + { + PyErr_SetString (LDAP_exception, ldap_err2string (ret)); + return NULL; + } + + if (entries == NULL) /* timed out */ + return PyList_New (0); + + retval = PyList_New (0); + + entry = ldap_first_entry (watch->cnx, entries); + while (entry != NULL) + { + PyObject *tuple; + int type; + char *dn; + char *prev_dn; + + type = 0; + dn = prev_dn = NULL; + ret = parse_result (watch->cnx, entry, &dn, &type, &prev_dn); + if (ret != LDAP_SUCCESS) + { + ldap_msgfree (entries); + + PyErr_SetString (LDAP_exception, ldap_err2string (ret)); + Py_DECREF (retval); + + return NULL; + } + + tuple = PyTuple_New (3); + + PyTuple_SET_ITEM (tuple, 0, PyString_FromString (dn)); + PyTuple_SET_ITEM (tuple, 1, lookup_item_type (type)); + + if (prev_dn) + { + PyTuple_SET_ITEM (tuple, 2, PyString_FromString (prev_dn)); + } + else + { + Py_INCREF (Py_None); + PyTuple_SET_ITEM (tuple, 2, Py_None); + } + + PyList_Append (retval, tuple); + Py_DECREF (tuple); + + free (dn); + dn = NULL; + + type = 0; + + if (prev_dn != NULL) + free (prev_dn); + prev_dn = NULL; + + entry = ldap_next_entry (watch->cnx, entry); + } + + ldap_msgfree (entries); + + return retval; +} + +static int +create_psearch_control (int change_types, + int changes_only, + int return_ecs, + LDAPControl **control_ret) +{ + LDAPControl *control; + BerElement *element; + ber_int_t ber_change_types; + ber_int_t ber_changes_only; + ber_int_t ber_return_ecs; + int ret; + + if (control_ret != NULL) + *control_ret = NULL; + + if ((element = ber_alloc_t (LBER_USE_DER)) == NULL) + return LDAP_NO_MEMORY; + + ber_change_types = change_types; + ber_changes_only = changes_only; + ber_return_ecs = return_ecs; + + if (ber_printf (element, "{ibb}", ber_change_types, ber_changes_only, ber_return_ecs) < 0) + { + ber_free (element, 1); + return LDAP_ENCODING_ERROR; + } + + control = NULL; + if ((ret = ldap_create_control (_LDAP_CONTROL_PERSISTENTSEARCH, element, 1, &control)) != LDAP_SUCCESS) + { + ber_free (element, 1); + return ret; + } + + ber_free (element, 1); + + if (control_ret != NULL) + *control_ret = control; + else + ldap_control_free (control); + + return LDAP_SUCCESS; +} + +static int +start_persistent_search (LDAP *cnx, + const char *base, + int *msgid) +{ + LDAPControl *controls[2]; + int ret; + + if ((ret = create_psearch_control (_LDAP_CHANGETYPE_ANY, 1, 1, &controls[0])) != LDAP_SUCCESS) + return ret; + + controls[1] = NULL; + + ret = ldap_search_ext (cnx, + base, + LDAP_SCOPE_SUBTREE, + "(objectClass=*)", + NULL, /* attrs */ + 0, /* attrsonly */ + controls, + NULL, /* clientctrls */ + NULL, /* timeout */ + LDAP_NO_LIMIT, + msgid); + + ldap_control_free (controls[0]); + + return ret; +} + +static int +initialize_ldap_connection (LDAP **cnxp, + const char *uri, + const char *bind_dn, + const char *bind_passwd) +{ + LDAP *cnx; + int protocol; + int ret; + + if (cnxp != NULL) + *cnxp = NULL; + + cnx = NULL; + if ((ret = ldap_initialize (&cnx, uri)) != LDAP_SUCCESS) + return ret; + + protocol = LDAP_VERSION3; + + if ((ret = ldap_set_option (cnx, LDAP_OPT_PROTOCOL_VERSION, &protocol)) != LDAP_SUCCESS) + { + ldap_unbind_ext (cnx, NULL, NULL); + return ret; + } + + if (bind_dn != NULL && bind_passwd != NULL) + { + struct berval bind_cred; + + bind_cred.bv_val = (char *) bind_passwd; + bind_cred.bv_len = strlen (bind_passwd); + + if ((ret = ldap_sasl_bind_s (cnx, bind_dn, LDAP_SASL_SIMPLE, &bind_cred, NULL, NULL, NULL)) != LDAP_SUCCESS) + { + ldap_unbind_ext (cnx, NULL, NULL); + return ret; + } + } + + if (cnxp != NULL) + *cnxp = cnx; + + return LDAP_SUCCESS; +} + +static int +pyldap_watch_init (PyLDAPWatch *self, + PyObject *args, + PyObject *kwargs) +{ + char *uri; + char *base; + char *bind_dn; + char *bind_passwd; + int ret; + + uri = base = bind_dn = bind_passwd = NULL; + if (!PyArg_ParseTuple (args, "ss|zz:ldapwatch.LDAPWatch.__init__", &uri, &base, &bind_dn, &bind_passwd)) + return -1; + + self->uri = strdup (uri); + self->base = strdup (base); + + if (bind_dn != NULL) + self->bind_dn = strdup (base); + else + self->bind_dn = NULL; + + if (bind_passwd != NULL) + self->bind_passwd = strdup (base); + else + self->bind_passwd = NULL; + + if (self->uri == NULL || self->base == NULL || + (bind_dn != NULL && self->bind_dn == NULL) || + (bind_passwd != NULL && self->bind_passwd == NULL)) + { + PyErr_SetString (PyExc_RuntimeError, "could not allocate memory"); + return -1; + } + + self->cnx = NULL; + ret = initialize_ldap_connection (&self->cnx, + self->uri, + self->bind_dn, + self->bind_passwd); + if (ret != LDAP_SUCCESS) + { + PyErr_SetString (LDAP_exception, ldap_err2string (ret)); + return -1; + } + + self->msgid = 0; + ret = start_persistent_search (self->cnx, self->base, &self->msgid); + if (ret != LDAP_SUCCESS) + { + PyErr_SetString (LDAP_exception, ldap_err2string (ret)); + return -1; + } + + return 0; +} + +static void +pyldap_watch_dealloc (PyLDAPWatch *self) +{ + if (self->cnx != NULL) + ldap_unbind_ext (self->cnx, NULL, NULL); + self->cnx = NULL; + + self->msgid = 0; + + if (self->uri != NULL) + free (self->uri); + self->uri = NULL; + + if (self->base != NULL) + free (self->base); + self->base = NULL; + + if (self->bind_dn != NULL) + free (self->bind_dn); + self->bind_dn = NULL; + + if (self->bind_passwd != NULL) + free (self->bind_passwd); + self->bind_passwd = NULL; + + PyObject_DEL (self); +} + +static PyObject * +pyldap_watch_getattro (PyLDAPWatch *self, + PyObject *py_attr) +{ + if (PyString_Check (py_attr)) + { + char *attr; + + attr = PyString_AsString (py_attr); + + if (!strcmp (attr, "__members__")) + { + return Py_BuildValue ("[]"); + } + } + + return PyObject_GenericGetAttr ((PyObject *) self, py_attr); +} + +static struct PyMethodDef pyldap_watch_methods[] = +{ + { "get_changes", pyldap_watch_get_changes, METH_VARARGS }, + { NULL, NULL, 0 } +}; + +static PyTypeObject PyLDAPWatch_Type = +{ + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "ldapwatch.LDAPWatch", /* tp_name */ + sizeof (PyLDAPWatch), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor) pyldap_watch_dealloc, /* tp_dealloc */ + (printfunc)0, /* tp_print */ + (getattrfunc)0, /* tp_getattr */ + (setattrfunc)0, /* tp_setattr */ + (cmpfunc)0, /* tp_compare */ + (reprfunc)0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + (reprfunc)0, /* tp_str */ + (getattrofunc)pyldap_watch_getattro, /* tp_getattro */ + (setattrofunc)0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + NULL, /* Documentation string */ + (traverseproc)0, /* tp_traverse */ + (inquiry)0, /* tp_clear */ + (richcmpfunc)0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)0, /* tp_iter */ + (iternextfunc)0, /* tp_iternext */ + pyldap_watch_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + (PyTypeObject *)0, /* tp_base */ + (PyObject *)0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)pyldap_watch_init, /* tp_init */ + 0, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ + 0, /* tp_free */ + (inquiry)0, /* tp_is_gc */ + (PyObject *)0, /* tp_bases */ +}; + +static struct PyMethodDef ldapwatch_methods[] = { { NULL, NULL, 0 } }; + +void initldapwatch (void); + +DL_EXPORT (void) +initldapwatch (void) +{ + module = Py_InitModule4 ("ldapwatch", ldapwatch_methods, 0, 0, PYTHON_API_VERSION); + + PyLDAPWatch_Type.ob_type = &PyType_Type; + PyType_Ready (&PyLDAPWatch_Type); + PyModule_AddObject (module, "LDAPWatch", (PyObject *) &PyLDAPWatch_Type); + + LDAP_exception = PyErr_NewException ("ldapwatch.LDAPError", + NULL, + NULL); + PyModule_AddObject (module, "LDAPError", LDAP_exception); + + PyModule_AddIntConstant (module, "TYPE_INVALID", 0); + PyModule_AddIntConstant (module, "TYPE_ADD", _LDAP_CHANGETYPE_ADD); + PyModule_AddIntConstant (module, "TYPE_DELETE", _LDAP_CHANGETYPE_DELETE); + PyModule_AddIntConstant (module, "TYPE_MODIFY", _LDAP_CHANGETYPE_MODIFY); + PyModule_AddIntConstant (module, "TYPE_MODDN", _LDAP_CHANGETYPE_MODDN); +} diff -r f5d8a4a5c2c2 ipa-server/ipa-server.spec --- a/ipa-server/ipa-server.spec Fri Jan 18 08:51:17 2008 +0000 +++ b/ipa-server/ipa-server.spec Fri Jan 18 14:02:21 2008 +0000 @@ -66,6 +66,7 @@ rm %{buildroot}/%{plugin_dir}/libipa_pwd rm %{buildroot}/%{plugin_dir}/libipa_pwd_extop.la rm %{buildroot}/%{plugin_dir}/libipa-memberof-plugin.la rm %{buildroot}/%{plugin_dir}/libipa-dna-plugin.la +rm %{buildroot}/%{python_sitelib}/ipaconfigd/ldapwatch.la %clean rm -rf %{buildroot} @@ -113,6 +114,7 @@ fi %dir %{python_sitelib}/ipaconfigd/ %{python_sitelib}/ipaconfigd/*.py* +%{python_sitelib}/ipaconfigd/ldapwatch.so %attr(755,root,root) %{plugin_dir}/libipa_pwd_extop.so %attr(755,root,root) %{plugin_dir}/libipa-memberof-plugin.so diff -r f5d8a4a5c2c2 ipa-server/ipa-server.spec.in --- a/ipa-server/ipa-server.spec.in Fri Jan 18 08:51:17 2008 +0000 +++ b/ipa-server/ipa-server.spec.in Fri Jan 18 14:02:21 2008 +0000 @@ -66,6 +66,7 @@ rm %{buildroot}/%{plugin_dir}/libipa_pwd rm %{buildroot}/%{plugin_dir}/libipa_pwd_extop.la rm %{buildroot}/%{plugin_dir}/libipa-memberof-plugin.la rm %{buildroot}/%{plugin_dir}/libipa-dna-plugin.la +rm %{buildroot}/%{python_sitelib}/ipaconfigd/ldapwatch.la %clean rm -rf %{buildroot} @@ -113,6 +114,7 @@ fi %dir %{python_sitelib}/ipaconfigd/ %{python_sitelib}/ipaconfigd/*.py* +%{python_sitelib}/ipaconfigd/ldapwatch.so %attr(755,root,root) %{plugin_dir}/libipa_pwd_extop.so %attr(755,root,root) %{plugin_dir}/libipa-memberof-plugin.so