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