1/* $OpenLDAP$ */
2/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3 *
4 * Copyright 1999-2011 The OpenLDAP Foundation.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted only as authorized by the OpenLDAP
9 * Public License.
10 *
11 * A copy of this license is available in file LICENSE in the
12 * top-level directory of the distribution or, alternatively, at
13 * <http://www.OpenLDAP.org/license.html>.
14 */
15
16#include "portable.h"
17
18#include <stdio.h>
19
20#include "ac/stdlib.h"
21
22#include "ac/ctype.h"
23#include "ac/param.h"
24#include "ac/socket.h"
25#include "ac/string.h"
26#include "ac/unistd.h"
27#include "ac/wait.h"
28
29#include "ldap.h"
30#include "lutil.h"
31
32#include "slapd-common.h"
33
34#define LOOPS	100
35#define RETRIES 0
36
37static void
38do_modify( char *uri, char *manager, struct berval *passwd,
39		char *entry, char *attr, char *value, int maxloop,
40		int maxretries, int delay, int friendly, int chaserefs );
41
42
43static void
44usage( char *name )
45{
46        fprintf( stderr,
47		"usage: %s "
48		"-H <uri> | ([-h <host>] -p <port>) "
49		"-D <manager> "
50		"-w <passwd> "
51		"-e <entry> "
52		"[-i <ignore>] "
53		"[-l <loops>] "
54		"[-L <outerloops>] "
55		"[-r <maxretries>] "
56		"[-t <delay>] "
57		"[-F] "
58		"[-C]\n",
59			name );
60	exit( EXIT_FAILURE );
61}
62
63int
64main( int argc, char **argv )
65{
66	int		i;
67	char		*uri = NULL;
68	char		*host = "localhost";
69	int		port = -1;
70	char		*manager = NULL;
71	struct berval	passwd = { 0, NULL };
72	char		*entry = NULL;
73	char		*ava = NULL;
74	char		*value = NULL;
75	int		loops = LOOPS;
76	int		outerloops = 1;
77	int		retries = RETRIES;
78	int		delay = 0;
79	int		friendly = 0;
80	int		chaserefs = 0;
81
82	tester_init( "slapd-modify", TESTER_MODIFY );
83
84	while ( ( i = getopt( argc, argv, "a:CD:e:FH:h:i:L:l:p:r:t:w:" ) ) != EOF )
85	{
86		switch ( i ) {
87		case 'C':
88			chaserefs++;
89			break;
90
91		case 'F':
92			friendly++;
93			break;
94
95		case 'H':		/* the server uri */
96			uri = strdup( optarg );
97			break;
98
99		case 'h':		/* the servers host */
100			host = strdup( optarg );
101			break;
102
103		case 'i':
104			/* ignored (!) by now */
105			break;
106
107		case 'p':		/* the servers port */
108			if ( lutil_atoi( &port, optarg ) != 0 ) {
109				usage( argv[0] );
110			}
111			break;
112
113		case 'D':		/* the servers manager */
114			manager = strdup( optarg );
115			break;
116
117		case 'w':		/* the server managers password */
118			passwd.bv_val = strdup( optarg );
119			passwd.bv_len = strlen( optarg );
120			memset( optarg, '*', passwd.bv_len );
121			break;
122
123		case 'e':		/* entry to modify */
124			entry = strdup( optarg );
125			break;
126
127		case 'a':
128			ava = strdup( optarg );
129			break;
130
131		case 'l':		/* the number of loops */
132			if ( lutil_atoi( &loops, optarg ) != 0 ) {
133				usage( argv[0] );
134			}
135			break;
136
137		case 'L':		/* the number of outerloops */
138			if ( lutil_atoi( &outerloops, optarg ) != 0 ) {
139				usage( argv[0] );
140			}
141			break;
142
143		case 'r':		/* number of retries */
144			if ( lutil_atoi( &retries, optarg ) != 0 ) {
145				usage( argv[0] );
146			}
147			break;
148
149		case 't':		/* delay in seconds */
150			if ( lutil_atoi( &delay, optarg ) != 0 ) {
151				usage( argv[0] );
152			}
153			break;
154
155		default:
156			usage( argv[0] );
157			break;
158		}
159	}
160
161	if (( entry == NULL ) || ( ava == NULL ) || ( port == -1 && uri == NULL ))
162		usage( argv[0] );
163
164	if ( *entry == '\0' ) {
165
166		fprintf( stderr, "%s: invalid EMPTY entry DN.\n",
167				argv[0] );
168		exit( EXIT_FAILURE );
169
170	}
171	if ( *ava  == '\0' ) {
172		fprintf( stderr, "%s: invalid EMPTY AVA.\n",
173				argv[0] );
174		exit( EXIT_FAILURE );
175	}
176
177	if ( !( value = strchr( ava, ':' ))) {
178		fprintf( stderr, "%s: invalid AVA.\n",
179				argv[0] );
180		exit( EXIT_FAILURE );
181	}
182	*value++ = '\0';
183	while ( *value && isspace( (unsigned char) *value ))
184		value++;
185
186	uri = tester_uri( uri, host, port );
187
188	for ( i = 0; i < outerloops; i++ ) {
189		do_modify( uri, manager, &passwd, entry, ava, value,
190				loops, retries, delay, friendly, chaserefs );
191	}
192
193	exit( EXIT_SUCCESS );
194}
195
196
197static void
198do_modify( char *uri, char *manager,
199	struct berval *passwd, char *entry, char* attr, char* value,
200	int maxloop, int maxretries, int delay, int friendly, int chaserefs )
201{
202	LDAP	*ld = NULL;
203	int  	i = 0, do_retry = maxretries;
204	int     rc = LDAP_SUCCESS;
205
206	struct ldapmod mod;
207	struct ldapmod *mods[2];
208	char *values[2];
209	int version = LDAP_VERSION3;
210
211	values[0] = value;
212	values[1] = NULL;
213	mod.mod_op = LDAP_MOD_ADD;
214	mod.mod_type = attr;
215	mod.mod_values = values;
216	mods[0] = &mod;
217	mods[1] = NULL;
218
219retry:;
220	ldap_initialize( &ld, uri );
221	if ( ld == NULL ) {
222		tester_perror( "ldap_initialize", NULL );
223		exit( EXIT_FAILURE );
224	}
225
226	(void) ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
227	(void) ldap_set_option( ld, LDAP_OPT_REFERRALS,
228		chaserefs ? LDAP_OPT_ON : LDAP_OPT_OFF );
229
230	if ( do_retry == maxretries ) {
231		fprintf( stderr, "PID=%ld - Modify(%d): entry=\"%s\".\n",
232			(long) pid, maxloop, entry );
233	}
234
235	rc = ldap_sasl_bind_s( ld, manager, LDAP_SASL_SIMPLE, passwd, NULL, NULL, NULL );
236	if ( rc != LDAP_SUCCESS ) {
237		tester_ldap_error( ld, "ldap_sasl_bind_s", NULL );
238		switch ( rc ) {
239		case LDAP_BUSY:
240		case LDAP_UNAVAILABLE:
241			if ( do_retry > 0 ) {
242				do_retry--;
243				if ( delay > 0 ) {
244				    sleep( delay );
245				}
246				goto retry;
247			}
248		/* fallthru */
249		default:
250			break;
251		}
252		exit( EXIT_FAILURE );
253	}
254
255	for ( ; i < maxloop; i++ ) {
256		mod.mod_op = LDAP_MOD_ADD;
257		rc = ldap_modify_ext_s( ld, entry, mods, NULL, NULL );
258		if ( rc != LDAP_SUCCESS ) {
259			tester_ldap_error( ld, "ldap_modify_ext_s", NULL );
260			switch ( rc ) {
261			case LDAP_TYPE_OR_VALUE_EXISTS:
262				/* NOTE: this likely means
263				 * the second modify failed
264				 * during the previous round... */
265				if ( !friendly ) {
266					goto done;
267				}
268				break;
269
270			case LDAP_BUSY:
271			case LDAP_UNAVAILABLE:
272				if ( do_retry > 0 ) {
273					do_retry--;
274					goto retry;
275				}
276				/* fall thru */
277
278			default:
279				goto done;
280			}
281		}
282
283		mod.mod_op = LDAP_MOD_DELETE;
284		rc = ldap_modify_ext_s( ld, entry, mods, NULL, NULL );
285		if ( rc != LDAP_SUCCESS ) {
286			tester_ldap_error( ld, "ldap_modify_ext_s", NULL );
287			switch ( rc ) {
288			case LDAP_NO_SUCH_ATTRIBUTE:
289				/* NOTE: this likely means
290				 * the first modify failed
291				 * during the previous round... */
292				if ( !friendly ) {
293					goto done;
294				}
295				break;
296
297			case LDAP_BUSY:
298			case LDAP_UNAVAILABLE:
299				if ( do_retry > 0 ) {
300					do_retry--;
301					goto retry;
302				}
303				/* fall thru */
304
305			default:
306				goto done;
307			}
308		}
309
310	}
311
312done:;
313	fprintf( stderr, "  PID=%ld - Modify done (%d).\n", (long) pid, rc );
314
315	ldap_unbind_ext( ld, NULL, NULL );
316}
317
318
319