int
main(int argc, char *argv[])
{
	int               opt, c_flag = 0;
	CK_SLOT_ID	  slot_id = 0;
	char		 *so_pin = NULL, *user_pin = NULL, *data_store = NULL;

	CK_FUNCTION_LIST *funcs;
	CK_ULONG	  slot_count;
	CK_SESSION_HANDLE sess;
	CK_RV		  rv;
	struct object    *objs_to_migrate = NULL, *tmp, *to_free;
	int		  exit_code = 0, rc;
	
	lib_csulcca = dlopen("libcsulcca.so", (RTLD_GLOBAL | RTLD_NOW));
	if (lib_csulcca == NULL) {
		print_error("Couldn't get a handle to the CCA library.");
		return NULL;
	}

	CSNDKTC = dlsym(lib_csulcca, "CSNDKTC_32");
	CSNBKTC = dlsym(lib_csulcca, "CSNBKTC_32");	

	while ((opt = getopt(argc, argv, "c:d:s:u:nvh")) != -1) {
		switch (opt) {
		case 'c':
			c_flag++;
			slot_id = atoi(optarg);
			break;

		case 'd':
			data_store = strdup(optarg);
			break;

		case 's':
			so_pin = strdup(optarg);
			break;

		case 'u':
			user_pin = strdup(optarg);
			break;

		case 'n':
			n_flag++;
			break;

		case 'v':
			v_flag++;
			break;

		case 'h':
			usage(argv[0]);
			return 0;

		default:
			usage(argv[0]);
			return 1;
		}
	}

	if (!c_flag || !data_store || !so_pin || !user_pin) {
		usage(argv[0]);
		return 1;
	}

	if (n_flag)
		printf("Dry-run of migration in progress\n");

	funcs = p11_init();
	if (!funcs) {
		return 2;
	}

	rv = funcs->C_GetSlotList(TRUE, NULL_PTR, &slot_count);
	if (rv != CKR_OK) {
		p11_error("C_GetSlotList", rv);
		exit_code = 3;
		goto finalize;
	}

	if (slot_id >= slot_count) {
		print_error("%lu is not a valid slot ID.", slot_id);
		exit_code = 4;
		goto finalize;
	}
	if (v_flag > 1)
		printf("Slot id %lu is valid\n", slot_id);

	/* Open a r/w session */
	rv = funcs->C_OpenSession(slot_id, CKF_RW_SESSION|CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &sess);
	if (rv != CKR_OK) {
		p11_error("C_OpenSession", rv);
		exit_code = 5;
		goto finalize;
	}
	if (v_flag > 1)
		printf("PKCS#11 r/w session opened\n");

	/* Login as the SO just validate the supplied pin */
	rv = funcs->C_Login(sess, CKU_SO, (CK_BYTE *)so_pin, strlen(so_pin));
	if (rv != CKR_OK) {
		p11_error("C_Login (SO)", rv);
		exit_code = 6;
		goto finalize;
	}
	if (v_flag > 1)
		printf("PKCS#11 SO login successful\n");

	/* Logout the SO */
	rv = funcs->C_Logout(sess);
	if (rv != CKR_OK) {
		p11_error("C_Logout", rv);
		exit_code = 7;
		goto finalize;
	}

	/* Login as the USER to validate the supplied pin and do the migration */
	rv = funcs->C_Login(sess, CKU_USER, (CK_BYTE *)user_pin, strlen(user_pin));
	if (rv != CKR_OK) {
		p11_error("C_Login (USER)", rv);
		exit_code = 8;
		goto finalize;
	}
	if (v_flag > 1)
		printf("PKCS#11 USER login successful\n");

	/* Find the affected PKCS#11 objects */
	rc = find_opaque_objects(funcs, sess, &objs_to_migrate);
	if (rc) {
		exit_code = 9;
		goto close;
	}

	/* XXX Print status: migrating X pub keys, X priv keys, X 3DES keys... */

	/* Use the CCA lib to migrate them to the new master key */
	rv = migrate_objects(objs_to_migrate);
	if (rv != CKR_OK) {
		exit_code = 10;
		goto close;
	}

	/* XXX Print status */

	/* Delete the old PKCS#11 objects (or just attribs if possible) and replace with the
	 * migrated data */
	rc = replace_objects(funcs, sess, objs_to_migrate);
	if (rc) {
		exit_code = 11;
		goto close;
	}

	/* XXX Print status: X objects successfully migrated */

	/* Free the list of PKCS#11 objects */
	for (to_free = objs_to_migrate; to_free; to_free = tmp) {
		tmp = to_free->next;
		free(to_free->opaque_attr);
		free(to_free);
	}

	/* Migrate the keys used to encrypt the data store */
	rc = migrate_master_keys(so_pin, user_pin, data_store);
	if (rc) {
		exit_code = 12;
		goto close;
	}

close:
	funcs->C_CloseSession(sess);
finalize:
	p11_fini(funcs);
	return exit_code;
}